HID: hid-logitech-hidpp: Add support for very long packets
authorSimon Wood <simon@mungewell.org>
Thu, 19 Nov 2015 23:42:11 +0000 (16:42 -0700)
committerJiri Kosina <jkosina@suse.cz>
Fri, 20 Nov 2015 09:29:57 +0000 (10:29 +0100)
Patch add support for the 'very long' HID++ packets, which are
64 bytes in length.

Signed-off-by: Simon Wood <simon@mungewell.org>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-logitech-hidpp.c

index 5fd97860aec4d8ec1e92b02031e715e8644367e8..0f53dc8c79b17f3bf1c6dd87e1f0f829efd58b00 100644 (file)
@@ -40,9 +40,11 @@ MODULE_PARM_DESC(disable_tap_to_click,
 
 #define REPORT_ID_HIDPP_SHORT                  0x10
 #define REPORT_ID_HIDPP_LONG                   0x11
+#define REPORT_ID_HIDPP_VERY_LONG              0x12
 
 #define HIDPP_REPORT_SHORT_LENGTH              7
 #define HIDPP_REPORT_LONG_LENGTH               20
+#define HIDPP_REPORT_VERY_LONG_LENGTH          64
 
 #define HIDPP_QUIRK_CLASS_WTP                  BIT(0)
 #define HIDPP_QUIRK_CLASS_M560                 BIT(1)
@@ -81,13 +83,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
 struct fap {
        u8 feature_index;
        u8 funcindex_clientid;
-       u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+       u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U];
 };
 
 struct rap {
        u8 sub_id;
        u8 reg_address;
-       u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+       u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U];
 };
 
 struct hidpp_report {
@@ -153,6 +155,9 @@ static int __hidpp_send_report(struct hid_device *hdev,
        case REPORT_ID_HIDPP_LONG:
                fields_count = HIDPP_REPORT_LONG_LENGTH;
                break;
+       case REPORT_ID_HIDPP_VERY_LONG:
+               fields_count = HIDPP_REPORT_VERY_LONG_LENGTH;
+               break;
        default:
                return -ENODEV;
        }
@@ -217,8 +222,9 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
                goto exit;
        }
 
-       if (response->report_id == REPORT_ID_HIDPP_LONG &&
-           response->fap.feature_index == HIDPP20_ERROR) {
+       if ((response->report_id == REPORT_ID_HIDPP_LONG ||
+                       response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
+                       response->fap.feature_index == HIDPP20_ERROR) {
                ret = response->fap.params[1];
                dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
                goto exit;
@@ -243,7 +249,11 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
        message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
        if (!message)
                return -ENOMEM;
-       message->report_id = REPORT_ID_HIDPP_LONG;
+
+       if (param_count > (HIDPP_REPORT_LONG_LENGTH - 4))
+               message->report_id = REPORT_ID_HIDPP_VERY_LONG;
+       else
+               message->report_id = REPORT_ID_HIDPP_LONG;
        message->fap.feature_index = feat_index;
        message->fap.funcindex_clientid = funcindex_clientid;
        memcpy(&message->fap.params, params, param_count);
@@ -258,13 +268,23 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
        struct hidpp_report *response)
 {
        struct hidpp_report *message;
-       int ret;
+       int ret, max_count;
 
-       if ((report_id != REPORT_ID_HIDPP_SHORT) &&
-           (report_id != REPORT_ID_HIDPP_LONG))
+       switch (report_id) {
+       case REPORT_ID_HIDPP_SHORT:
+               max_count = HIDPP_REPORT_SHORT_LENGTH - 4;
+               break;
+       case REPORT_ID_HIDPP_LONG:
+               max_count = HIDPP_REPORT_LONG_LENGTH - 4;
+               break;
+       case REPORT_ID_HIDPP_VERY_LONG:
+               max_count = HIDPP_REPORT_VERY_LONG_LENGTH - 4;
+               break;
+       default:
                return -EINVAL;
+       }
 
-       if (param_count > sizeof(message->rap.params))
+       if (param_count > max_count)
                return -EINVAL;
 
        message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
@@ -508,10 +528,19 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
        if (ret)
                return ret;
 
-       if (response.report_id == REPORT_ID_HIDPP_LONG)
+       switch (response.report_id) {
+       case REPORT_ID_HIDPP_VERY_LONG:
+               count = HIDPP_REPORT_VERY_LONG_LENGTH - 4;
+               break;
+       case REPORT_ID_HIDPP_LONG:
                count = HIDPP_REPORT_LONG_LENGTH - 4;
-       else
+               break;
+       case REPORT_ID_HIDPP_SHORT:
                count = HIDPP_REPORT_SHORT_LENGTH - 4;
+               break;
+       default:
+               return -EPROTO;
+       }
 
        if (len_buf < count)
                count = len_buf;
@@ -1347,6 +1376,14 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
 
        /* Generic HID++ processing. */
        switch (data[0]) {
+       case REPORT_ID_HIDPP_VERY_LONG:
+               if (size != HIDPP_REPORT_VERY_LONG_LENGTH) {
+                       hid_err(hdev, "received hid++ report of bad size (%d)",
+                               size);
+                       return 1;
+               }
+               ret = hidpp_raw_hidpp_event(hidpp, data, size);
+               break;
        case REPORT_ID_HIDPP_LONG:
                if (size != HIDPP_REPORT_LONG_LENGTH) {
                        hid_err(hdev, "received hid++ report of bad size (%d)",