Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.19-rc6 908 lines 39 kB view raw
1/* 2 * HID driver for some logitech "special" devices 3 * 4 * Copyright (c) 1999 Andreas Gal 5 * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 6 * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 7 * Copyright (c) 2006-2007 Jiri Kosina 8 * Copyright (c) 2008 Jiri Slaby 9 * Copyright (c) 2010 Hendrik Iben 10 */ 11 12/* 13 * This program is free software; you can redistribute it and/or modify it 14 * under the terms of the GNU General Public License as published by the Free 15 * Software Foundation; either version 2 of the License, or (at your option) 16 * any later version. 17 */ 18 19#include <linux/device.h> 20#include <linux/hid.h> 21#include <linux/module.h> 22#include <linux/random.h> 23#include <linux/sched.h> 24#include <linux/usb.h> 25#include <linux/wait.h> 26 27#include "usbhid/usbhid.h" 28#include "hid-ids.h" 29#include "hid-lg.h" 30#include "hid-lg4ff.h" 31 32#define LG_RDESC 0x001 33#define LG_BAD_RELATIVE_KEYS 0x002 34#define LG_DUPLICATE_USAGES 0x004 35#define LG_EXPANDED_KEYMAP 0x010 36#define LG_IGNORE_DOUBLED_WHEEL 0x020 37#define LG_WIRELESS 0x040 38#define LG_INVERT_HWHEEL 0x080 39#define LG_NOGET 0x100 40#define LG_FF 0x200 41#define LG_FF2 0x400 42#define LG_RDESC_REL_ABS 0x800 43#define LG_FF3 0x1000 44#define LG_FF4 0x2000 45 46/* Size of the original descriptors of the Driving Force (and Pro) wheels */ 47#define DF_RDESC_ORIG_SIZE 130 48#define DFP_RDESC_ORIG_SIZE 97 49#define FV_RDESC_ORIG_SIZE 130 50#define MOMO_RDESC_ORIG_SIZE 87 51#define MOMO2_RDESC_ORIG_SIZE 87 52#define FFG_RDESC_ORIG_SIZE 85 53 54/* Fixed report descriptors for Logitech Driving Force (and Pro) 55 * wheel controllers 56 * 57 * The original descriptors hide the separate throttle and brake axes in 58 * a custom vendor usage page, providing only a combined value as 59 * GenericDesktop.Y. 60 * These descriptors remove the combined Y axis and instead report 61 * separate throttle (Y) and brake (RZ). 62 */ 63static __u8 df_rdesc_fixed[] = { 640x05, 0x01, /* Usage Page (Desktop), */ 650x09, 0x04, /* Usage (Joystick), */ 660xA1, 0x01, /* Collection (Application), */ 670xA1, 0x02, /* Collection (Logical), */ 680x95, 0x01, /* Report Count (1), */ 690x75, 0x0A, /* Report Size (10), */ 700x14, /* Logical Minimum (0), */ 710x26, 0xFF, 0x03, /* Logical Maximum (1023), */ 720x34, /* Physical Minimum (0), */ 730x46, 0xFF, 0x03, /* Physical Maximum (1023), */ 740x09, 0x30, /* Usage (X), */ 750x81, 0x02, /* Input (Variable), */ 760x95, 0x0C, /* Report Count (12), */ 770x75, 0x01, /* Report Size (1), */ 780x25, 0x01, /* Logical Maximum (1), */ 790x45, 0x01, /* Physical Maximum (1), */ 800x05, 0x09, /* Usage (Buttons), */ 810x19, 0x01, /* Usage Minimum (1), */ 820x29, 0x0c, /* Usage Maximum (12), */ 830x81, 0x02, /* Input (Variable), */ 840x95, 0x02, /* Report Count (2), */ 850x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */ 860x09, 0x01, /* Usage (?: 1), */ 870x81, 0x02, /* Input (Variable), */ 880x05, 0x01, /* Usage Page (Desktop), */ 890x26, 0xFF, 0x00, /* Logical Maximum (255), */ 900x46, 0xFF, 0x00, /* Physical Maximum (255), */ 910x95, 0x01, /* Report Count (1), */ 920x75, 0x08, /* Report Size (8), */ 930x81, 0x02, /* Input (Variable), */ 940x25, 0x07, /* Logical Maximum (7), */ 950x46, 0x3B, 0x01, /* Physical Maximum (315), */ 960x75, 0x04, /* Report Size (4), */ 970x65, 0x14, /* Unit (Degrees), */ 980x09, 0x39, /* Usage (Hat Switch), */ 990x81, 0x42, /* Input (Variable, Null State), */ 1000x75, 0x01, /* Report Size (1), */ 1010x95, 0x04, /* Report Count (4), */ 1020x65, 0x00, /* Unit (none), */ 1030x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */ 1040x09, 0x01, /* Usage (?: 1), */ 1050x25, 0x01, /* Logical Maximum (1), */ 1060x45, 0x01, /* Physical Maximum (1), */ 1070x81, 0x02, /* Input (Variable), */ 1080x05, 0x01, /* Usage Page (Desktop), */ 1090x95, 0x01, /* Report Count (1), */ 1100x75, 0x08, /* Report Size (8), */ 1110x26, 0xFF, 0x00, /* Logical Maximum (255), */ 1120x46, 0xFF, 0x00, /* Physical Maximum (255), */ 1130x09, 0x31, /* Usage (Y), */ 1140x81, 0x02, /* Input (Variable), */ 1150x09, 0x35, /* Usage (Rz), */ 1160x81, 0x02, /* Input (Variable), */ 1170xC0, /* End Collection, */ 1180xA1, 0x02, /* Collection (Logical), */ 1190x26, 0xFF, 0x00, /* Logical Maximum (255), */ 1200x46, 0xFF, 0x00, /* Physical Maximum (255), */ 1210x95, 0x07, /* Report Count (7), */ 1220x75, 0x08, /* Report Size (8), */ 1230x09, 0x03, /* Usage (?: 3), */ 1240x91, 0x02, /* Output (Variable), */ 1250xC0, /* End Collection, */ 1260xC0 /* End Collection */ 127}; 128 129static __u8 dfp_rdesc_fixed[] = { 1300x05, 0x01, /* Usage Page (Desktop), */ 1310x09, 0x04, /* Usage (Joystick), */ 1320xA1, 0x01, /* Collection (Application), */ 1330xA1, 0x02, /* Collection (Logical), */ 1340x95, 0x01, /* Report Count (1), */ 1350x75, 0x0E, /* Report Size (14), */ 1360x14, /* Logical Minimum (0), */ 1370x26, 0xFF, 0x3F, /* Logical Maximum (16383), */ 1380x34, /* Physical Minimum (0), */ 1390x46, 0xFF, 0x3F, /* Physical Maximum (16383), */ 1400x09, 0x30, /* Usage (X), */ 1410x81, 0x02, /* Input (Variable), */ 1420x95, 0x0E, /* Report Count (14), */ 1430x75, 0x01, /* Report Size (1), */ 1440x25, 0x01, /* Logical Maximum (1), */ 1450x45, 0x01, /* Physical Maximum (1), */ 1460x05, 0x09, /* Usage Page (Button), */ 1470x19, 0x01, /* Usage Minimum (01h), */ 1480x29, 0x0E, /* Usage Maximum (0Eh), */ 1490x81, 0x02, /* Input (Variable), */ 1500x05, 0x01, /* Usage Page (Desktop), */ 1510x95, 0x01, /* Report Count (1), */ 1520x75, 0x04, /* Report Size (4), */ 1530x25, 0x07, /* Logical Maximum (7), */ 1540x46, 0x3B, 0x01, /* Physical Maximum (315), */ 1550x65, 0x14, /* Unit (Degrees), */ 1560x09, 0x39, /* Usage (Hat Switch), */ 1570x81, 0x42, /* Input (Variable, Nullstate), */ 1580x65, 0x00, /* Unit, */ 1590x26, 0xFF, 0x00, /* Logical Maximum (255), */ 1600x46, 0xFF, 0x00, /* Physical Maximum (255), */ 1610x75, 0x08, /* Report Size (8), */ 1620x81, 0x01, /* Input (Constant), */ 1630x09, 0x31, /* Usage (Y), */ 1640x81, 0x02, /* Input (Variable), */ 1650x09, 0x35, /* Usage (Rz), */ 1660x81, 0x02, /* Input (Variable), */ 1670x81, 0x01, /* Input (Constant), */ 1680xC0, /* End Collection, */ 1690xA1, 0x02, /* Collection (Logical), */ 1700x09, 0x02, /* Usage (02h), */ 1710x95, 0x07, /* Report Count (7), */ 1720x91, 0x02, /* Output (Variable), */ 1730xC0, /* End Collection, */ 1740xC0 /* End Collection */ 175}; 176 177static __u8 fv_rdesc_fixed[] = { 1780x05, 0x01, /* Usage Page (Desktop), */ 1790x09, 0x04, /* Usage (Joystick), */ 1800xA1, 0x01, /* Collection (Application), */ 1810xA1, 0x02, /* Collection (Logical), */ 1820x95, 0x01, /* Report Count (1), */ 1830x75, 0x0A, /* Report Size (10), */ 1840x15, 0x00, /* Logical Minimum (0), */ 1850x26, 0xFF, 0x03, /* Logical Maximum (1023), */ 1860x35, 0x00, /* Physical Minimum (0), */ 1870x46, 0xFF, 0x03, /* Physical Maximum (1023), */ 1880x09, 0x30, /* Usage (X), */ 1890x81, 0x02, /* Input (Variable), */ 1900x95, 0x0C, /* Report Count (12), */ 1910x75, 0x01, /* Report Size (1), */ 1920x25, 0x01, /* Logical Maximum (1), */ 1930x45, 0x01, /* Physical Maximum (1), */ 1940x05, 0x09, /* Usage Page (Button), */ 1950x19, 0x01, /* Usage Minimum (01h), */ 1960x29, 0x0C, /* Usage Maximum (0Ch), */ 1970x81, 0x02, /* Input (Variable), */ 1980x95, 0x02, /* Report Count (2), */ 1990x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 2000x09, 0x01, /* Usage (01h), */ 2010x81, 0x02, /* Input (Variable), */ 2020x09, 0x02, /* Usage (02h), */ 2030x26, 0xFF, 0x00, /* Logical Maximum (255), */ 2040x46, 0xFF, 0x00, /* Physical Maximum (255), */ 2050x95, 0x01, /* Report Count (1), */ 2060x75, 0x08, /* Report Size (8), */ 2070x81, 0x02, /* Input (Variable), */ 2080x05, 0x01, /* Usage Page (Desktop), */ 2090x25, 0x07, /* Logical Maximum (7), */ 2100x46, 0x3B, 0x01, /* Physical Maximum (315), */ 2110x75, 0x04, /* Report Size (4), */ 2120x65, 0x14, /* Unit (Degrees), */ 2130x09, 0x39, /* Usage (Hat Switch), */ 2140x81, 0x42, /* Input (Variable, Null State), */ 2150x75, 0x01, /* Report Size (1), */ 2160x95, 0x04, /* Report Count (4), */ 2170x65, 0x00, /* Unit, */ 2180x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 2190x09, 0x01, /* Usage (01h), */ 2200x25, 0x01, /* Logical Maximum (1), */ 2210x45, 0x01, /* Physical Maximum (1), */ 2220x81, 0x02, /* Input (Variable), */ 2230x05, 0x01, /* Usage Page (Desktop), */ 2240x95, 0x01, /* Report Count (1), */ 2250x75, 0x08, /* Report Size (8), */ 2260x26, 0xFF, 0x00, /* Logical Maximum (255), */ 2270x46, 0xFF, 0x00, /* Physical Maximum (255), */ 2280x09, 0x31, /* Usage (Y), */ 2290x81, 0x02, /* Input (Variable), */ 2300x09, 0x32, /* Usage (Z), */ 2310x81, 0x02, /* Input (Variable), */ 2320xC0, /* End Collection, */ 2330xA1, 0x02, /* Collection (Logical), */ 2340x26, 0xFF, 0x00, /* Logical Maximum (255), */ 2350x46, 0xFF, 0x00, /* Physical Maximum (255), */ 2360x95, 0x07, /* Report Count (7), */ 2370x75, 0x08, /* Report Size (8), */ 2380x09, 0x03, /* Usage (03h), */ 2390x91, 0x02, /* Output (Variable), */ 2400xC0, /* End Collection, */ 2410xC0 /* End Collection */ 242}; 243 244static __u8 momo_rdesc_fixed[] = { 2450x05, 0x01, /* Usage Page (Desktop), */ 2460x09, 0x04, /* Usage (Joystick), */ 2470xA1, 0x01, /* Collection (Application), */ 2480xA1, 0x02, /* Collection (Logical), */ 2490x95, 0x01, /* Report Count (1), */ 2500x75, 0x0A, /* Report Size (10), */ 2510x15, 0x00, /* Logical Minimum (0), */ 2520x26, 0xFF, 0x03, /* Logical Maximum (1023), */ 2530x35, 0x00, /* Physical Minimum (0), */ 2540x46, 0xFF, 0x03, /* Physical Maximum (1023), */ 2550x09, 0x30, /* Usage (X), */ 2560x81, 0x02, /* Input (Variable), */ 2570x95, 0x08, /* Report Count (8), */ 2580x75, 0x01, /* Report Size (1), */ 2590x25, 0x01, /* Logical Maximum (1), */ 2600x45, 0x01, /* Physical Maximum (1), */ 2610x05, 0x09, /* Usage Page (Button), */ 2620x19, 0x01, /* Usage Minimum (01h), */ 2630x29, 0x08, /* Usage Maximum (08h), */ 2640x81, 0x02, /* Input (Variable), */ 2650x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 2660x75, 0x0E, /* Report Size (14), */ 2670x95, 0x01, /* Report Count (1), */ 2680x26, 0xFF, 0x00, /* Logical Maximum (255), */ 2690x46, 0xFF, 0x00, /* Physical Maximum (255), */ 2700x09, 0x00, /* Usage (00h), */ 2710x81, 0x02, /* Input (Variable), */ 2720x05, 0x01, /* Usage Page (Desktop), */ 2730x75, 0x08, /* Report Size (8), */ 2740x09, 0x31, /* Usage (Y), */ 2750x81, 0x02, /* Input (Variable), */ 2760x09, 0x32, /* Usage (Z), */ 2770x81, 0x02, /* Input (Variable), */ 2780x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 2790x09, 0x01, /* Usage (01h), */ 2800x81, 0x02, /* Input (Variable), */ 2810xC0, /* End Collection, */ 2820xA1, 0x02, /* Collection (Logical), */ 2830x09, 0x02, /* Usage (02h), */ 2840x95, 0x07, /* Report Count (7), */ 2850x91, 0x02, /* Output (Variable), */ 2860xC0, /* End Collection, */ 2870xC0 /* End Collection */ 288}; 289 290static __u8 momo2_rdesc_fixed[] = { 2910x05, 0x01, /* Usage Page (Desktop), */ 2920x09, 0x04, /* Usage (Joystick), */ 2930xA1, 0x01, /* Collection (Application), */ 2940xA1, 0x02, /* Collection (Logical), */ 2950x95, 0x01, /* Report Count (1), */ 2960x75, 0x0A, /* Report Size (10), */ 2970x15, 0x00, /* Logical Minimum (0), */ 2980x26, 0xFF, 0x03, /* Logical Maximum (1023), */ 2990x35, 0x00, /* Physical Minimum (0), */ 3000x46, 0xFF, 0x03, /* Physical Maximum (1023), */ 3010x09, 0x30, /* Usage (X), */ 3020x81, 0x02, /* Input (Variable), */ 3030x95, 0x0A, /* Report Count (10), */ 3040x75, 0x01, /* Report Size (1), */ 3050x25, 0x01, /* Logical Maximum (1), */ 3060x45, 0x01, /* Physical Maximum (1), */ 3070x05, 0x09, /* Usage Page (Button), */ 3080x19, 0x01, /* Usage Minimum (01h), */ 3090x29, 0x0A, /* Usage Maximum (0Ah), */ 3100x81, 0x02, /* Input (Variable), */ 3110x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 3120x09, 0x00, /* Usage (00h), */ 3130x95, 0x04, /* Report Count (4), */ 3140x81, 0x02, /* Input (Variable), */ 3150x95, 0x01, /* Report Count (1), */ 3160x75, 0x08, /* Report Size (8), */ 3170x26, 0xFF, 0x00, /* Logical Maximum (255), */ 3180x46, 0xFF, 0x00, /* Physical Maximum (255), */ 3190x09, 0x01, /* Usage (01h), */ 3200x81, 0x02, /* Input (Variable), */ 3210x05, 0x01, /* Usage Page (Desktop), */ 3220x09, 0x31, /* Usage (Y), */ 3230x81, 0x02, /* Input (Variable), */ 3240x09, 0x32, /* Usage (Z), */ 3250x81, 0x02, /* Input (Variable), */ 3260x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 3270x09, 0x00, /* Usage (00h), */ 3280x81, 0x02, /* Input (Variable), */ 3290xC0, /* End Collection, */ 3300xA1, 0x02, /* Collection (Logical), */ 3310x09, 0x02, /* Usage (02h), */ 3320x95, 0x07, /* Report Count (7), */ 3330x91, 0x02, /* Output (Variable), */ 3340xC0, /* End Collection, */ 3350xC0 /* End Collection */ 336}; 337 338static __u8 ffg_rdesc_fixed[] = { 3390x05, 0x01, /* Usage Page (Desktop), */ 3400x09, 0x04, /* Usage (Joystik), */ 3410xA1, 0x01, /* Collection (Application), */ 3420xA1, 0x02, /* Collection (Logical), */ 3430x95, 0x01, /* Report Count (1), */ 3440x75, 0x0A, /* Report Size (10), */ 3450x15, 0x00, /* Logical Minimum (0), */ 3460x26, 0xFF, 0x03, /* Logical Maximum (1023), */ 3470x35, 0x00, /* Physical Minimum (0), */ 3480x46, 0xFF, 0x03, /* Physical Maximum (1023), */ 3490x09, 0x30, /* Usage (X), */ 3500x81, 0x02, /* Input (Variable), */ 3510x95, 0x06, /* Report Count (6), */ 3520x75, 0x01, /* Report Size (1), */ 3530x25, 0x01, /* Logical Maximum (1), */ 3540x45, 0x01, /* Physical Maximum (1), */ 3550x05, 0x09, /* Usage Page (Button), */ 3560x19, 0x01, /* Usage Minimum (01h), */ 3570x29, 0x06, /* Usage Maximum (06h), */ 3580x81, 0x02, /* Input (Variable), */ 3590x95, 0x01, /* Report Count (1), */ 3600x75, 0x08, /* Report Size (8), */ 3610x26, 0xFF, 0x00, /* Logical Maximum (255), */ 3620x46, 0xFF, 0x00, /* Physical Maximum (255), */ 3630x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 3640x09, 0x01, /* Usage (01h), */ 3650x81, 0x02, /* Input (Variable), */ 3660x05, 0x01, /* Usage Page (Desktop), */ 3670x81, 0x01, /* Input (Constant), */ 3680x09, 0x31, /* Usage (Y), */ 3690x81, 0x02, /* Input (Variable), */ 3700x09, 0x32, /* Usage (Z), */ 3710x81, 0x02, /* Input (Variable), */ 3720x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 3730x09, 0x01, /* Usage (01h), */ 3740x81, 0x02, /* Input (Variable), */ 3750xC0, /* End Collection, */ 3760xA1, 0x02, /* Collection (Logical), */ 3770x09, 0x02, /* Usage (02h), */ 3780x95, 0x07, /* Report Count (7), */ 3790x91, 0x02, /* Output (Variable), */ 3800xC0, /* End Collection, */ 3810xC0 /* End Collection */ 382}; 383 384/* 385 * Certain Logitech keyboards send in report #3 keys which are far 386 * above the logical maximum described in descriptor. This extends 387 * the original value of 0x28c of logical maximum to 0x104d 388 */ 389static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, 390 unsigned int *rsize) 391{ 392 struct lg_drv_data *drv_data = hid_get_drvdata(hdev); 393 394 if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 && 395 rdesc[84] == 0x8c && rdesc[85] == 0x02) { 396 hid_info(hdev, 397 "fixing up Logitech keyboard report descriptor\n"); 398 rdesc[84] = rdesc[89] = 0x4d; 399 rdesc[85] = rdesc[90] = 0x10; 400 } 401 if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 && 402 rdesc[32] == 0x81 && rdesc[33] == 0x06 && 403 rdesc[49] == 0x81 && rdesc[50] == 0x06) { 404 hid_info(hdev, 405 "fixing up rel/abs in Logitech report descriptor\n"); 406 rdesc[33] = rdesc[50] = 0x02; 407 } 408 409 switch (hdev->product) { 410 411 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: 412 if (*rsize == FFG_RDESC_ORIG_SIZE) { 413 hid_info(hdev, 414 "fixing up Logitech Wingman Formula Force GP report descriptor\n"); 415 rdesc = ffg_rdesc_fixed; 416 *rsize = sizeof(ffg_rdesc_fixed); 417 } 418 break; 419 420 /* Several wheels report as this id when operating in emulation mode. */ 421 case USB_DEVICE_ID_LOGITECH_WHEEL: 422 if (*rsize == DF_RDESC_ORIG_SIZE) { 423 hid_info(hdev, 424 "fixing up Logitech Driving Force report descriptor\n"); 425 rdesc = df_rdesc_fixed; 426 *rsize = sizeof(df_rdesc_fixed); 427 } 428 break; 429 430 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: 431 if (*rsize == MOMO_RDESC_ORIG_SIZE) { 432 hid_info(hdev, 433 "fixing up Logitech Momo Force (Red) report descriptor\n"); 434 rdesc = momo_rdesc_fixed; 435 *rsize = sizeof(momo_rdesc_fixed); 436 } 437 break; 438 439 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: 440 if (*rsize == MOMO2_RDESC_ORIG_SIZE) { 441 hid_info(hdev, 442 "fixing up Logitech Momo Racing Force (Black) report descriptor\n"); 443 rdesc = momo2_rdesc_fixed; 444 *rsize = sizeof(momo2_rdesc_fixed); 445 } 446 break; 447 448 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL: 449 if (*rsize == FV_RDESC_ORIG_SIZE) { 450 hid_info(hdev, 451 "fixing up Logitech Formula Vibration report descriptor\n"); 452 rdesc = fv_rdesc_fixed; 453 *rsize = sizeof(fv_rdesc_fixed); 454 } 455 break; 456 457 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: 458 if (*rsize == DFP_RDESC_ORIG_SIZE) { 459 hid_info(hdev, 460 "fixing up Logitech Driving Force Pro report descriptor\n"); 461 rdesc = dfp_rdesc_fixed; 462 *rsize = sizeof(dfp_rdesc_fixed); 463 } 464 break; 465 466 case USB_DEVICE_ID_LOGITECH_WII_WHEEL: 467 if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B && 468 rdesc[47] == 0x05 && rdesc[48] == 0x09) { 469 hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n"); 470 rdesc[41] = 0x05; 471 rdesc[42] = 0x09; 472 rdesc[47] = 0x95; 473 rdesc[48] = 0x0B; 474 } 475 break; 476 } 477 478 return rdesc; 479} 480 481#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ 482 EV_KEY, (c)) 483 484static int lg_ultrax_remote_mapping(struct hid_input *hi, 485 struct hid_usage *usage, unsigned long **bit, int *max) 486{ 487 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) 488 return 0; 489 490 set_bit(EV_REP, hi->input->evbit); 491 switch (usage->hid & HID_USAGE) { 492 /* Reported on Logitech Ultra X Media Remote */ 493 case 0x004: lg_map_key_clear(KEY_AGAIN); break; 494 case 0x00d: lg_map_key_clear(KEY_HOME); break; 495 case 0x024: lg_map_key_clear(KEY_SHUFFLE); break; 496 case 0x025: lg_map_key_clear(KEY_TV); break; 497 case 0x026: lg_map_key_clear(KEY_MENU); break; 498 case 0x031: lg_map_key_clear(KEY_AUDIO); break; 499 case 0x032: lg_map_key_clear(KEY_TEXT); break; 500 case 0x033: lg_map_key_clear(KEY_LAST); break; 501 case 0x047: lg_map_key_clear(KEY_MP3); break; 502 case 0x048: lg_map_key_clear(KEY_DVD); break; 503 case 0x049: lg_map_key_clear(KEY_MEDIA); break; 504 case 0x04a: lg_map_key_clear(KEY_VIDEO); break; 505 case 0x04b: lg_map_key_clear(KEY_ANGLE); break; 506 case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break; 507 case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break; 508 case 0x051: lg_map_key_clear(KEY_RED); break; 509 case 0x052: lg_map_key_clear(KEY_CLOSE); break; 510 511 default: 512 return 0; 513 } 514 return 1; 515} 516 517static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage, 518 unsigned long **bit, int *max) 519{ 520 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) 521 return 0; 522 523 switch (usage->hid & HID_USAGE) { 524 525 case 0x00d: lg_map_key_clear(KEY_MEDIA); break; 526 default: 527 return 0; 528 529 } 530 return 1; 531} 532 533static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage, 534 unsigned long **bit, int *max) 535{ 536 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) 537 return 0; 538 539 switch (usage->hid & HID_USAGE) { 540 case 0x1001: lg_map_key_clear(KEY_MESSENGER); break; 541 case 0x1003: lg_map_key_clear(KEY_SOUND); break; 542 case 0x1004: lg_map_key_clear(KEY_VIDEO); break; 543 case 0x1005: lg_map_key_clear(KEY_AUDIO); break; 544 case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break; 545 /* The following two entries are Playlist 1 and 2 on the MX3200 */ 546 case 0x100f: lg_map_key_clear(KEY_FN_1); break; 547 case 0x1010: lg_map_key_clear(KEY_FN_2); break; 548 case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break; 549 case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break; 550 case 0x1013: lg_map_key_clear(KEY_CAMERA); break; 551 case 0x1014: lg_map_key_clear(KEY_MESSENGER); break; 552 case 0x1015: lg_map_key_clear(KEY_RECORD); break; 553 case 0x1016: lg_map_key_clear(KEY_PLAYER); break; 554 case 0x1017: lg_map_key_clear(KEY_EJECTCD); break; 555 case 0x1018: lg_map_key_clear(KEY_MEDIA); break; 556 case 0x1019: lg_map_key_clear(KEY_PROG1); break; 557 case 0x101a: lg_map_key_clear(KEY_PROG2); break; 558 case 0x101b: lg_map_key_clear(KEY_PROG3); break; 559 case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); break; 560 case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break; 561 case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break; 562 case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break; 563 case 0x1023: lg_map_key_clear(KEY_CLOSE); break; 564 case 0x1027: lg_map_key_clear(KEY_MENU); break; 565 /* this one is marked as 'Rotate' */ 566 case 0x1028: lg_map_key_clear(KEY_ANGLE); break; 567 case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break; 568 case 0x102a: lg_map_key_clear(KEY_BACK); break; 569 case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break; 570 case 0x102d: lg_map_key_clear(KEY_WWW); break; 571 /* The following two are 'Start/answer call' and 'End/reject call' 572 on the MX3200 */ 573 case 0x1031: lg_map_key_clear(KEY_OK); break; 574 case 0x1032: lg_map_key_clear(KEY_CANCEL); break; 575 case 0x1041: lg_map_key_clear(KEY_BATTERY); break; 576 case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break; 577 case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break; 578 case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break; 579 case 0x1045: lg_map_key_clear(KEY_UNDO); break; 580 case 0x1046: lg_map_key_clear(KEY_REDO); break; 581 case 0x1047: lg_map_key_clear(KEY_PRINT); break; 582 case 0x1048: lg_map_key_clear(KEY_SAVE); break; 583 case 0x1049: lg_map_key_clear(KEY_PROG1); break; 584 case 0x104a: lg_map_key_clear(KEY_PROG2); break; 585 case 0x104b: lg_map_key_clear(KEY_PROG3); break; 586 case 0x104c: lg_map_key_clear(KEY_PROG4); break; 587 588 default: 589 return 0; 590 } 591 return 1; 592} 593 594static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, 595 struct hid_field *field, struct hid_usage *usage, 596 unsigned long **bit, int *max) 597{ 598 /* extended mapping for certain Logitech hardware (Logitech cordless 599 desktop LX500) */ 600 static const u8 e_keymap[] = { 601 0,216, 0,213,175,156, 0, 0, 0, 0, 602 144, 0, 0, 0, 0, 0, 0, 0, 0,212, 603 174,167,152,161,112, 0, 0, 0,154, 0, 604 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 605 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 606 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 607 0, 0, 0, 0, 0,183,184,185,186,187, 608 188,189,190,191,192,193,194, 0, 0, 0 609 }; 610 struct lg_drv_data *drv_data = hid_get_drvdata(hdev); 611 unsigned int hid = usage->hid; 612 613 if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && 614 lg_ultrax_remote_mapping(hi, usage, bit, max)) 615 return 1; 616 617 if (hdev->product == USB_DEVICE_ID_DINOVO_MINI && 618 lg_dinovo_mapping(hi, usage, bit, max)) 619 return 1; 620 621 if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) 622 return 1; 623 624 if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON) 625 return 0; 626 627 hid &= HID_USAGE; 628 629 /* Special handling for Logitech Cordless Desktop */ 630 if (field->application == HID_GD_MOUSE) { 631 if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) && 632 (hid == 7 || hid == 8)) 633 return -1; 634 } else { 635 if ((drv_data->quirks & LG_EXPANDED_KEYMAP) && 636 hid < ARRAY_SIZE(e_keymap) && 637 e_keymap[hid] != 0) { 638 hid_map_usage(hi, usage, bit, max, EV_KEY, 639 e_keymap[hid]); 640 return 1; 641 } 642 } 643 644 return 0; 645} 646 647static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, 648 struct hid_field *field, struct hid_usage *usage, 649 unsigned long **bit, int *max) 650{ 651 struct lg_drv_data *drv_data = hid_get_drvdata(hdev); 652 653 if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && 654 (field->flags & HID_MAIN_ITEM_RELATIVE)) 655 field->flags &= ~HID_MAIN_ITEM_RELATIVE; 656 657 if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY || 658 usage->type == EV_REL || usage->type == EV_ABS)) 659 clear_bit(usage->code, *bit); 660 661 /* Ensure that Logitech wheels are not given a default fuzz/flat value */ 662 if (usage->type == EV_ABS && (usage->code == ABS_X || 663 usage->code == ABS_Y || usage->code == ABS_Z || 664 usage->code == ABS_RZ)) { 665 switch (hdev->product) { 666 case USB_DEVICE_ID_LOGITECH_G29_WHEEL: 667 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: 668 case USB_DEVICE_ID_LOGITECH_WHEEL: 669 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: 670 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: 671 case USB_DEVICE_ID_LOGITECH_G25_WHEEL: 672 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: 673 case USB_DEVICE_ID_LOGITECH_G27_WHEEL: 674 case USB_DEVICE_ID_LOGITECH_WII_WHEEL: 675 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: 676 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL: 677 field->application = HID_GD_MULTIAXIS; 678 break; 679 default: 680 break; 681 } 682 } 683 684 return 0; 685} 686 687static int lg_event(struct hid_device *hdev, struct hid_field *field, 688 struct hid_usage *usage, __s32 value) 689{ 690 struct lg_drv_data *drv_data = hid_get_drvdata(hdev); 691 692 if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { 693 input_event(field->hidinput->input, usage->type, usage->code, 694 -value); 695 return 1; 696 } 697 if (drv_data->quirks & LG_FF4) { 698 return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data); 699 } 700 701 return 0; 702} 703 704static int lg_raw_event(struct hid_device *hdev, struct hid_report *report, 705 u8 *rd, int size) 706{ 707 struct lg_drv_data *drv_data = hid_get_drvdata(hdev); 708 709 if (drv_data->quirks & LG_FF4) 710 return lg4ff_raw_event(hdev, report, rd, size, drv_data); 711 712 return 0; 713} 714 715static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) 716{ 717 struct usb_interface *iface = to_usb_interface(hdev->dev.parent); 718 __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; 719 unsigned int connect_mask = HID_CONNECT_DEFAULT; 720 struct lg_drv_data *drv_data; 721 int ret; 722 723 /* G29 only work with the 1st interface */ 724 if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) && 725 (iface_num != 0)) { 726 dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num); 727 return -ENODEV; 728 } 729 730 drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL); 731 if (!drv_data) { 732 hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); 733 return -ENOMEM; 734 } 735 drv_data->quirks = id->driver_data; 736 737 hid_set_drvdata(hdev, (void *)drv_data); 738 739 if (drv_data->quirks & LG_NOGET) 740 hdev->quirks |= HID_QUIRK_NOGET; 741 742 ret = hid_parse(hdev); 743 if (ret) { 744 hid_err(hdev, "parse failed\n"); 745 goto err_free; 746 } 747 748 if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4)) 749 connect_mask &= ~HID_CONNECT_FF; 750 751 ret = hid_hw_start(hdev, connect_mask); 752 if (ret) { 753 hid_err(hdev, "hw start failed\n"); 754 goto err_free; 755 } 756 757 /* Setup wireless link with Logitech Wii wheel */ 758 if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { 759 static const unsigned char cbuf[] = { 760 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 761 }; 762 u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL); 763 764 if (!buf) { 765 ret = -ENOMEM; 766 goto err_free; 767 } 768 769 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf), 770 HID_FEATURE_REPORT, HID_REQ_SET_REPORT); 771 if (ret >= 0) { 772 /* insert a little delay of 10 jiffies ~ 40ms */ 773 wait_queue_head_t wait; 774 init_waitqueue_head (&wait); 775 wait_event_interruptible_timeout(wait, 0, 776 msecs_to_jiffies(40)); 777 778 /* Select random Address */ 779 buf[1] = 0xB2; 780 get_random_bytes(&buf[2], 2); 781 782 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf), 783 HID_FEATURE_REPORT, HID_REQ_SET_REPORT); 784 } 785 kfree(buf); 786 } 787 788 if (drv_data->quirks & LG_FF) 789 ret = lgff_init(hdev); 790 else if (drv_data->quirks & LG_FF2) 791 ret = lg2ff_init(hdev); 792 else if (drv_data->quirks & LG_FF3) 793 ret = lg3ff_init(hdev); 794 else if (drv_data->quirks & LG_FF4) 795 ret = lg4ff_init(hdev); 796 797 if (ret) 798 goto err_free; 799 800 return 0; 801err_free: 802 kfree(drv_data); 803 return ret; 804} 805 806static void lg_remove(struct hid_device *hdev) 807{ 808 struct lg_drv_data *drv_data = hid_get_drvdata(hdev); 809 if (drv_data->quirks & LG_FF4) 810 lg4ff_deinit(hdev); 811 else 812 hid_hw_stop(hdev); 813 kfree(drv_data); 814} 815 816static const struct hid_device_id lg_devices[] = { 817 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), 818 .driver_data = LG_RDESC | LG_WIRELESS }, 819 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER), 820 .driver_data = LG_RDESC | LG_WIRELESS }, 821 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), 822 .driver_data = LG_RDESC | LG_WIRELESS }, 823 824 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER), 825 .driver_data = LG_BAD_RELATIVE_KEYS }, 826 827 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP), 828 .driver_data = LG_DUPLICATE_USAGES }, 829 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE), 830 .driver_data = LG_DUPLICATE_USAGES }, 831 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), 832 .driver_data = LG_DUPLICATE_USAGES }, 833 834 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), 835 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, 836 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500), 837 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, 838 839 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), 840 .driver_data = LG_NOGET }, 841 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION), 842 .driver_data = LG_NOGET }, 843 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), 844 .driver_data = LG_NOGET | LG_FF4 }, 845 846 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD), 847 .driver_data = LG_FF2 }, 848 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD), 849 .driver_data = LG_FF }, 850 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), 851 .driver_data = LG_FF }, 852 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL), 853 .driver_data = LG_FF4 }, 854 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D), 855 .driver_data = LG_FF }, 856 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO), 857 .driver_data = LG_FF }, 858 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL), 859 .driver_data = LG_NOGET | LG_FF4 }, 860 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2), 861 .driver_data = LG_FF4 }, 862 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL), 863 .driver_data = LG_FF2 }, 864 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), 865 .driver_data = LG_FF4 }, 866 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL), 867 .driver_data = LG_FF4 }, 868 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL), 869 .driver_data = LG_FF4 }, 870 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL), 871 .driver_data = LG_NOGET | LG_FF4 }, 872 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), 873 .driver_data = LG_FF4 }, 874 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG), 875 .driver_data = LG_NOGET | LG_FF4 }, 876 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), 877 .driver_data = LG_NOGET | LG_FF2 }, 878 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940), 879 .driver_data = LG_FF3 }, 880 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR), 881 .driver_data = LG_RDESC_REL_ABS }, 882 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER), 883 .driver_data = LG_RDESC_REL_ABS }, 884 { } 885}; 886 887MODULE_DEVICE_TABLE(hid, lg_devices); 888 889static struct hid_driver lg_driver = { 890 .name = "logitech", 891 .id_table = lg_devices, 892 .report_fixup = lg_report_fixup, 893 .input_mapping = lg_input_mapping, 894 .input_mapped = lg_input_mapped, 895 .event = lg_event, 896 .raw_event = lg_raw_event, 897 .probe = lg_probe, 898 .remove = lg_remove, 899}; 900module_hid_driver(lg_driver); 901 902#ifdef CONFIG_LOGIWHEELS_FF 903int lg4ff_no_autoswitch = 0; 904module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO); 905MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically"); 906#endif 907 908MODULE_LICENSE("GPL");