Root/drivers/hid/hid-lg.c

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

Archive Download this file



interactive