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