linux/drivers/hid/hid-lg.c
<<
>>
Prefs
   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
  31#define LG_RDESC                0x001
  32#define LG_BAD_RELATIVE_KEYS    0x002
  33#define LG_DUPLICATE_USAGES     0x004
  34#define LG_EXPANDED_KEYMAP      0x010
  35#define LG_IGNORE_DOUBLED_WHEEL 0x020
  36#define LG_WIRELESS             0x040
  37#define LG_INVERT_HWHEEL        0x080
  38#define LG_NOGET                0x100
  39#define LG_FF                   0x200
  40#define LG_FF2                  0x400
  41#define LG_RDESC_REL_ABS        0x800
  42#define LG_FF3                  0x1000
  43#define LG_FF4                  0x2000
  44
  45/* Size of the original descriptors of the Driving Force (and Pro) wheels */
  46#define DF_RDESC_ORIG_SIZE      130
  47#define DFP_RDESC_ORIG_SIZE     97
  48#define FV_RDESC_ORIG_SIZE      130
  49#define MOMO_RDESC_ORIG_SIZE    87
  50#define MOMO2_RDESC_ORIG_SIZE   87
  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 (Joystik),                        */
  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 (Joystik),                        */
 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 (Joystik),                        */
 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 (Joystik),                    */
 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 (Joystik),                    */
 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
 336/*
 337 * Certain Logitech keyboards send in report #3 keys which are far
 338 * above the logical maximum described in descriptor. This extends
 339 * the original value of 0x28c of logical maximum to 0x104d
 340 */
 341static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 342                unsigned int *rsize)
 343{
 344        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 345        struct usb_device_descriptor *udesc;
 346        __u16 bcdDevice, rev_maj, rev_min;
 347
 348        if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
 349                        rdesc[84] == 0x8c && rdesc[85] == 0x02) {
 350                hid_info(hdev,
 351                         "fixing up Logitech keyboard report descriptor\n");
 352                rdesc[84] = rdesc[89] = 0x4d;
 353                rdesc[85] = rdesc[90] = 0x10;
 354        }
 355        if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
 356                        rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
 357                        rdesc[49] == 0x81 && rdesc[50] == 0x06) {
 358                hid_info(hdev,
 359                         "fixing up rel/abs in Logitech report descriptor\n");
 360                rdesc[33] = rdesc[50] = 0x02;
 361        }
 362
 363        switch (hdev->product) {
 364
 365        /* Several wheels report as this id when operating in emulation mode. */
 366        case USB_DEVICE_ID_LOGITECH_WHEEL:
 367                udesc = &(hid_to_usb_dev(hdev)->descriptor);
 368                if (!udesc) {
 369                        hid_err(hdev, "NULL USB device descriptor\n");
 370                        break;
 371                }
 372                bcdDevice = le16_to_cpu(udesc->bcdDevice);
 373                rev_maj = bcdDevice >> 8;
 374                rev_min = bcdDevice & 0xff;
 375
 376                /* Update the report descriptor for only the Driving Force wheel */
 377                if (rev_maj == 1 && rev_min == 2 &&
 378                                *rsize == DF_RDESC_ORIG_SIZE) {
 379                        hid_info(hdev,
 380                                "fixing up Logitech Driving Force report descriptor\n");
 381                        rdesc = df_rdesc_fixed;
 382                        *rsize = sizeof(df_rdesc_fixed);
 383                }
 384                break;
 385
 386        case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
 387                if (*rsize == MOMO_RDESC_ORIG_SIZE) {
 388                        hid_info(hdev,
 389                                "fixing up Logitech Momo Force (Red) report descriptor\n");
 390                        rdesc = momo_rdesc_fixed;
 391                        *rsize = sizeof(momo_rdesc_fixed);
 392                }
 393                break;
 394
 395        case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
 396                if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
 397                        hid_info(hdev,
 398                                "fixing up Logitech Momo Racing Force (Black) report descriptor\n");
 399                        rdesc = momo2_rdesc_fixed;
 400                        *rsize = sizeof(momo2_rdesc_fixed);
 401                }
 402                break;
 403
 404        case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
 405                if (*rsize == FV_RDESC_ORIG_SIZE) {
 406                        hid_info(hdev,
 407                                "fixing up Logitech Formula Vibration report descriptor\n");
 408                        rdesc = fv_rdesc_fixed;
 409                        *rsize = sizeof(fv_rdesc_fixed);
 410                }
 411                break;
 412
 413        case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
 414                if (*rsize == DFP_RDESC_ORIG_SIZE) {
 415                        hid_info(hdev,
 416                                "fixing up Logitech Driving Force Pro report descriptor\n");
 417                        rdesc = dfp_rdesc_fixed;
 418                        *rsize = sizeof(dfp_rdesc_fixed);
 419                }
 420                break;
 421
 422        case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
 423                if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
 424                                rdesc[47] == 0x05 && rdesc[48] == 0x09) {
 425                        hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
 426                        rdesc[41] = 0x05;
 427                        rdesc[42] = 0x09;
 428                        rdesc[47] = 0x95;
 429                        rdesc[48] = 0x0B;
 430                }
 431                break;
 432        }
 433
 434        return rdesc;
 435}
 436
 437#define lg_map_key_clear(c)     hid_map_usage_clear(hi, usage, bit, max, \
 438                EV_KEY, (c))
 439
 440static int lg_ultrax_remote_mapping(struct hid_input *hi,
 441                struct hid_usage *usage, unsigned long **bit, int *max)
 442{
 443        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
 444                return 0;
 445
 446        set_bit(EV_REP, hi->input->evbit);
 447        switch (usage->hid & HID_USAGE) {
 448        /* Reported on Logitech Ultra X Media Remote */
 449        case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
 450        case 0x00d: lg_map_key_clear(KEY_HOME);         break;
 451        case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
 452        case 0x025: lg_map_key_clear(KEY_TV);           break;
 453        case 0x026: lg_map_key_clear(KEY_MENU);         break;
 454        case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
 455        case 0x032: lg_map_key_clear(KEY_TEXT);         break;
 456        case 0x033: lg_map_key_clear(KEY_LAST);         break;
 457        case 0x047: lg_map_key_clear(KEY_MP3);          break;
 458        case 0x048: lg_map_key_clear(KEY_DVD);          break;
 459        case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
 460        case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
 461        case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
 462        case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
 463        case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
 464        case 0x051: lg_map_key_clear(KEY_RED);          break;
 465        case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
 466
 467        default:
 468                return 0;
 469        }
 470        return 1;
 471}
 472
 473static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
 474                unsigned long **bit, int *max)
 475{
 476        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
 477                return 0;
 478
 479        switch (usage->hid & HID_USAGE) {
 480
 481        case 0x00d: lg_map_key_clear(KEY_MEDIA);        break;
 482        default:
 483                return 0;
 484
 485        }
 486        return 1;
 487}
 488
 489static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
 490                unsigned long **bit, int *max)
 491{
 492        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
 493                return 0;
 494
 495        switch (usage->hid & HID_USAGE) {
 496        case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
 497        case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
 498        case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
 499        case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
 500        case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
 501        /* The following two entries are Playlist 1 and 2 on the MX3200 */
 502        case 0x100f: lg_map_key_clear(KEY_FN_1);                break;
 503        case 0x1010: lg_map_key_clear(KEY_FN_2);                break;
 504        case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
 505        case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
 506        case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
 507        case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
 508        case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
 509        case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
 510        case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
 511        case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
 512        case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
 513        case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
 514        case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
 515        case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 516        case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
 517        case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
 518        case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
 519        case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
 520        case 0x1027: lg_map_key_clear(KEY_MENU);                break;
 521        /* this one is marked as 'Rotate' */
 522        case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
 523        case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
 524        case 0x102a: lg_map_key_clear(KEY_BACK);                break;
 525        case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 526        case 0x102d: lg_map_key_clear(KEY_WWW);                 break;
 527        /* The following two are 'Start/answer call' and 'End/reject call'
 528           on the MX3200 */
 529        case 0x1031: lg_map_key_clear(KEY_OK);                  break;
 530        case 0x1032: lg_map_key_clear(KEY_CANCEL);              break;
 531        case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
 532        case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
 533        case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
 534        case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
 535        case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
 536        case 0x1046: lg_map_key_clear(KEY_REDO);                break;
 537        case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
 538        case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
 539        case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
 540        case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
 541        case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
 542        case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
 543
 544        default:
 545                return 0;
 546        }
 547        return 1;
 548}
 549
 550static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 551                struct hid_field *field, struct hid_usage *usage,
 552                unsigned long **bit, int *max)
 553{
 554        /* extended mapping for certain Logitech hardware (Logitech cordless
 555           desktop LX500) */
 556        static const u8 e_keymap[] = {
 557                  0,216,  0,213,175,156,  0,  0,  0,  0,
 558                144,  0,  0,  0,  0,  0,  0,  0,  0,212,
 559                174,167,152,161,112,  0,  0,  0,154,  0,
 560                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 561                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 562                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 563                  0,  0,  0,  0,  0,183,184,185,186,187,
 564                188,189,190,191,192,193,194,  0,  0,  0
 565        };
 566        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 567        unsigned int hid = usage->hid;
 568
 569        if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
 570                        lg_ultrax_remote_mapping(hi, usage, bit, max))
 571                return 1;
 572
 573        if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
 574                        lg_dinovo_mapping(hi, usage, bit, max))
 575                return 1;
 576
 577        if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
 578                return 1;
 579
 580        if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
 581                return 0;
 582
 583        hid &= HID_USAGE;
 584
 585        /* Special handling for Logitech Cordless Desktop */
 586        if (field->application == HID_GD_MOUSE) {
 587                if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
 588                                (hid == 7 || hid == 8))
 589                        return -1;
 590        } else {
 591                if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
 592                                hid < ARRAY_SIZE(e_keymap) &&
 593                                e_keymap[hid] != 0) {
 594                        hid_map_usage(hi, usage, bit, max, EV_KEY,
 595                                        e_keymap[hid]);
 596                        return 1;
 597                }
 598        }
 599
 600        return 0;
 601}
 602
 603static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 604                struct hid_field *field, struct hid_usage *usage,
 605                unsigned long **bit, int *max)
 606{
 607        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 608
 609        if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
 610                        (field->flags & HID_MAIN_ITEM_RELATIVE))
 611                field->flags &= ~HID_MAIN_ITEM_RELATIVE;
 612
 613        if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
 614                         usage->type == EV_REL || usage->type == EV_ABS))
 615                clear_bit(usage->code, *bit);
 616
 617        /* Ensure that Logitech wheels are not given a default fuzz/flat value */
 618        if (usage->type == EV_ABS && (usage->code == ABS_X ||
 619                        usage->code == ABS_Y || usage->code == ABS_Z ||
 620                        usage->code == ABS_RZ)) {
 621                switch (hdev->product) {
 622                case USB_DEVICE_ID_LOGITECH_WHEEL:
 623                case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
 624                case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
 625                case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
 626                case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
 627                case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
 628                case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
 629                case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
 630                case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
 631                        field->application = HID_GD_MULTIAXIS;
 632                        break;
 633                default:
 634                        break;
 635                }
 636        }
 637
 638        return 0;
 639}
 640
 641static int lg_event(struct hid_device *hdev, struct hid_field *field,
 642                struct hid_usage *usage, __s32 value)
 643{
 644        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 645
 646        if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
 647                input_event(field->hidinput->input, usage->type, usage->code,
 648                                -value);
 649                return 1;
 650        }
 651        if (drv_data->quirks & LG_FF4) {
 652                return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
 653        }
 654
 655        return 0;
 656}
 657
 658static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 659{
 660        unsigned int connect_mask = HID_CONNECT_DEFAULT;
 661        struct lg_drv_data *drv_data;
 662        int ret;
 663
 664        drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
 665        if (!drv_data) {
 666                hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
 667                return -ENOMEM;
 668        }
 669        drv_data->quirks = id->driver_data;
 670
 671        hid_set_drvdata(hdev, (void *)drv_data);
 672
 673        if (drv_data->quirks & LG_NOGET)
 674                hdev->quirks |= HID_QUIRK_NOGET;
 675
 676        ret = hid_parse(hdev);
 677        if (ret) {
 678                hid_err(hdev, "parse failed\n");
 679                goto err_free;
 680        }
 681
 682        if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
 683                connect_mask &= ~HID_CONNECT_FF;
 684
 685        ret = hid_hw_start(hdev, connect_mask);
 686        if (ret) {
 687                hid_err(hdev, "hw start failed\n");
 688                goto err_free;
 689        }
 690
 691        /* Setup wireless link with Logitech Wii wheel */
 692        if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
 693                unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 694
 695                ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
 696                                        HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 697
 698                if (ret >= 0) {
 699                        /* insert a little delay of 10 jiffies ~ 40ms */
 700                        wait_queue_head_t wait;
 701                        init_waitqueue_head (&wait);
 702                        wait_event_interruptible_timeout(wait, 0, 10);
 703
 704                        /* Select random Address */
 705                        buf[1] = 0xB2;
 706                        get_random_bytes(&buf[2], 2);
 707
 708                        ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
 709                                        HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 710                }
 711        }
 712
 713        if (drv_data->quirks & LG_FF)
 714                lgff_init(hdev);
 715        if (drv_data->quirks & LG_FF2)
 716                lg2ff_init(hdev);
 717        if (drv_data->quirks & LG_FF3)
 718                lg3ff_init(hdev);
 719        if (drv_data->quirks & LG_FF4)
 720                lg4ff_init(hdev);
 721
 722        return 0;
 723err_free:
 724        kfree(drv_data);
 725        return ret;
 726}
 727
 728static void lg_remove(struct hid_device *hdev)
 729{
 730        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 731        if (drv_data->quirks & LG_FF4)
 732                lg4ff_deinit(hdev);
 733
 734        hid_hw_stop(hdev);
 735        kfree(drv_data);
 736}
 737
 738static const struct hid_device_id lg_devices[] = {
 739        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
 740                .driver_data = LG_RDESC | LG_WIRELESS },
 741        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
 742                .driver_data = LG_RDESC | LG_WIRELESS },
 743        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
 744                .driver_data = LG_RDESC | LG_WIRELESS },
 745
 746        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
 747                .driver_data = LG_BAD_RELATIVE_KEYS },
 748
 749        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
 750                .driver_data = LG_DUPLICATE_USAGES },
 751        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
 752                .driver_data = LG_DUPLICATE_USAGES },
 753        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
 754                .driver_data = LG_DUPLICATE_USAGES },
 755
 756        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
 757                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 758        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
 759                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 760
 761        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
 762                .driver_data = LG_NOGET },
 763        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
 764                .driver_data = LG_NOGET },
 765        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
 766                .driver_data = LG_NOGET | LG_FF4 },
 767
 768        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
 769                .driver_data = LG_FF2 },
 770        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
 771                .driver_data = LG_FF },
 772        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
 773                .driver_data = LG_FF },
 774        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
 775                .driver_data = LG_FF },
 776        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
 777                .driver_data = LG_FF },
 778        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
 779                .driver_data = LG_NOGET | LG_FF4 },
 780        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
 781                .driver_data = LG_FF4 },
 782        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
 783                .driver_data = LG_FF2 },
 784        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
 785                .driver_data = LG_FF4 },
 786        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
 787                .driver_data = LG_FF4 },
 788        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
 789                .driver_data = LG_FF4 },
 790        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
 791                .driver_data = LG_NOGET | LG_FF4 },
 792        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
 793                .driver_data = LG_FF4 },
 794        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
 795                .driver_data = LG_FF },
 796        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
 797                .driver_data = LG_FF2 },
 798        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
 799                .driver_data = LG_FF3 },
 800        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
 801                .driver_data = LG_RDESC_REL_ABS },
 802        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
 803                .driver_data = LG_RDESC_REL_ABS },
 804        { }
 805};
 806
 807MODULE_DEVICE_TABLE(hid, lg_devices);
 808
 809static struct hid_driver lg_driver = {
 810        .name = "logitech",
 811        .id_table = lg_devices,
 812        .report_fixup = lg_report_fixup,
 813        .input_mapping = lg_input_mapping,
 814        .input_mapped = lg_input_mapped,
 815        .event = lg_event,
 816        .probe = lg_probe,
 817        .remove = lg_remove,
 818};
 819module_hid_driver(lg_driver);
 820
 821MODULE_LICENSE("GPL");
 822