diff options
Diffstat (limited to 'drivers/hid/hid-lg.c')
| -rw-r--r-- | drivers/hid/hid-lg.c | 49 |
1 files changed, 45 insertions, 4 deletions
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index f6433d8050a9..b629fba5a057 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | 8 | * Copyright (c) 2007 Paul Walmsley |
| 9 | * Copyright (c) 2008 Jiri Slaby | 9 | * Copyright (c) 2008 Jiri Slaby |
| 10 | * Copyright (c) 2010 Hendrik Iben | ||
| 10 | */ | 11 | */ |
| 11 | 12 | ||
| 12 | /* | 13 | /* |
| @@ -19,6 +20,9 @@ | |||
| 19 | #include <linux/device.h> | 20 | #include <linux/device.h> |
| 20 | #include <linux/hid.h> | 21 | #include <linux/hid.h> |
| 21 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| 23 | #include <linux/random.h> | ||
| 24 | #include <linux/sched.h> | ||
| 25 | #include <linux/wait.h> | ||
| 22 | 26 | ||
| 23 | #include "hid-ids.h" | 27 | #include "hid-ids.h" |
| 24 | #include "hid-lg.h" | 28 | #include "hid-lg.h" |
| @@ -35,31 +39,43 @@ | |||
| 35 | #define LG_FF2 0x400 | 39 | #define LG_FF2 0x400 |
| 36 | #define LG_RDESC_REL_ABS 0x800 | 40 | #define LG_RDESC_REL_ABS 0x800 |
| 37 | #define LG_FF3 0x1000 | 41 | #define LG_FF3 0x1000 |
| 42 | #define LG_FF4 0x2000 | ||
| 38 | 43 | ||
| 39 | /* | 44 | /* |
| 40 | * Certain Logitech keyboards send in report #3 keys which are far | 45 | * Certain Logitech keyboards send in report #3 keys which are far |
| 41 | * above the logical maximum described in descriptor. This extends | 46 | * above the logical maximum described in descriptor. This extends |
| 42 | * the original value of 0x28c of logical maximum to 0x104d | 47 | * the original value of 0x28c of logical maximum to 0x104d |
| 43 | */ | 48 | */ |
| 44 | static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 49 | static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
| 45 | unsigned int rsize) | 50 | unsigned int *rsize) |
| 46 | { | 51 | { |
| 47 | unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); | 52 | unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); |
| 48 | 53 | ||
| 49 | if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 && | 54 | if ((quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 && |
| 50 | rdesc[84] == 0x8c && rdesc[85] == 0x02) { | 55 | rdesc[84] == 0x8c && rdesc[85] == 0x02) { |
| 51 | dev_info(&hdev->dev, "fixing up Logitech keyboard report " | 56 | dev_info(&hdev->dev, "fixing up Logitech keyboard report " |
| 52 | "descriptor\n"); | 57 | "descriptor\n"); |
| 53 | rdesc[84] = rdesc[89] = 0x4d; | 58 | rdesc[84] = rdesc[89] = 0x4d; |
| 54 | rdesc[85] = rdesc[90] = 0x10; | 59 | rdesc[85] = rdesc[90] = 0x10; |
| 55 | } | 60 | } |
| 56 | if ((quirks & LG_RDESC_REL_ABS) && rsize >= 50 && | 61 | if ((quirks & LG_RDESC_REL_ABS) && *rsize >= 50 && |
| 57 | rdesc[32] == 0x81 && rdesc[33] == 0x06 && | 62 | rdesc[32] == 0x81 && rdesc[33] == 0x06 && |
| 58 | rdesc[49] == 0x81 && rdesc[50] == 0x06) { | 63 | rdesc[49] == 0x81 && rdesc[50] == 0x06) { |
| 59 | dev_info(&hdev->dev, "fixing up rel/abs in Logitech " | 64 | dev_info(&hdev->dev, "fixing up rel/abs in Logitech " |
| 60 | "report descriptor\n"); | 65 | "report descriptor\n"); |
| 61 | rdesc[33] = rdesc[50] = 0x02; | 66 | rdesc[33] = rdesc[50] = 0x02; |
| 62 | } | 67 | } |
| 68 | if ((quirks & LG_FF4) && *rsize >= 101 && | ||
| 69 | rdesc[41] == 0x95 && rdesc[42] == 0x0B && | ||
| 70 | rdesc[47] == 0x05 && rdesc[48] == 0x09) { | ||
| 71 | dev_info(&hdev->dev, "fixing up Logitech Speed Force Wireless " | ||
| 72 | "button descriptor\n"); | ||
| 73 | rdesc[41] = 0x05; | ||
| 74 | rdesc[42] = 0x09; | ||
| 75 | rdesc[47] = 0x95; | ||
| 76 | rdesc[48] = 0x0B; | ||
| 77 | } | ||
| 78 | return rdesc; | ||
| 63 | } | 79 | } |
| 64 | 80 | ||
| 65 | #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ | 81 | #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ |
| @@ -285,12 +301,33 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 285 | goto err_free; | 301 | goto err_free; |
| 286 | } | 302 | } |
| 287 | 303 | ||
| 304 | if (quirks & LG_FF4) { | ||
| 305 | unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| 306 | |||
| 307 | ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); | ||
| 308 | |||
| 309 | if (ret >= 0) { | ||
| 310 | /* insert a little delay of 10 jiffies ~ 40ms */ | ||
| 311 | wait_queue_head_t wait; | ||
| 312 | init_waitqueue_head (&wait); | ||
| 313 | wait_event_interruptible_timeout(wait, 0, 10); | ||
| 314 | |||
| 315 | /* Select random Address */ | ||
| 316 | buf[1] = 0xB2; | ||
| 317 | get_random_bytes(&buf[2], 2); | ||
| 318 | |||
| 319 | ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 288 | if (quirks & LG_FF) | 323 | if (quirks & LG_FF) |
| 289 | lgff_init(hdev); | 324 | lgff_init(hdev); |
| 290 | if (quirks & LG_FF2) | 325 | if (quirks & LG_FF2) |
| 291 | lg2ff_init(hdev); | 326 | lg2ff_init(hdev); |
| 292 | if (quirks & LG_FF3) | 327 | if (quirks & LG_FF3) |
| 293 | lg3ff_init(hdev); | 328 | lg3ff_init(hdev); |
| 329 | if (quirks & LG_FF4) | ||
| 330 | lg4ff_init(hdev); | ||
| 294 | 331 | ||
| 295 | return 0; | 332 | return 0; |
| 296 | err_free: | 333 | err_free: |
| @@ -325,6 +362,8 @@ static const struct hid_device_id lg_devices[] = { | |||
| 325 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), | 362 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), |
| 326 | .driver_data = LG_NOGET | LG_FF }, | 363 | .driver_data = LG_NOGET | LG_FF }, |
| 327 | 364 | ||
| 365 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD), | ||
| 366 | .driver_data = LG_FF2 }, | ||
| 328 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD), | 367 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD), |
| 329 | .driver_data = LG_FF }, | 368 | .driver_data = LG_FF }, |
| 330 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), | 369 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), |
| @@ -339,6 +378,8 @@ static const struct hid_device_id lg_devices[] = { | |||
| 339 | .driver_data = LG_FF }, | 378 | .driver_data = LG_FF }, |
| 340 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), | 379 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), |
| 341 | .driver_data = LG_FF }, | 380 | .driver_data = LG_FF }, |
| 381 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), | ||
| 382 | .driver_data = LG_FF4 }, | ||
| 342 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), | 383 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), |
| 343 | .driver_data = LG_FF }, | 384 | .driver_data = LG_FF }, |
| 344 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), | 385 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), |
