Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 20 Apr 2018 15:55:30 +0000 (08:55 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 20 Apr 2018 15:55:30 +0000 (08:55 -0700)
Pull HID updates from Jiri Kosina:

 - suspend/resume handling fix for Raydium I2C-connected touchscreen
   from Aaron Ma

 - protocol fixup for certain BT-connected Wacoms from Aaron Armstrong
   Skomra

 - battery level reporting fix on BT-connected mice from Dmitry Torokhov

 - hidraw race condition fix from Rodrigo Rivas Costa

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
  HID: i2c-hid: fix inverted return value from i2c_hid_command()
  HID: i2c-hid: Fix resume issue on Raydium touchscreen device
  HID: wacom: bluetooth: send exit report for recent Bluetooth devices
  HID: hidraw: Fix crash on HIDIOCGFEATURE with a destroyed device
  HID: input: fix battery level reporting on BT mice

drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hidraw.c
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/wacom_wac.c
include/linux/hid.h

index 5a3a7ead301214dceb5cbe99308a08160515ae89..0b5cc910f62e560758f50bb03585ac27942ef199 100644 (file)
 #define I2C_VENDOR_ID_HANTICK          0x0911
 #define I2C_PRODUCT_ID_HANTICK_5288    0x5288
 
+#define I2C_VENDOR_ID_RAYD             0x2386
+#define I2C_PRODUCT_ID_RAYD_3118       0x3118
+
 #define USB_VENDOR_ID_HANWANG          0x0b57
 #define USB_DEVICE_ID_HANWANG_TABLET_FIRST     0x5000
 #define USB_DEVICE_ID_HANWANG_TABLET_LAST      0x8fff
index 6836a856c243ab5c05ff7b1ff2f4af354fd34efb..930652c25120ee14e57859af3bbce8d5072dab26 100644 (file)
@@ -387,7 +387,8 @@ static int hidinput_get_battery_property(struct power_supply *psy,
                break;
 
        case POWER_SUPPLY_PROP_CAPACITY:
-               if (dev->battery_report_type == HID_FEATURE_REPORT) {
+               if (dev->battery_status != HID_BATTERY_REPORTED &&
+                   !dev->battery_avoid_query) {
                        value = hidinput_query_battery_capacity(dev);
                        if (value < 0)
                                return value;
@@ -403,17 +404,17 @@ static int hidinput_get_battery_property(struct power_supply *psy,
                break;
 
        case POWER_SUPPLY_PROP_STATUS:
-               if (!dev->battery_reported &&
-                   dev->battery_report_type == HID_FEATURE_REPORT) {
+               if (dev->battery_status != HID_BATTERY_REPORTED &&
+                   !dev->battery_avoid_query) {
                        value = hidinput_query_battery_capacity(dev);
                        if (value < 0)
                                return value;
 
                        dev->battery_capacity = value;
-                       dev->battery_reported = true;
+                       dev->battery_status = HID_BATTERY_QUERIED;
                }
 
-               if (!dev->battery_reported)
+               if (dev->battery_status == HID_BATTERY_UNKNOWN)
                        val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
                else if (dev->battery_capacity == 100)
                        val->intval = POWER_SUPPLY_STATUS_FULL;
@@ -486,6 +487,14 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
        dev->battery_report_type = report_type;
        dev->battery_report_id = field->report->id;
 
+       /*
+        * Stylus is normally not connected to the device and thus we
+        * can't query the device and get meaningful battery strength.
+        * We have to wait for the device to report it on its own.
+        */
+       dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
+                                  field->physical == HID_DG_STYLUS;
+
        dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
        if (IS_ERR(dev->battery)) {
                error = PTR_ERR(dev->battery);
@@ -530,9 +539,10 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
 
        capacity = hidinput_scale_battery_capacity(dev, value);
 
-       if (!dev->battery_reported || capacity != dev->battery_capacity) {
+       if (dev->battery_status != HID_BATTERY_REPORTED ||
+           capacity != dev->battery_capacity) {
                dev->battery_capacity = capacity;
-               dev->battery_reported = true;
+               dev->battery_status = HID_BATTERY_REPORTED;
                power_supply_changed(dev->battery);
        }
 }
index fbfcc80094329e561c7dfaa0fd3e411144503a49..b39844adea47a333f52079abbefa2ee0186bb13b 100644 (file)
@@ -192,6 +192,11 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
        int ret = 0, len;
        unsigned char report_number;
 
+       if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
+               ret = -ENODEV;
+               goto out;
+       }
+
        dev = hidraw_table[minor]->hid;
 
        if (!dev->ll_driver->raw_request) {
index 97689e98e53fbe9ba1ca80e2d8a9c34a42224026..963328674e93af929fde1e20a3dc3deff3d75569 100644 (file)
@@ -47,6 +47,7 @@
 /* quirks to control the device */
 #define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV       BIT(0)
 #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET       BIT(1)
+#define I2C_HID_QUIRK_RESEND_REPORT_DESCR      BIT(2)
 
 /* flags */
 #define I2C_HID_STARTED                0
@@ -171,6 +172,8 @@ static const struct i2c_hid_quirks {
                I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
        { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
                I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
+       { I2C_VENDOR_ID_RAYD, I2C_PRODUCT_ID_RAYD_3118,
+               I2C_HID_QUIRK_RESEND_REPORT_DESCR },
        { 0, 0 }
 };
 
@@ -1220,6 +1223,16 @@ static int i2c_hid_resume(struct device *dev)
        if (ret)
                return ret;
 
+       /* RAYDIUM device (2386:3118) need to re-send report descr cmd
+        * after resume, after this it will be back normal.
+        * otherwise it issues too many incomplete reports.
+        */
+       if (ihid->quirks & I2C_HID_QUIRK_RESEND_REPORT_DESCR) {
+               ret = i2c_hid_command(client, &hid_report_descr_cmd, NULL, 0);
+               if (ret)
+                       return ret;
+       }
+
        if (hid->driver && hid->driver->reset_resume) {
                ret = hid->driver->reset_resume(hid);
                return ret;
index 6da16a879c9f27e6c1281684c5200a27f21448d5..5f947ec20dcb35de628c321086c4178e96527f2c 100644 (file)
@@ -689,6 +689,45 @@ static int wacom_intuos_get_tool_type(int tool_id)
        return tool_type;
 }
 
+static void wacom_exit_report(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->pen_input;
+       struct wacom_features *features = &wacom->features;
+       unsigned char *data = wacom->data;
+       int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
+
+       /*
+        * Reset all states otherwise we lose the initial states
+        * when in-prox next time
+        */
+       input_report_abs(input, ABS_X, 0);
+       input_report_abs(input, ABS_Y, 0);
+       input_report_abs(input, ABS_DISTANCE, 0);
+       input_report_abs(input, ABS_TILT_X, 0);
+       input_report_abs(input, ABS_TILT_Y, 0);
+       if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
+               input_report_key(input, BTN_LEFT, 0);
+               input_report_key(input, BTN_MIDDLE, 0);
+               input_report_key(input, BTN_RIGHT, 0);
+               input_report_key(input, BTN_SIDE, 0);
+               input_report_key(input, BTN_EXTRA, 0);
+               input_report_abs(input, ABS_THROTTLE, 0);
+               input_report_abs(input, ABS_RZ, 0);
+       } else {
+               input_report_abs(input, ABS_PRESSURE, 0);
+               input_report_key(input, BTN_STYLUS, 0);
+               input_report_key(input, BTN_STYLUS2, 0);
+               input_report_key(input, BTN_TOUCH, 0);
+               input_report_abs(input, ABS_WHEEL, 0);
+               if (features->type >= INTUOS3S)
+                       input_report_abs(input, ABS_Z, 0);
+       }
+       input_report_key(input, wacom->tool[idx], 0);
+       input_report_abs(input, ABS_MISC, 0); /* reset tool id */
+       input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+       wacom->id[idx] = 0;
+}
+
 static int wacom_intuos_inout(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
@@ -741,36 +780,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
                if (!wacom->id[idx])
                        return 1;
 
-               /*
-                * Reset all states otherwise we lose the initial states
-                * when in-prox next time
-                */
-               input_report_abs(input, ABS_X, 0);
-               input_report_abs(input, ABS_Y, 0);
-               input_report_abs(input, ABS_DISTANCE, 0);
-               input_report_abs(input, ABS_TILT_X, 0);
-               input_report_abs(input, ABS_TILT_Y, 0);
-               if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
-                       input_report_key(input, BTN_LEFT, 0);
-                       input_report_key(input, BTN_MIDDLE, 0);
-                       input_report_key(input, BTN_RIGHT, 0);
-                       input_report_key(input, BTN_SIDE, 0);
-                       input_report_key(input, BTN_EXTRA, 0);
-                       input_report_abs(input, ABS_THROTTLE, 0);
-                       input_report_abs(input, ABS_RZ, 0);
-               } else {
-                       input_report_abs(input, ABS_PRESSURE, 0);
-                       input_report_key(input, BTN_STYLUS, 0);
-                       input_report_key(input, BTN_STYLUS2, 0);
-                       input_report_key(input, BTN_TOUCH, 0);
-                       input_report_abs(input, ABS_WHEEL, 0);
-                       if (features->type >= INTUOS3S)
-                               input_report_abs(input, ABS_Z, 0);
-               }
-               input_report_key(input, wacom->tool[idx], 0);
-               input_report_abs(input, ABS_MISC, 0); /* reset tool id */
-               input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
-               wacom->id[idx] = 0;
+               wacom_exit_report(wacom);
                return 2;
        }
 
@@ -1235,6 +1245,12 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
                if (!valid)
                        continue;
 
+               if (!prox) {
+                       wacom->shared->stylus_in_proximity = false;
+                       wacom_exit_report(wacom);
+                       input_sync(pen_input);
+                       return;
+               }
                if (range) {
                        input_report_abs(pen_input, ABS_X, get_unaligned_le16(&frame[1]));
                        input_report_abs(pen_input, ABS_Y, get_unaligned_le16(&frame[3]));
index 8da3e1f48195be58641820fa0c5f85a7a6a833e9..26240a22978a4cedf80cad5fa26c026b07ce5aad 100644 (file)
@@ -516,6 +516,12 @@ enum hid_type {
        HID_TYPE_USBNONE
 };
 
+enum hid_battery_status {
+       HID_BATTERY_UNKNOWN = 0,
+       HID_BATTERY_QUERIED,            /* Kernel explicitly queried battery strength */
+       HID_BATTERY_REPORTED,           /* Device sent unsolicited battery strength report */
+};
+
 struct hid_driver;
 struct hid_ll_driver;
 
@@ -558,7 +564,8 @@ struct hid_device {                                                 /* device report descriptor */
        __s32 battery_max;
        __s32 battery_report_type;
        __s32 battery_report_id;
-       bool battery_reported;
+       enum hid_battery_status battery_status;
+       bool battery_avoid_query;
 #endif
 
        unsigned int status;                                            /* see STAT flags above */