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