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/wait.h>
  25
  26#include "hid-ids.h"
  27#include "hid-lg.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 descriptor of the Driving Force Pro wheel */
  44#define DFP_RDESC_ORIG_SIZE     97
  45
  46/* Fixed report descriptor for Logitech Driving Force Pro wheel controller
  47 *
  48 * The original descriptor hides the separate throttle and brake axes in
  49 * a custom vendor usage page, providing only a combined value as
  50 * GenericDesktop.Y.
  51 * This descriptor removes the combined Y axis and instead reports
  52 * separate throttle (Y) and brake (RZ).
  53 */
  54static __u8 dfp_rdesc_fixed[] = {
  550x05, 0x01,         /*  Usage Page (Desktop),                   */
  560x09, 0x04,         /*  Usage (Joystik),                        */
  570xA1, 0x01,         /*  Collection (Application),               */
  580xA1, 0x02,         /*      Collection (Logical),               */
  590x95, 0x01,         /*          Report Count (1),               */
  600x75, 0x0E,         /*          Report Size (14),               */
  610x14,               /*          Logical Minimum (0),            */
  620x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),        */
  630x34,               /*          Physical Minimum (0),           */
  640x46, 0xFF, 0x3F,   /*          Physical Maximum (16383),       */
  650x09, 0x30,         /*          Usage (X),                      */
  660x81, 0x02,         /*          Input (Variable),               */
  670x95, 0x0E,         /*          Report Count (14),              */
  680x75, 0x01,         /*          Report Size (1),                */
  690x25, 0x01,         /*          Logical Maximum (1),            */
  700x45, 0x01,         /*          Physical Maximum (1),           */
  710x05, 0x09,         /*          Usage Page (Button),            */
  720x19, 0x01,         /*          Usage Minimum (01h),            */
  730x29, 0x0E,         /*          Usage Maximum (0Eh),            */
  740x81, 0x02,         /*          Input (Variable),               */
  750x05, 0x01,         /*          Usage Page (Desktop),           */
  760x95, 0x01,         /*          Report Count (1),               */
  770x75, 0x04,         /*          Report Size (4),                */
  780x25, 0x07,         /*          Logical Maximum (7),            */
  790x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
  800x65, 0x14,         /*          Unit (Degrees),                 */
  810x09, 0x39,         /*          Usage (Hat Switch),             */
  820x81, 0x42,         /*          Input (Variable, Nullstate),    */
  830x65, 0x00,         /*          Unit,                           */
  840x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
  850x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
  860x75, 0x08,         /*          Report Size (8),                */
  870x81, 0x01,         /*          Input (Constant),               */
  880x09, 0x31,         /*          Usage (Y),                      */
  890x81, 0x02,         /*          Input (Variable),               */
  900x09, 0x35,         /*          Usage (Rz),                     */
  910x81, 0x02,         /*          Input (Variable),               */
  920x81, 0x01,         /*          Input (Constant),               */
  930xC0,               /*      End Collection,                     */
  940xA1, 0x02,         /*      Collection (Logical),               */
  950x09, 0x02,         /*          Usage (02h),                    */
  960x95, 0x07,         /*          Report Count (7),               */
  970x91, 0x02,         /*          Output (Variable),              */
  980xC0,               /*      End Collection,                     */
  990xC0                /*  End Collection                          */
 100};
 101
 102
 103/*
 104 * Certain Logitech keyboards send in report #3 keys which are far
 105 * above the logical maximum described in descriptor. This extends
 106 * the original value of 0x28c of logical maximum to 0x104d
 107 */
 108static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 109                unsigned int *rsize)
 110{
 111        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 112
 113        if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
 114                        rdesc[84] == 0x8c && rdesc[85] == 0x02) {
 115                hid_info(hdev,
 116                         "fixing up Logitech keyboard report descriptor\n");
 117                rdesc[84] = rdesc[89] = 0x4d;
 118                rdesc[85] = rdesc[90] = 0x10;
 119        }
 120        if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
 121                        rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
 122                        rdesc[49] == 0x81 && rdesc[50] == 0x06) {
 123                hid_info(hdev,
 124                         "fixing up rel/abs in Logitech report descriptor\n");
 125                rdesc[33] = rdesc[50] = 0x02;
 126        }
 127        if ((drv_data->quirks & LG_FF4) && *rsize >= 101 &&
 128                        rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
 129                        rdesc[47] == 0x05 && rdesc[48] == 0x09) {
 130                hid_info(hdev, "fixing up Logitech Speed Force Wireless button descriptor\n");
 131                rdesc[41] = 0x05;
 132                rdesc[42] = 0x09;
 133                rdesc[47] = 0x95;
 134                rdesc[48] = 0x0B;
 135        }
 136
 137        switch (hdev->product) {
 138        case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
 139                if (*rsize == DFP_RDESC_ORIG_SIZE) {
 140                        hid_info(hdev,
 141                                "fixing up Logitech Driving Force Pro report descriptor\n");
 142                        rdesc = dfp_rdesc_fixed;
 143                        *rsize = sizeof(dfp_rdesc_fixed);
 144                }
 145                break;
 146        }
 147
 148        return rdesc;
 149}
 150
 151#define lg_map_key_clear(c)     hid_map_usage_clear(hi, usage, bit, max, \
 152                EV_KEY, (c))
 153
 154static int lg_ultrax_remote_mapping(struct hid_input *hi,
 155                struct hid_usage *usage, unsigned long **bit, int *max)
 156{
 157        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
 158                return 0;
 159
 160        set_bit(EV_REP, hi->input->evbit);
 161        switch (usage->hid & HID_USAGE) {
 162        /* Reported on Logitech Ultra X Media Remote */
 163        case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
 164        case 0x00d: lg_map_key_clear(KEY_HOME);         break;
 165        case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
 166        case 0x025: lg_map_key_clear(KEY_TV);           break;
 167        case 0x026: lg_map_key_clear(KEY_MENU);         break;
 168        case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
 169        case 0x032: lg_map_key_clear(KEY_TEXT);         break;
 170        case 0x033: lg_map_key_clear(KEY_LAST);         break;
 171        case 0x047: lg_map_key_clear(KEY_MP3);          break;
 172        case 0x048: lg_map_key_clear(KEY_DVD);          break;
 173        case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
 174        case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
 175        case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
 176        case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
 177        case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
 178        case 0x051: lg_map_key_clear(KEY_RED);          break;
 179        case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
 180
 181        default:
 182                return 0;
 183        }
 184        return 1;
 185}
 186
 187static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
 188                unsigned long **bit, int *max)
 189{
 190        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
 191                return 0;
 192
 193        switch (usage->hid & HID_USAGE) {
 194
 195        case 0x00d: lg_map_key_clear(KEY_MEDIA);        break;
 196        default:
 197                return 0;
 198
 199        }
 200        return 1;
 201}
 202
 203static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
 204                unsigned long **bit, int *max)
 205{
 206        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
 207                return 0;
 208
 209        switch (usage->hid & HID_USAGE) {
 210        case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
 211        case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
 212        case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
 213        case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
 214        case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
 215        /* The following two entries are Playlist 1 and 2 on the MX3200 */
 216        case 0x100f: lg_map_key_clear(KEY_FN_1);                break;
 217        case 0x1010: lg_map_key_clear(KEY_FN_2);                break;
 218        case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
 219        case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
 220        case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
 221        case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
 222        case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
 223        case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
 224        case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
 225        case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
 226        case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
 227        case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
 228        case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
 229        case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 230        case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
 231        case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
 232        case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
 233        case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
 234        case 0x1027: lg_map_key_clear(KEY_MENU);                break;
 235        /* this one is marked as 'Rotate' */
 236        case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
 237        case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
 238        case 0x102a: lg_map_key_clear(KEY_BACK);                break;
 239        case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
 240        case 0x102d: lg_map_key_clear(KEY_WWW);                 break;
 241        /* The following two are 'Start/answer call' and 'End/reject call'
 242           on the MX3200 */
 243        case 0x1031: lg_map_key_clear(KEY_OK);                  break;
 244        case 0x1032: lg_map_key_clear(KEY_CANCEL);              break;
 245        case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
 246        case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
 247        case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
 248        case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
 249        case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
 250        case 0x1046: lg_map_key_clear(KEY_REDO);                break;
 251        case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
 252        case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
 253        case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
 254        case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
 255        case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
 256        case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
 257
 258        default:
 259                return 0;
 260        }
 261        return 1;
 262}
 263
 264static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 265                struct hid_field *field, struct hid_usage *usage,
 266                unsigned long **bit, int *max)
 267{
 268        /* extended mapping for certain Logitech hardware (Logitech cordless
 269           desktop LX500) */
 270        static const u8 e_keymap[] = {
 271                  0,216,  0,213,175,156,  0,  0,  0,  0,
 272                144,  0,  0,  0,  0,  0,  0,  0,  0,212,
 273                174,167,152,161,112,  0,  0,  0,154,  0,
 274                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 275                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 276                  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 277                  0,  0,  0,  0,  0,183,184,185,186,187,
 278                188,189,190,191,192,193,194,  0,  0,  0
 279        };
 280        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 281        unsigned int hid = usage->hid;
 282
 283        if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
 284                        lg_ultrax_remote_mapping(hi, usage, bit, max))
 285                return 1;
 286
 287        if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
 288                        lg_dinovo_mapping(hi, usage, bit, max))
 289                return 1;
 290
 291        if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
 292                return 1;
 293
 294        if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
 295                return 0;
 296
 297        hid &= HID_USAGE;
 298
 299        /* Special handling for Logitech Cordless Desktop */
 300        if (field->application == HID_GD_MOUSE) {
 301                if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
 302                                (hid == 7 || hid == 8))
 303                        return -1;
 304        } else {
 305                if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
 306                                hid < ARRAY_SIZE(e_keymap) &&
 307                                e_keymap[hid] != 0) {
 308                        hid_map_usage(hi, usage, bit, max, EV_KEY,
 309                                        e_keymap[hid]);
 310                        return 1;
 311                }
 312        }
 313
 314        return 0;
 315}
 316
 317static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 318                struct hid_field *field, struct hid_usage *usage,
 319                unsigned long **bit, int *max)
 320{
 321        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 322
 323        if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
 324                        (field->flags & HID_MAIN_ITEM_RELATIVE))
 325                field->flags &= ~HID_MAIN_ITEM_RELATIVE;
 326
 327        if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
 328                         usage->type == EV_REL || usage->type == EV_ABS))
 329                clear_bit(usage->code, *bit);
 330
 331        return 0;
 332}
 333
 334static int lg_event(struct hid_device *hdev, struct hid_field *field,
 335                struct hid_usage *usage, __s32 value)
 336{
 337        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 338
 339        if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
 340                input_event(field->hidinput->input, usage->type, usage->code,
 341                                -value);
 342                return 1;
 343        }
 344        if (drv_data->quirks & LG_FF4) {
 345                return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
 346        }
 347
 348        return 0;
 349}
 350
 351static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 352{
 353        unsigned int connect_mask = HID_CONNECT_DEFAULT;
 354        struct lg_drv_data *drv_data;
 355        int ret;
 356
 357        drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
 358        if (!drv_data) {
 359                hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
 360                return -ENOMEM;
 361        }
 362        drv_data->quirks = id->driver_data;
 363
 364        hid_set_drvdata(hdev, (void *)drv_data);
 365
 366        if (drv_data->quirks & LG_NOGET)
 367                hdev->quirks |= HID_QUIRK_NOGET;
 368
 369        ret = hid_parse(hdev);
 370        if (ret) {
 371                hid_err(hdev, "parse failed\n");
 372                goto err_free;
 373        }
 374
 375        if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
 376                connect_mask &= ~HID_CONNECT_FF;
 377
 378        ret = hid_hw_start(hdev, connect_mask);
 379        if (ret) {
 380                hid_err(hdev, "hw start failed\n");
 381                goto err_free;
 382        }
 383
 384        /* Setup wireless link with Logitech Wii wheel */
 385        if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
 386                unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 387
 388                ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 389
 390                if (ret >= 0) {
 391                        /* insert a little delay of 10 jiffies ~ 40ms */
 392                        wait_queue_head_t wait;
 393                        init_waitqueue_head (&wait);
 394                        wait_event_interruptible_timeout(wait, 0, 10);
 395
 396                        /* Select random Address */
 397                        buf[1] = 0xB2;
 398                        get_random_bytes(&buf[2], 2);
 399
 400                        ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 401                }
 402        }
 403
 404        if (drv_data->quirks & LG_FF)
 405                lgff_init(hdev);
 406        if (drv_data->quirks & LG_FF2)
 407                lg2ff_init(hdev);
 408        if (drv_data->quirks & LG_FF3)
 409                lg3ff_init(hdev);
 410        if (drv_data->quirks & LG_FF4)
 411                lg4ff_init(hdev);
 412
 413        return 0;
 414err_free:
 415        kfree(drv_data);
 416        return ret;
 417}
 418
 419static void lg_remove(struct hid_device *hdev)
 420{
 421        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 422        if (drv_data->quirks & LG_FF4)
 423                lg4ff_deinit(hdev);
 424
 425        hid_hw_stop(hdev);
 426        kfree(drv_data);
 427}
 428
 429static const struct hid_device_id lg_devices[] = {
 430        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
 431                .driver_data = LG_RDESC | LG_WIRELESS },
 432        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
 433                .driver_data = LG_RDESC | LG_WIRELESS },
 434        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
 435                .driver_data = LG_RDESC | LG_WIRELESS },
 436
 437        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
 438                .driver_data = LG_BAD_RELATIVE_KEYS },
 439
 440        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
 441                .driver_data = LG_DUPLICATE_USAGES },
 442        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
 443                .driver_data = LG_DUPLICATE_USAGES },
 444        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
 445                .driver_data = LG_DUPLICATE_USAGES },
 446
 447        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
 448                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 449        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
 450                .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
 451
 452        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
 453                .driver_data = LG_NOGET },
 454        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
 455                .driver_data = LG_NOGET | LG_FF4 },
 456
 457        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
 458                .driver_data = LG_FF2 },
 459        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
 460                .driver_data = LG_FF },
 461        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
 462                .driver_data = LG_FF },
 463        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
 464                .driver_data = LG_FF },
 465        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
 466                .driver_data = LG_FF },
 467        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
 468                .driver_data = LG_FF4 },
 469        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
 470                .driver_data = LG_FF4 },
 471        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
 472                .driver_data = LG_FF4 },
 473        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
 474                .driver_data = LG_FF4 },
 475        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
 476                .driver_data = LG_FF4 },
 477        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
 478                .driver_data = LG_NOGET | LG_FF4 },
 479        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
 480                .driver_data = LG_FF4 },
 481        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
 482                .driver_data = LG_FF },
 483        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
 484                .driver_data = LG_FF2 },
 485        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
 486                .driver_data = LG_FF3 },
 487        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
 488                .driver_data = LG_RDESC_REL_ABS },
 489        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
 490                .driver_data = LG_RDESC_REL_ABS },
 491        { }
 492};
 493
 494MODULE_DEVICE_TABLE(hid, lg_devices);
 495
 496static struct hid_driver lg_driver = {
 497        .name = "logitech",
 498        .id_table = lg_devices,
 499        .report_fixup = lg_report_fixup,
 500        .input_mapping = lg_input_mapping,
 501        .input_mapped = lg_input_mapped,
 502        .event = lg_event,
 503        .probe = lg_probe,
 504        .remove = lg_remove,
 505};
 506
 507static int __init lg_init(void)
 508{
 509        return hid_register_driver(&lg_driver);
 510}
 511
 512static void __exit lg_exit(void)
 513{
 514        hid_unregister_driver(&lg_driver);
 515}
 516
 517module_init(lg_init);
 518module_exit(lg_exit);
 519MODULE_LICENSE("GPL");
 520
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.