diff options
| author | Simon Wood <simon@mungewell.org> | 2015-11-19 18:42:11 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2015-11-20 04:29:57 -0500 |
| commit | a5ce8f5b12966d34623d6c20278b5b4da7a53675 (patch) | |
| tree | 6d899e575b439446c8df8e5def2820d676d74fdc | |
| parent | 2f23985879c2fb2967e8bca77b1014b437a9b9ff (diff) | |
HID: hid-logitech-hidpp: Add support for very long packets
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>
| -rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 59 |
1 files changed, 48 insertions, 11 deletions
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 5fd97860aec4..0f53dc8c79b1 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c | |||
| @@ -40,9 +40,11 @@ MODULE_PARM_DESC(disable_tap_to_click, | |||
| 40 | 40 | ||
| 41 | #define REPORT_ID_HIDPP_SHORT 0x10 | 41 | #define REPORT_ID_HIDPP_SHORT 0x10 |
| 42 | #define REPORT_ID_HIDPP_LONG 0x11 | 42 | #define REPORT_ID_HIDPP_LONG 0x11 |
| 43 | #define REPORT_ID_HIDPP_VERY_LONG 0x12 | ||
| 43 | 44 | ||
| 44 | #define HIDPP_REPORT_SHORT_LENGTH 7 | 45 | #define HIDPP_REPORT_SHORT_LENGTH 7 |
| 45 | #define HIDPP_REPORT_LONG_LENGTH 20 | 46 | #define HIDPP_REPORT_LONG_LENGTH 20 |
| 47 | #define HIDPP_REPORT_VERY_LONG_LENGTH 64 | ||
| 46 | 48 | ||
| 47 | #define HIDPP_QUIRK_CLASS_WTP BIT(0) | 49 | #define HIDPP_QUIRK_CLASS_WTP BIT(0) |
| 48 | #define HIDPP_QUIRK_CLASS_M560 BIT(1) | 50 | #define HIDPP_QUIRK_CLASS_M560 BIT(1) |
| @@ -81,13 +83,13 @@ MODULE_PARM_DESC(disable_tap_to_click, | |||
| 81 | struct fap { | 83 | struct fap { |
| 82 | u8 feature_index; | 84 | u8 feature_index; |
| 83 | u8 funcindex_clientid; | 85 | u8 funcindex_clientid; |
| 84 | u8 params[HIDPP_REPORT_LONG_LENGTH - 4U]; | 86 | u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U]; |
| 85 | }; | 87 | }; |
| 86 | 88 | ||
| 87 | struct rap { | 89 | struct rap { |
| 88 | u8 sub_id; | 90 | u8 sub_id; |
| 89 | u8 reg_address; | 91 | u8 reg_address; |
| 90 | u8 params[HIDPP_REPORT_LONG_LENGTH - 4U]; | 92 | u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U]; |
| 91 | }; | 93 | }; |
| 92 | 94 | ||
| 93 | struct hidpp_report { | 95 | struct hidpp_report { |
| @@ -153,6 +155,9 @@ static int __hidpp_send_report(struct hid_device *hdev, | |||
| 153 | case REPORT_ID_HIDPP_LONG: | 155 | case REPORT_ID_HIDPP_LONG: |
| 154 | fields_count = HIDPP_REPORT_LONG_LENGTH; | 156 | fields_count = HIDPP_REPORT_LONG_LENGTH; |
| 155 | break; | 157 | break; |
| 158 | case REPORT_ID_HIDPP_VERY_LONG: | ||
| 159 | fields_count = HIDPP_REPORT_VERY_LONG_LENGTH; | ||
| 160 | break; | ||
| 156 | default: | 161 | default: |
| 157 | return -ENODEV; | 162 | return -ENODEV; |
| 158 | } | 163 | } |
| @@ -217,8 +222,9 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp, | |||
| 217 | goto exit; | 222 | goto exit; |
| 218 | } | 223 | } |
| 219 | 224 | ||
| 220 | if (response->report_id == REPORT_ID_HIDPP_LONG && | 225 | if ((response->report_id == REPORT_ID_HIDPP_LONG || |
| 221 | response->fap.feature_index == HIDPP20_ERROR) { | 226 | response->report_id == REPORT_ID_HIDPP_VERY_LONG) && |
| 227 | response->fap.feature_index == HIDPP20_ERROR) { | ||
| 222 | ret = response->fap.params[1]; | 228 | ret = response->fap.params[1]; |
| 223 | dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); | 229 | dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); |
| 224 | goto exit; | 230 | goto exit; |
| @@ -243,7 +249,11 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp, | |||
| 243 | message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); | 249 | message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); |
| 244 | if (!message) | 250 | if (!message) |
| 245 | return -ENOMEM; | 251 | return -ENOMEM; |
| 246 | message->report_id = REPORT_ID_HIDPP_LONG; | 252 | |
| 253 | if (param_count > (HIDPP_REPORT_LONG_LENGTH - 4)) | ||
| 254 | message->report_id = REPORT_ID_HIDPP_VERY_LONG; | ||
| 255 | else | ||
| 256 | message->report_id = REPORT_ID_HIDPP_LONG; | ||
| 247 | message->fap.feature_index = feat_index; | 257 | message->fap.feature_index = feat_index; |
| 248 | message->fap.funcindex_clientid = funcindex_clientid; | 258 | message->fap.funcindex_clientid = funcindex_clientid; |
| 249 | memcpy(&message->fap.params, params, param_count); | 259 | memcpy(&message->fap.params, params, param_count); |
| @@ -258,13 +268,23 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev, | |||
| 258 | struct hidpp_report *response) | 268 | struct hidpp_report *response) |
| 259 | { | 269 | { |
| 260 | struct hidpp_report *message; | 270 | struct hidpp_report *message; |
| 261 | int ret; | 271 | int ret, max_count; |
| 262 | 272 | ||
| 263 | if ((report_id != REPORT_ID_HIDPP_SHORT) && | 273 | switch (report_id) { |
| 264 | (report_id != REPORT_ID_HIDPP_LONG)) | 274 | case REPORT_ID_HIDPP_SHORT: |
| 275 | max_count = HIDPP_REPORT_SHORT_LENGTH - 4; | ||
| 276 | break; | ||
| 277 | case REPORT_ID_HIDPP_LONG: | ||
| 278 | max_count = HIDPP_REPORT_LONG_LENGTH - 4; | ||
| 279 | break; | ||
| 280 | case REPORT_ID_HIDPP_VERY_LONG: | ||
| 281 | max_count = HIDPP_REPORT_VERY_LONG_LENGTH - 4; | ||
| 282 | break; | ||
| 283 | default: | ||
| 265 | return -EINVAL; | 284 | return -EINVAL; |
| 285 | } | ||
| 266 | 286 | ||
| 267 | if (param_count > sizeof(message->rap.params)) | 287 | if (param_count > max_count) |
| 268 | return -EINVAL; | 288 | return -EINVAL; |
| 269 | 289 | ||
| 270 | message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); | 290 | message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL); |
| @@ -508,10 +528,19 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp, | |||
| 508 | if (ret) | 528 | if (ret) |
| 509 | return ret; | 529 | return ret; |
| 510 | 530 | ||
| 511 | if (response.report_id == REPORT_ID_HIDPP_LONG) | 531 | switch (response.report_id) { |
| 532 | case REPORT_ID_HIDPP_VERY_LONG: | ||
| 533 | count = HIDPP_REPORT_VERY_LONG_LENGTH - 4; | ||
| 534 | break; | ||
| 535 | case REPORT_ID_HIDPP_LONG: | ||
| 512 | count = HIDPP_REPORT_LONG_LENGTH - 4; | 536 | count = HIDPP_REPORT_LONG_LENGTH - 4; |
| 513 | else | 537 | break; |
| 538 | case REPORT_ID_HIDPP_SHORT: | ||
| 514 | count = HIDPP_REPORT_SHORT_LENGTH - 4; | 539 | count = HIDPP_REPORT_SHORT_LENGTH - 4; |
| 540 | break; | ||
| 541 | default: | ||
| 542 | return -EPROTO; | ||
| 543 | } | ||
| 515 | 544 | ||
| 516 | if (len_buf < count) | 545 | if (len_buf < count) |
| 517 | count = len_buf; | 546 | count = len_buf; |
| @@ -1347,6 +1376,14 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, | |||
| 1347 | 1376 | ||
| 1348 | /* Generic HID++ processing. */ | 1377 | /* Generic HID++ processing. */ |
| 1349 | switch (data[0]) { | 1378 | switch (data[0]) { |
| 1379 | case REPORT_ID_HIDPP_VERY_LONG: | ||
| 1380 | if (size != HIDPP_REPORT_VERY_LONG_LENGTH) { | ||
| 1381 | hid_err(hdev, "received hid++ report of bad size (%d)", | ||
| 1382 | size); | ||
| 1383 | return 1; | ||
| 1384 | } | ||
| 1385 | ret = hidpp_raw_hidpp_event(hidpp, data, size); | ||
| 1386 | break; | ||
| 1350 | case REPORT_ID_HIDPP_LONG: | 1387 | case REPORT_ID_HIDPP_LONG: |
| 1351 | if (size != HIDPP_REPORT_LONG_LENGTH) { | 1388 | if (size != HIDPP_REPORT_LONG_LENGTH) { |
| 1352 | hid_err(hdev, "received hid++ report of bad size (%d)", | 1389 | hid_err(hdev, "received hid++ report of bad size (%d)", |
