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 MOMO_RDESC_ORIG_SIZE    87
  49
  50/* Fixed report descriptors for Logitech Driving Force (and Pro)
  51 * wheel controllers
  52 *
  53 * The original descriptors hide the separate throttle and brake axes in
  54 * a custom vendor usage page, providing only a combined value as
  55 * GenericDesktop.Y.
  56 * These descriptors remove the combined Y axis and instead report
  57 * separate throttle (Y) and brake (RZ).
  58 */
  59static __u8 df_rdesc_fixed[] = {
  600x05, 0x01,         /*  Usage Page (Desktop),                   */
  610x09, 0x04,         /*  Usage (Joystik),                        */
  620xA1, 0x01,         /*  Collection (Application),               */
  630xA1, 0x02,         /*      Collection (Logical),               */
  640x95, 0x01,         /*          Report Count (1),               */
  650x75, 0x0A,         /*          Report Size (10),               */
  660x14,               /*          Logical Minimum (0),            */
  670x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
  680x34,               /*          Physical Minimum (0),           */
  690x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
  700x09, 0x30,         /*          Usage (X),                      */
  710x81, 0x02,         /*          Input (Variable),               */
  720x95, 0x0C,         /*          Report Count (12),              */
  730x75, 0x01,         /*          Report Size (1),                */
  740x25, 0x01,         /*          Logical Maximum (1),            */
  750x45, 0x01,         /*          Physical Maximum (1),           */
  760x05, 0x09,         /*          Usage (Buttons),                */
  770x19, 0x01,         /*          Usage Minimum (1),              */
  780x29, 0x0c,         /*          Usage Maximum (12),             */
  790x81, 0x02,         /*          Input (Variable),               */
  800x95, 0x02,         /*          Report Count (2),               */
  810x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
  820x09, 0x01,         /*          Usage (?: 1),                   */
  830x81, 0x02,         /*          Input (Variable),               */
  840x05, 0x01,         /*          Usage Page (Desktop),           */
  850x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
  860x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
  870x95, 0x01,         /*          Report Count (1),               */
  880x75, 0x08,         /*          Report Size (8),                */
  890x81, 0x02,         /*          Input (Variable),               */
  900x25, 0x07,         /*          Logical Maximum (7),            */
  910x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
  920x75, 0x04,         /*          Report Size (4),                */
  930x65, 0x14,         /*          Unit (Degrees),                 */
  940x09, 0x39,         /*          Usage (Hat Switch),             */
  950x81, 0x42,         /*          Input (Variable, Null State),   */
  960x75, 0x01,         /*          Report Size (1),                */
  970x95, 0x04,         /*          Report Count (4),               */
  980x65, 0x00,         /*          Unit (none),                    */
  990x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
 1000x09, 0x01,         /*          Usage (?: 1),                   */
 1010x25, 0x01,         /*          Logical Maximum (1),            */
 1020x45, 0x01,         /*          Physical Maximum (1),           */
 1030x81, 0x02,         /*          Input (Variable),               */
 1040x05, 0x01,         /*          Usage Page (Desktop),           */
 1050x95, 0x01,         /*          Report Count (1),               */
 1060x75, 0x08,         /*          Report Size (8),                */
 1070x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
 1080x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
 1090x09, 0x31,         /*          Usage (Y),                      */
 1100x81, 0x02,         /*          Input (Variable),               */
 1110x09, 0x35,         /*          Usage (Rz),                     */
 1120x81, 0x02,         /*          Input (Variable),               */
 1130xC0,               /*      End Collection,                     */
 1140xA1, 0x02,         /*      Collection (Logical),               */
 1150x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
 1160x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
 1170x95, 0x07,         /*          Report Count (7),               */
 1180x75, 0x08,         /*          Report Size (8),                */
 1190x09, 0x03,         /*          Usage (?: 3),                   */
 1200x91, 0x02,         /*          Output (Variable),              */
 1210xC0,               /*      End Collection,                     */
 1220xC0                /*  End Collection                          */
 123};
 124
 125static __u8 dfp_rdesc_fixed[] = {
 1260x05, 0x01,         /*  Usage Page (Desktop),                   */
 1270x09, 0x04,         /*  Usage (Joystik),                        */
 1280xA1, 0x01,         /*  Collection (Application),               */
 1290xA1, 0x02,         /*      Collection (Logical),               */
 1300x95, 0x01,         /*          Report Count (1),               */
 1310x75, 0x0E,         /*          Report Size (14),               */
 1320x14,               /*          Logical Minimum (0),            */
 1330x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),        */
 1340x34,               /*          Physical Minimum (0),           */
 1350x46, 0xFF, 0x3F,   /*          Physical Maximum (16383),       */
 1360x09, 0x30,         /*          Usage (X),                      */
 1370x81, 0x02,         /*          Input (Variable),               */
 1380x95, 0x0E,         /*          Report Count (14),              */
 1390x75, 0x01,         /*          Report Size (1),                */
 1400x25, 0x01,         /*          Logical Maximum (1),            */
 1410x45, 0x01,         /*          Physical Maximum (1),           */
 1420x05, 0x09,         /*          Usage Page (Button),            */
 1430x19, 0x01,         /*          Usage Minimum (01h),            */
 1440x29, 0x0E,         /*          Usage Maximum (0Eh),            */
 1450x81, 0x02,         /*          Input (Variable),               */
 1460x05, 0x01,         /*          Usage Page (Desktop),           */
 1470x95, 0x01,         /*          Report Count (1),               */
 1480x75, 0x04,         /*          Report Size (4),                */
 1490x25, 0x07,         /*          Logical Maximum (7),            */
 1500x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
 1510x65, 0x14,         /*          Unit (Degrees),                 */
 1520x09, 0x39,         /*          Usage (Hat Switch),             */
 1530x81, 0x42,         /*          Input (Variable, Nullstate),    */
 1540x65, 0x00,         /*          Unit,                           */
 1550x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
 1560x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
 1570x75, 0x08,         /*          Report Size (8),                */
 1580x81, 0x01,         /*          Input (Constant),               */
 1590x09, 0x31,         /*          Usage (Y),                      */
 1600x81, 0x02,         /*          Input (Variable),               */
 1610x09, 0x35,         /*          Usage (Rz),                     */
 1620x81, 0x02,         /*          Input (Variable),               */
 1630x81, 0x01,         /*          Input (Constant),               */
 1640xC0,               /*      End Collection,                     */
 1650xA1, 0x02,         /*      Collection (Logical),               */
 1660x09, 0x02,         /*          Usage (02h),                    */
 1670x95, 0x07,         /*          Report Count (7),               */
 1680x91, 0x02,         /*          Output (Variable),              */
 1690xC0,               /*      End Collection,                     */
 1700xC0                /*  End Collection                          */
 171};
 172
 173static __u8 momo_rdesc_fixed[] = {
 1740x05, 0x01,         /*  Usage Page (Desktop),               */
 1750x09, 0x04,         /*  Usage (Joystik),                    */
 1760xA1, 0x01,         /*  Collection (Application),           */
 1770xA1, 0x02,         /*      Collection (Logical),           */
 1780x95, 0x01,         /*          Report Count (1),           */
 1790x75, 0x0A,         /*          Report Size (10),           */
 1800x15, 0x00,         /*          Logical Minimum (0),        */
 1810x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
 1820x35, 0x00,         /*          Physical Minimum (0),       */
 1830x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
 1840x09, 0x30,         /*          Usage (X),                  */
 1850x81, 0x02,         /*          Input (Variable),           */
 1860x95, 0x08,         /*          Report Count (8),           */
 1870x75, 0x01,         /*          Report Size (1),            */
 1880x25, 0x01,         /*          Logical Maximum (1),        */
 1890x45, 0x01,         /*          Physical Maximum (1),       */
 1900x05, 0x09,         /*          Usage Page (Button),        */
 1910x19, 0x01,         /*          Usage Minimum (01h),        */
 1920x29, 0x08,         /*          Usage Maximum (08h),        */
 1930x81, 0x02,         /*          Input (Variable),           */
 1940x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
 1950x75, 0x0E,         /*          Report Size (14),           */
 1960x95, 0x01,         /*          Report Count (1),           */
 1970x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
 1980x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
 1990x09, 0x00,         /*          Usage (00h),                */
 2000x81, 0x02,         /*          Input (Variable),           */
 2010x05, 0x01,         /*          Usage Page (Desktop),       */
 2020x75, 0x08,         /*          Report Size (8),            */
 2030x09, 0x31,         /*          Usage (Y),                  */
 2040x81, 0x02,         /*          Input (Variable),           */
 2050x09, 0x32,         /*          Usage (Z),                  */
 2060x81, 0x02,         /*          Input (Variable),           */
 2070x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
 2080x09, 0x01,         /*          Usage (01h),                */
 2090x81, 0x02,         /*          Input (Variable),           */
 2100xC0,               /*      End Collection,                 */
 2110xA1, 0x02,         /*      Collection (Logical),           */
 2120x09, 0x02,         /*          Usage (02h),                */
 2130x95, 0x07,         /*          Report Count (7),           */
 2140x91, 0x02,         /*          Output (Variable),          */
 2150xC0,               /*      End Collection,                 */
 2160xC0                /*  End Collection                      */
 217};
 218
 219/*
 220 * Certain Logitech keyboards send in report #3 keys which are far
 221 * above the logical maximum described in descriptor. This extends
 222 * the original value of 0x28c of logical maximum to 0x104d
 223 */
 224static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 225                unsigned int *rsize)
 226{
 227        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 228        struct usb_device_descriptor *udesc;
 229        __u16 bcdDevice, rev_maj, rev_min;
 230
 231        if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
 232                        rdesc[84] == 0x8c && rdesc[85] == 0x02) {
 233                hid_info(hdev,
 234                         "fixing up Logitech keyboard report descriptor\n");
 235                rdesc[84] = rdesc[89] = 0x4d;
 236                rdesc[85] = rdesc[90] = 0x10;
 237        }
 238        if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
 239                        rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
 240                        rdesc[49] == 0x81 && rdesc[50] == 0x06) {
 241                hid_info(hdev,
 242                         "fixing up rel/abs in Logitech report descriptor\n");
 243                rdesc[33] = rdesc[50] = 0x02;
 244        }
 245
 246        switch (hdev->product) {
 247
 248        /* Several wheels report as this id when operating in emulation mode. */
 249        case USB_DEVICE_ID_LOGITECH_WHEEL:
 250                udesc = &(hid_to_usb_dev(hdev)->descriptor);
 251                if (!udesc) {
 252                        hid_err(hdev, "NULL USB device descriptor\n");
 253                        break;
 254                }
 255                bcdDevice = le16_to_cpu(udesc->bcdDevice);
 256                rev_maj = bcdDevice >> 8;
 257                rev_min = bcdDevice & 0xff;
 258
 259                /* Update the report descriptor for only the Driving Force wheel */
 260                if (rev_maj == 1 && rev_min == 2 &&
 261                                *rsize == DF_RDESC_ORIG_SIZE) {
 262                        hid_info(hdev,
 263                                "fixing up Logitech Driving Force report descriptor\n");
 264                        rdesc = df_rdesc_fixed;
 265                        *rsize = sizeof(df_rdesc_fixed);
 266                }
 267                break;
 268
 269        case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
 270                if (*rsize == MOMO_RDESC_ORIG_SIZE) {
 271                        hid_info(hdev,
 272                                "fixing up Logitech Momo Force (Red) report descriptor\n");
 273                        rdesc = momo_rdesc_fixed;
 274                        *rsize = sizeof(momo_rdesc_fixed);
 275                }
 276                break;
 277
 278        case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
 279                if (*rsize == DFP_RDESC_ORIG_SIZE) {
 280                        hid_info(hdev,
 281                                "fixing up Logitech Driving Force Pro report descriptor\n");
 282                        rdesc = dfp_rdesc_fixed;
 283                        *rsize = sizeof(dfp_rdesc_fixed);
 284                }
 285                break;
 286
 287        case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
 288                if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
 289                                rdesc[47] == 0x05 && rdesc[48] == 0x09) {
 290                        hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
 291                        rdesc[41] = 0x05;
 292                        rdesc[42] = 0x09;
 293                        rdesc[47] = 0x95;
 294                        rdesc[48] = 0x0B;
 295                }
 296                break;
 297        }
 298
 299        return rdesc;
 300}
 301
 302#define lg_map_key_clear(c)     hid_map_usage_clear(hi, usage, bit, max, \
 303                EV_KEY, (c))
 304
 305static int lg_ultrax_remote_mapping(struct hid_input *hi,
 306                struct hid_usage *usage, unsigned long **bit, int *max)
 307{
 308        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
 309                return 0;
 310
 311        set_bit(EV_REP, hi->input->evbit);
 312        switch (usage->hid & HID_USAGE) {
 313        /* Reported on Logitech Ultra X Media Remote */
 314        case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
 315        case 0x00d: lg_map_key_clear(KEY_HOME);         break;
 316        case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
 317        case 0x025: lg_map_key_clear(KEY_TV);           break;
 318        case 0x026: lg_map_key_clear(KEY_MENU);         break;
 319        case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
 320        case 0x032: lg_map_key_clear(KEY_TEXT);         break;
 321        case 0x033: lg_map_key_clear(KEY_LAST);         break;
 322        case 0x047: lg_map_key_clear(KEY_MP3);          break;
 323        case 0x048: lg_map_key_clear(KEY_DVD);          break;
 324        case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
 325        case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
 326        case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
 327        case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
 328        case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
 329        case 0x051: lg_map_key_clear(KEY_RED);          break;
 330        case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
 331
 332        default:
 333                return 0;
 334        }
 335        return 1;
 336}
 337
 338static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
 339                unsigned long **bit, int *max)
 340{
 341        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
 342                return 0;
 343
 344        switch (usage->hid & HID_USAGE) {
 345
 346        case 0x00d: lg_map_key_clear(KEY_MEDIA);        break;
 347        default:
 348                return 0;
 349
 350        }
 351        return 1;
 352}
 353
 354static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
 355                unsigned long **bit, int *max)
 356{
 357        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
 358                return 0;
 359
 360        switch (usage->hid & HID_USAGE) {
 361        case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
 362        case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
 363        case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
 364        case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
 365        case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
 366        /* The following two entries are Playlist 1 and 2 on the MX3200 */
 367        case 0x100f: lg_map_key_clear(KEY_FN_1);                break;
 368        case 0x1010: lg_map_key_clear(KEY_FN_2);                break;
 369        case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
 370        case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
 371        case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
 372        case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
 373        case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
 374        case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
 375        case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
 376        case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
 377        case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
 378        case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
 379        case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
 380        case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 381        case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
 382        case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
 383        case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
 384        case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
 385        case 0x1027: lg_map_key_clear(KEY_MENU);                break;
 386        /* this one is marked as 'Rotate' */
 387        case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
 388        case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
 389        case 0x102a: lg_map_key_clear(KEY_BACK);                break;
 390        case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 391        case 0x102d: lg_map_key_clear(KEY_WWW);                 break;
 392        /* The following two are 'Start/answer call' and 'End/reject call'
 393           on the MX3200 */
 394        case 0x1031: lg_map_key_clear(KEY_OK);                  break;
 395        case 0x1032: lg_map_key_clear(KEY_CANCEL);              break;
 396        case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
 397        case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
 398        case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
 399        case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
 400        case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
 401        case 0x1046: lg_map_key_clear(KEY_REDO);                break;
 402        case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
 403        case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
 404        case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
 405        case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
 406        case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
 407        case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
 408
 409        default:
 410                return 0;
 411        }
 412        return 1;
 413}
 414
 415static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 416                struct hid_field *field, struct hid_usage *usage,
 417                unsigned long **bit, int *max)
 418{
 419        /* extended mapping for certain Logitech hardware (Logitech cordless
 420           desktop LX500) */
 421        static const u8 e_keymap[] = {
 422                  0,216,  0,213,175,156,  0,  0,  0,  0,
 423                144,  0,  0,  0,  0,  0,  0,  0,  0,212,
 424                174,167,152,161,112,  0,  0,  0,154,  0,
 425                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 426                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 427                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 428                  0,  0,  0,  0,  0,183,184,185,186,187,
 429                188,189,190,191,192,193,194,  0,  0,  0
 430        };
 431        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 432        unsigned int hid = usage->hid;
 433
 434        if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
 435                        lg_ultrax_remote_mapping(hi, usage, bit, max))
 436                return 1;
 437
 438        if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
 439                        lg_dinovo_mapping(hi, usage, bit, max))
 440                return 1;
 441
 442        if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
 443                return 1;
 444
 445        if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
 446                return 0;
 447
 448        hid &= HID_USAGE;
 449
 450        /* Special handling for Logitech Cordless Desktop */
 451        if (field->application == HID_GD_MOUSE) {
 452                if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
 453                                (hid == 7 || hid == 8))
 454                        return -1;
 455        } else {
 456                if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
 457                                hid < ARRAY_SIZE(e_keymap) &&
 458                                e_keymap[hid] != 0) {
 459                        hid_map_usage(hi, usage, bit, max, EV_KEY,
 460                                        e_keymap[hid]);
 461                        return 1;
 462                }
 463        }
 464
 465        return 0;
 466}
 467
 468static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 469                struct hid_field *field, struct hid_usage *usage,
 470                unsigned long **bit, int *max)
 471{
 472        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 473
 474        if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
 475                        (field->flags & HID_MAIN_ITEM_RELATIVE))
 476                field->flags &= ~HID_MAIN_ITEM_RELATIVE;
 477
 478        if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
 479                         usage->type == EV_REL || usage->type == EV_ABS))
 480                clear_bit(usage->code, *bit);
 481
 482        /* Ensure that Logitech wheels are not given a default fuzz/flat value */
 483        if (usage->type == EV_ABS && (usage->code == ABS_X ||
 484                        usage->code == ABS_Y || usage->code == ABS_Z ||
 485                        usage->code == ABS_RZ)) {
 486                switch (hdev->product) {
 487                case USB_DEVICE_ID_LOGITECH_WHEEL:
 488                case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
 489                case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
 490                case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
 491                case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
 492                case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
 493                case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
 494                case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
 495                        field->application = HID_GD_MULTIAXIS;
 496                        break;
 497                default:
 498                        break;
 499                }
 500        }
 501
 502        return 0;
 503}
 504
 505static int lg_event(struct hid_device *hdev, struct hid_field *field,
 506                struct hid_usage *usage, __s32 value)
 507{
 508        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 509
 510        if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
 511                input_event(field->hidinput->input, usage->type, usage->code,
 512                                -value);
 513                return 1;
 514        }
 515        if (drv_data->quirks & LG_FF4) {
 516                return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
 517        }
 518
 519        return 0;
 520}
 521
 522static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 523{
 524        unsigned int connect_mask = HID_CONNECT_DEFAULT;
 525        struct lg_drv_data *drv_data;
 526        int ret;
 527
 528        drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
 529        if (!drv_data) {
 530                hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
 531                return -ENOMEM;
 532        }
 533        drv_data->quirks = id->driver_data;
 534
 535        hid_set_drvdata(hdev, (void *)drv_data);
 536
 537        if (drv_data->quirks & LG_NOGET)
 538                hdev->quirks |= HID_QUIRK_NOGET;
 539
 540        ret = hid_parse(hdev);
 541        if (ret) {
 542                hid_err(hdev, "parse failed\n");
 543                goto err_free;
 544        }
 545
 546        if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
 547                connect_mask &= ~HID_CONNECT_FF;
 548
 549        ret = hid_hw_start(hdev, connect_mask);
 550        if (ret) {
 551                hid_err(hdev, "hw start failed\n");
 552                goto err_free;
 553        }
 554
 555        /* Setup wireless link with Logitech Wii wheel */
 556        if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
 557                unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 558
 559                ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 560
 561                if (ret >= 0) {
 562                        /* insert a little delay of 10 jiffies ~ 40ms */
 563                        wait_queue_head_t wait;
 564                        init_waitqueue_head (&wait);
 565                        wait_event_interruptible_timeout(wait, 0, 10);
 566
 567                        /* Select random Address */
 568                        buf[1] = 0xB2;
 569                        get_random_bytes(&buf[2], 2);
 570
 571                        ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 572                }
 573        }
 574
 575        if (drv_data->quirks & LG_FF)
 576                lgff_init(hdev);
 577        if (drv_data->quirks & LG_FF2)
 578                lg2ff_init(hdev);
 579        if (drv_data->quirks & LG_FF3)
 580                lg3ff_init(hdev);
 581        if (drv_data->quirks & LG_FF4)
 582                lg4ff_init(hdev);
 583
 584        return 0;
 585err_free:
 586        kfree(drv_data);
 587        return ret;
 588}
 589
 590static void lg_remove(struct hid_device *hdev)
 591{
 592        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 593        if (drv_data->quirks & LG_FF4)
 594                lg4ff_deinit(hdev);
 595
 596        hid_hw_stop(hdev);
 597        kfree(drv_data);
 598}
 599
 600static const struct hid_device_id lg_devices[] = {
 601        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
 602                .driver_data = LG_RDESC | LG_WIRELESS },
 603        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
 604                .driver_data = LG_RDESC | LG_WIRELESS },
 605        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
 606                .driver_data = LG_RDESC | LG_WIRELESS },
 607
 608        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
 609                .driver_data = LG_BAD_RELATIVE_KEYS },
 610
 611        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
 612                .driver_data = LG_DUPLICATE_USAGES },
 613        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
 614                .driver_data = LG_DUPLICATE_USAGES },
 615        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
 616                .driver_data = LG_DUPLICATE_USAGES },
 617
 618        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
 619                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 620        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
 621                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 622
 623        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
 624                .driver_data = LG_NOGET },
 625        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
 626                .driver_data = LG_NOGET | LG_FF4 },
 627
 628        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
 629                .driver_data = LG_FF2 },
 630        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
 631                .driver_data = LG_FF },
 632        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
 633                .driver_data = LG_FF },
 634        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
 635                .driver_data = LG_FF },
 636        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
 637                .driver_data = LG_FF },
 638        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
 639                .driver_data = LG_NOGET | LG_FF4 },
 640        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
 641                .driver_data = LG_FF4 },
 642        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
 643                .driver_data = LG_FF4 },
 644        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
 645                .driver_data = LG_FF4 },
 646        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
 647                .driver_data = LG_FF4 },
 648        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
 649                .driver_data = LG_NOGET | LG_FF4 },
 650        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
 651                .driver_data = LG_FF4 },
 652        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
 653                .driver_data = LG_FF },
 654        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
 655                .driver_data = LG_FF2 },
 656        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
 657                .driver_data = LG_FF3 },
 658        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
 659                .driver_data = LG_RDESC_REL_ABS },
 660        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
 661                .driver_data = LG_RDESC_REL_ABS },
 662        { }
 663};
 664
 665MODULE_DEVICE_TABLE(hid, lg_devices);
 666
 667static struct hid_driver lg_driver = {
 668        .name = "logitech",
 669        .id_table = lg_devices,
 670        .report_fixup = lg_report_fixup,
 671        .input_mapping = lg_input_mapping,
 672        .input_mapped = lg_input_mapped,
 673        .event = lg_event,
 674        .probe = lg_probe,
 675        .remove = lg_remove,
 676};
 677module_hid_driver(lg_driver);
 678
 679MODULE_LICENSE("GPL");
 680
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.