Version:  2.0.40 2.2.26 2.4.37 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18

Linux/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) 2008 Jiri Slaby
  9  *  Copyright (c) 2010 Hendrik Iben
 10  */
 11 
 12 /*
 13  * This program is free software; you can redistribute it and/or modify it
 14  * under the terms of the GNU General Public License as published by the Free
 15  * Software Foundation; either version 2 of the License, or (at your option)
 16  * any later version.
 17  */
 18 
 19 #include <linux/device.h>
 20 #include <linux/hid.h>
 21 #include <linux/module.h>
 22 #include <linux/random.h>
 23 #include <linux/sched.h>
 24 #include <linux/usb.h>
 25 #include <linux/wait.h>
 26 
 27 #include "usbhid/usbhid.h"
 28 #include "hid-ids.h"
 29 #include "hid-lg.h"
 30 
 31 #define LG_RDESC                0x001
 32 #define LG_BAD_RELATIVE_KEYS    0x002
 33 #define LG_DUPLICATE_USAGES     0x004
 34 #define LG_EXPANDED_KEYMAP      0x010
 35 #define LG_IGNORE_DOUBLED_WHEEL 0x020
 36 #define LG_WIRELESS             0x040
 37 #define LG_INVERT_HWHEEL        0x080
 38 #define LG_NOGET                0x100
 39 #define LG_FF                   0x200
 40 #define LG_FF2                  0x400
 41 #define LG_RDESC_REL_ABS        0x800
 42 #define LG_FF3                  0x1000
 43 #define LG_FF4                  0x2000
 44 
 45 /* Size of the original descriptors of the Driving Force (and Pro) wheels */
 46 #define DF_RDESC_ORIG_SIZE      130
 47 #define DFP_RDESC_ORIG_SIZE     97
 48 #define FV_RDESC_ORIG_SIZE      130
 49 #define MOMO_RDESC_ORIG_SIZE    87
 50 #define MOMO2_RDESC_ORIG_SIZE   87
 51 
 52 /* Fixed report descriptors for Logitech Driving Force (and Pro)
 53  * wheel controllers
 54  *
 55  * The original descriptors hide the separate throttle and brake axes in
 56  * a custom vendor usage page, providing only a combined value as
 57  * GenericDesktop.Y.
 58  * These descriptors remove the combined Y axis and instead report
 59  * separate throttle (Y) and brake (RZ).
 60  */
 61 static __u8 df_rdesc_fixed[] = {
 62 0x05, 0x01,         /*  Usage Page (Desktop),                   */
 63 0x09, 0x04,         /*  Usage (Joystik),                        */
 64 0xA1, 0x01,         /*  Collection (Application),               */
 65 0xA1, 0x02,         /*      Collection (Logical),               */
 66 0x95, 0x01,         /*          Report Count (1),               */
 67 0x75, 0x0A,         /*          Report Size (10),               */
 68 0x14,               /*          Logical Minimum (0),            */
 69 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
 70 0x34,               /*          Physical Minimum (0),           */
 71 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
 72 0x09, 0x30,         /*          Usage (X),                      */
 73 0x81, 0x02,         /*          Input (Variable),               */
 74 0x95, 0x0C,         /*          Report Count (12),              */
 75 0x75, 0x01,         /*          Report Size (1),                */
 76 0x25, 0x01,         /*          Logical Maximum (1),            */
 77 0x45, 0x01,         /*          Physical Maximum (1),           */
 78 0x05, 0x09,         /*          Usage (Buttons),                */
 79 0x19, 0x01,         /*          Usage Minimum (1),              */
 80 0x29, 0x0c,         /*          Usage Maximum (12),             */
 81 0x81, 0x02,         /*          Input (Variable),               */
 82 0x95, 0x02,         /*          Report Count (2),               */
 83 0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
 84 0x09, 0x01,         /*          Usage (?: 1),                   */
 85 0x81, 0x02,         /*          Input (Variable),               */
 86 0x05, 0x01,         /*          Usage Page (Desktop),           */
 87 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
 88 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
 89 0x95, 0x01,         /*          Report Count (1),               */
 90 0x75, 0x08,         /*          Report Size (8),                */
 91 0x81, 0x02,         /*          Input (Variable),               */
 92 0x25, 0x07,         /*          Logical Maximum (7),            */
 93 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
 94 0x75, 0x04,         /*          Report Size (4),                */
 95 0x65, 0x14,         /*          Unit (Degrees),                 */
 96 0x09, 0x39,         /*          Usage (Hat Switch),             */
 97 0x81, 0x42,         /*          Input (Variable, Null State),   */
 98 0x75, 0x01,         /*          Report Size (1),                */
 99 0x95, 0x04,         /*          Report Count (4),               */
100 0x65, 0x00,         /*          Unit (none),                    */
101 0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
102 0x09, 0x01,         /*          Usage (?: 1),                   */
103 0x25, 0x01,         /*          Logical Maximum (1),            */
104 0x45, 0x01,         /*          Physical Maximum (1),           */
105 0x81, 0x02,         /*          Input (Variable),               */
106 0x05, 0x01,         /*          Usage Page (Desktop),           */
107 0x95, 0x01,         /*          Report Count (1),               */
108 0x75, 0x08,         /*          Report Size (8),                */
109 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
110 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
111 0x09, 0x31,         /*          Usage (Y),                      */
112 0x81, 0x02,         /*          Input (Variable),               */
113 0x09, 0x35,         /*          Usage (Rz),                     */
114 0x81, 0x02,         /*          Input (Variable),               */
115 0xC0,               /*      End Collection,                     */
116 0xA1, 0x02,         /*      Collection (Logical),               */
117 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
118 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
119 0x95, 0x07,         /*          Report Count (7),               */
120 0x75, 0x08,         /*          Report Size (8),                */
121 0x09, 0x03,         /*          Usage (?: 3),                   */
122 0x91, 0x02,         /*          Output (Variable),              */
123 0xC0,               /*      End Collection,                     */
124 0xC0                /*  End Collection                          */
125 };
126 
127 static __u8 dfp_rdesc_fixed[] = {
128 0x05, 0x01,         /*  Usage Page (Desktop),                   */
129 0x09, 0x04,         /*  Usage (Joystik),                        */
130 0xA1, 0x01,         /*  Collection (Application),               */
131 0xA1, 0x02,         /*      Collection (Logical),               */
132 0x95, 0x01,         /*          Report Count (1),               */
133 0x75, 0x0E,         /*          Report Size (14),               */
134 0x14,               /*          Logical Minimum (0),            */
135 0x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),        */
136 0x34,               /*          Physical Minimum (0),           */
137 0x46, 0xFF, 0x3F,   /*          Physical Maximum (16383),       */
138 0x09, 0x30,         /*          Usage (X),                      */
139 0x81, 0x02,         /*          Input (Variable),               */
140 0x95, 0x0E,         /*          Report Count (14),              */
141 0x75, 0x01,         /*          Report Size (1),                */
142 0x25, 0x01,         /*          Logical Maximum (1),            */
143 0x45, 0x01,         /*          Physical Maximum (1),           */
144 0x05, 0x09,         /*          Usage Page (Button),            */
145 0x19, 0x01,         /*          Usage Minimum (01h),            */
146 0x29, 0x0E,         /*          Usage Maximum (0Eh),            */
147 0x81, 0x02,         /*          Input (Variable),               */
148 0x05, 0x01,         /*          Usage Page (Desktop),           */
149 0x95, 0x01,         /*          Report Count (1),               */
150 0x75, 0x04,         /*          Report Size (4),                */
151 0x25, 0x07,         /*          Logical Maximum (7),            */
152 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
153 0x65, 0x14,         /*          Unit (Degrees),                 */
154 0x09, 0x39,         /*          Usage (Hat Switch),             */
155 0x81, 0x42,         /*          Input (Variable, Nullstate),    */
156 0x65, 0x00,         /*          Unit,                           */
157 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
158 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
159 0x75, 0x08,         /*          Report Size (8),                */
160 0x81, 0x01,         /*          Input (Constant),               */
161 0x09, 0x31,         /*          Usage (Y),                      */
162 0x81, 0x02,         /*          Input (Variable),               */
163 0x09, 0x35,         /*          Usage (Rz),                     */
164 0x81, 0x02,         /*          Input (Variable),               */
165 0x81, 0x01,         /*          Input (Constant),               */
166 0xC0,               /*      End Collection,                     */
167 0xA1, 0x02,         /*      Collection (Logical),               */
168 0x09, 0x02,         /*          Usage (02h),                    */
169 0x95, 0x07,         /*          Report Count (7),               */
170 0x91, 0x02,         /*          Output (Variable),              */
171 0xC0,               /*      End Collection,                     */
172 0xC0                /*  End Collection                          */
173 };
174 
175 static __u8 fv_rdesc_fixed[] = {
176 0x05, 0x01,         /*  Usage Page (Desktop),                   */
177 0x09, 0x04,         /*  Usage (Joystik),                        */
178 0xA1, 0x01,         /*  Collection (Application),               */
179 0xA1, 0x02,         /*      Collection (Logical),               */
180 0x95, 0x01,         /*          Report Count (1),               */
181 0x75, 0x0A,         /*          Report Size (10),               */
182 0x15, 0x00,         /*          Logical Minimum (0),            */
183 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
184 0x35, 0x00,         /*          Physical Minimum (0),           */
185 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
186 0x09, 0x30,         /*          Usage (X),                      */
187 0x81, 0x02,         /*          Input (Variable),               */
188 0x95, 0x0C,         /*          Report Count (12),              */
189 0x75, 0x01,         /*          Report Size (1),                */
190 0x25, 0x01,         /*          Logical Maximum (1),            */
191 0x45, 0x01,         /*          Physical Maximum (1),           */
192 0x05, 0x09,         /*          Usage Page (Button),            */
193 0x19, 0x01,         /*          Usage Minimum (01h),            */
194 0x29, 0x0C,         /*          Usage Maximum (0Ch),            */
195 0x81, 0x02,         /*          Input (Variable),               */
196 0x95, 0x02,         /*          Report Count (2),               */
197 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
198 0x09, 0x01,         /*          Usage (01h),                    */
199 0x81, 0x02,         /*          Input (Variable),               */
200 0x09, 0x02,         /*          Usage (02h),                    */
201 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
202 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
203 0x95, 0x01,         /*          Report Count (1),               */
204 0x75, 0x08,         /*          Report Size (8),                */
205 0x81, 0x02,         /*          Input (Variable),               */
206 0x05, 0x01,         /*          Usage Page (Desktop),           */
207 0x25, 0x07,         /*          Logical Maximum (7),            */
208 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
209 0x75, 0x04,         /*          Report Size (4),                */
210 0x65, 0x14,         /*          Unit (Degrees),                 */
211 0x09, 0x39,         /*          Usage (Hat Switch),             */
212 0x81, 0x42,         /*          Input (Variable, Null State),   */
213 0x75, 0x01,         /*          Report Size (1),                */
214 0x95, 0x04,         /*          Report Count (4),               */
215 0x65, 0x00,         /*          Unit,                           */
216 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
217 0x09, 0x01,         /*          Usage (01h),                    */
218 0x25, 0x01,         /*          Logical Maximum (1),            */
219 0x45, 0x01,         /*          Physical Maximum (1),           */
220 0x81, 0x02,         /*          Input (Variable),               */
221 0x05, 0x01,         /*          Usage Page (Desktop),           */
222 0x95, 0x01,         /*          Report Count (1),               */
223 0x75, 0x08,         /*          Report Size (8),                */
224 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
225 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
226 0x09, 0x31,         /*          Usage (Y),                      */
227 0x81, 0x02,         /*          Input (Variable),               */
228 0x09, 0x32,         /*          Usage (Z),                      */
229 0x81, 0x02,         /*          Input (Variable),               */
230 0xC0,               /*      End Collection,                     */
231 0xA1, 0x02,         /*      Collection (Logical),               */
232 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
233 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
234 0x95, 0x07,         /*          Report Count (7),               */
235 0x75, 0x08,         /*          Report Size (8),                */
236 0x09, 0x03,         /*          Usage (03h),                    */
237 0x91, 0x02,         /*          Output (Variable),              */
238 0xC0,               /*      End Collection,                     */
239 0xC0                /*  End Collection                          */
240 };
241 
242 static __u8 momo_rdesc_fixed[] = {
243 0x05, 0x01,         /*  Usage Page (Desktop),               */
244 0x09, 0x04,         /*  Usage (Joystik),                    */
245 0xA1, 0x01,         /*  Collection (Application),           */
246 0xA1, 0x02,         /*      Collection (Logical),           */
247 0x95, 0x01,         /*          Report Count (1),           */
248 0x75, 0x0A,         /*          Report Size (10),           */
249 0x15, 0x00,         /*          Logical Minimum (0),        */
250 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
251 0x35, 0x00,         /*          Physical Minimum (0),       */
252 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
253 0x09, 0x30,         /*          Usage (X),                  */
254 0x81, 0x02,         /*          Input (Variable),           */
255 0x95, 0x08,         /*          Report Count (8),           */
256 0x75, 0x01,         /*          Report Size (1),            */
257 0x25, 0x01,         /*          Logical Maximum (1),        */
258 0x45, 0x01,         /*          Physical Maximum (1),       */
259 0x05, 0x09,         /*          Usage Page (Button),        */
260 0x19, 0x01,         /*          Usage Minimum (01h),        */
261 0x29, 0x08,         /*          Usage Maximum (08h),        */
262 0x81, 0x02,         /*          Input (Variable),           */
263 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
264 0x75, 0x0E,         /*          Report Size (14),           */
265 0x95, 0x01,         /*          Report Count (1),           */
266 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
267 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
268 0x09, 0x00,         /*          Usage (00h),                */
269 0x81, 0x02,         /*          Input (Variable),           */
270 0x05, 0x01,         /*          Usage Page (Desktop),       */
271 0x75, 0x08,         /*          Report Size (8),            */
272 0x09, 0x31,         /*          Usage (Y),                  */
273 0x81, 0x02,         /*          Input (Variable),           */
274 0x09, 0x32,         /*          Usage (Z),                  */
275 0x81, 0x02,         /*          Input (Variable),           */
276 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
277 0x09, 0x01,         /*          Usage (01h),                */
278 0x81, 0x02,         /*          Input (Variable),           */
279 0xC0,               /*      End Collection,                 */
280 0xA1, 0x02,         /*      Collection (Logical),           */
281 0x09, 0x02,         /*          Usage (02h),                */
282 0x95, 0x07,         /*          Report Count (7),           */
283 0x91, 0x02,         /*          Output (Variable),          */
284 0xC0,               /*      End Collection,                 */
285 0xC0                /*  End Collection                      */
286 };
287 
288 static __u8 momo2_rdesc_fixed[] = {
289 0x05, 0x01,         /*  Usage Page (Desktop),               */
290 0x09, 0x04,         /*  Usage (Joystik),                    */
291 0xA1, 0x01,         /*  Collection (Application),           */
292 0xA1, 0x02,         /*      Collection (Logical),           */
293 0x95, 0x01,         /*          Report Count (1),           */
294 0x75, 0x0A,         /*          Report Size (10),           */
295 0x15, 0x00,         /*          Logical Minimum (0),        */
296 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
297 0x35, 0x00,         /*          Physical Minimum (0),       */
298 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
299 0x09, 0x30,         /*          Usage (X),                  */
300 0x81, 0x02,         /*          Input (Variable),           */
301 0x95, 0x0A,         /*          Report Count (10),          */
302 0x75, 0x01,         /*          Report Size (1),            */
303 0x25, 0x01,         /*          Logical Maximum (1),        */
304 0x45, 0x01,         /*          Physical Maximum (1),       */
305 0x05, 0x09,         /*          Usage Page (Button),        */
306 0x19, 0x01,         /*          Usage Minimum (01h),        */
307 0x29, 0x0A,         /*          Usage Maximum (0Ah),        */
308 0x81, 0x02,         /*          Input (Variable),           */
309 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
310 0x09, 0x00,         /*          Usage (00h),                */
311 0x95, 0x04,         /*          Report Count (4),           */
312 0x81, 0x02,         /*          Input (Variable),           */
313 0x95, 0x01,         /*          Report Count (1),           */
314 0x75, 0x08,         /*          Report Size (8),            */
315 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
316 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
317 0x09, 0x01,         /*          Usage (01h),                */
318 0x81, 0x02,         /*          Input (Variable),           */
319 0x05, 0x01,         /*          Usage Page (Desktop),       */
320 0x09, 0x31,         /*          Usage (Y),                  */
321 0x81, 0x02,         /*          Input (Variable),           */
322 0x09, 0x32,         /*          Usage (Z),                  */
323 0x81, 0x02,         /*          Input (Variable),           */
324 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
325 0x09, 0x00,         /*          Usage (00h),                */
326 0x81, 0x02,         /*          Input (Variable),           */
327 0xC0,               /*      End Collection,                 */
328 0xA1, 0x02,         /*      Collection (Logical),           */
329 0x09, 0x02,         /*          Usage (02h),                */
330 0x95, 0x07,         /*          Report Count (7),           */
331 0x91, 0x02,         /*          Output (Variable),          */
332 0xC0,               /*      End Collection,                 */
333 0xC0                /*  End Collection                      */
334 };
335 
336 /*
337  * Certain Logitech keyboards send in report #3 keys which are far
338  * above the logical maximum described in descriptor. This extends
339  * the original value of 0x28c of logical maximum to 0x104d
340  */
341 static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
342                 unsigned int *rsize)
343 {
344         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
345         struct usb_device_descriptor *udesc;
346         __u16 bcdDevice, rev_maj, rev_min;
347 
348         if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
349                         rdesc[84] == 0x8c && rdesc[85] == 0x02) {
350                 hid_info(hdev,
351                          "fixing up Logitech keyboard report descriptor\n");
352                 rdesc[84] = rdesc[89] = 0x4d;
353                 rdesc[85] = rdesc[90] = 0x10;
354         }
355         if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
356                         rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
357                         rdesc[49] == 0x81 && rdesc[50] == 0x06) {
358                 hid_info(hdev,
359                          "fixing up rel/abs in Logitech report descriptor\n");
360                 rdesc[33] = rdesc[50] = 0x02;
361         }
362 
363         switch (hdev->product) {
364 
365         /* Several wheels report as this id when operating in emulation mode. */
366         case USB_DEVICE_ID_LOGITECH_WHEEL:
367                 udesc = &(hid_to_usb_dev(hdev)->descriptor);
368                 if (!udesc) {
369                         hid_err(hdev, "NULL USB device descriptor\n");
370                         break;
371                 }
372                 bcdDevice = le16_to_cpu(udesc->bcdDevice);
373                 rev_maj = bcdDevice >> 8;
374                 rev_min = bcdDevice & 0xff;
375 
376                 /* Update the report descriptor for only the Driving Force wheel */
377                 if (rev_maj == 1 && rev_min == 2 &&
378                                 *rsize == DF_RDESC_ORIG_SIZE) {
379                         hid_info(hdev,
380                                 "fixing up Logitech Driving Force report descriptor\n");
381                         rdesc = df_rdesc_fixed;
382                         *rsize = sizeof(df_rdesc_fixed);
383                 }
384                 break;
385 
386         case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
387                 if (*rsize == MOMO_RDESC_ORIG_SIZE) {
388                         hid_info(hdev,
389                                 "fixing up Logitech Momo Force (Red) report descriptor\n");
390                         rdesc = momo_rdesc_fixed;
391                         *rsize = sizeof(momo_rdesc_fixed);
392                 }
393                 break;
394 
395         case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
396                 if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
397                         hid_info(hdev,
398                                 "fixing up Logitech Momo Racing Force (Black) report descriptor\n");
399                         rdesc = momo2_rdesc_fixed;
400                         *rsize = sizeof(momo2_rdesc_fixed);
401                 }
402                 break;
403 
404         case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
405                 if (*rsize == FV_RDESC_ORIG_SIZE) {
406                         hid_info(hdev,
407                                 "fixing up Logitech Formula Vibration report descriptor\n");
408                         rdesc = fv_rdesc_fixed;
409                         *rsize = sizeof(fv_rdesc_fixed);
410                 }
411                 break;
412 
413         case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
414                 if (*rsize == DFP_RDESC_ORIG_SIZE) {
415                         hid_info(hdev,
416                                 "fixing up Logitech Driving Force Pro report descriptor\n");
417                         rdesc = dfp_rdesc_fixed;
418                         *rsize = sizeof(dfp_rdesc_fixed);
419                 }
420                 break;
421 
422         case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
423                 if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
424                                 rdesc[47] == 0x05 && rdesc[48] == 0x09) {
425                         hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
426                         rdesc[41] = 0x05;
427                         rdesc[42] = 0x09;
428                         rdesc[47] = 0x95;
429                         rdesc[48] = 0x0B;
430                 }
431                 break;
432         }
433 
434         return rdesc;
435 }
436 
437 #define lg_map_key_clear(c)     hid_map_usage_clear(hi, usage, bit, max, \
438                 EV_KEY, (c))
439 
440 static int lg_ultrax_remote_mapping(struct hid_input *hi,
441                 struct hid_usage *usage, unsigned long **bit, int *max)
442 {
443         if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
444                 return 0;
445 
446         set_bit(EV_REP, hi->input->evbit);
447         switch (usage->hid & HID_USAGE) {
448         /* Reported on Logitech Ultra X Media Remote */
449         case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
450         case 0x00d: lg_map_key_clear(KEY_HOME);         break;
451         case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
452         case 0x025: lg_map_key_clear(KEY_TV);           break;
453         case 0x026: lg_map_key_clear(KEY_MENU);         break;
454         case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
455         case 0x032: lg_map_key_clear(KEY_TEXT);         break;
456         case 0x033: lg_map_key_clear(KEY_LAST);         break;
457         case 0x047: lg_map_key_clear(KEY_MP3);          break;
458         case 0x048: lg_map_key_clear(KEY_DVD);          break;
459         case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
460         case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
461         case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
462         case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
463         case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
464         case 0x051: lg_map_key_clear(KEY_RED);          break;
465         case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
466 
467         default:
468                 return 0;
469         }
470         return 1;
471 }
472 
473 static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
474                 unsigned long **bit, int *max)
475 {
476         if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
477                 return 0;
478 
479         switch (usage->hid & HID_USAGE) {
480 
481         case 0x00d: lg_map_key_clear(KEY_MEDIA);        break;
482         default:
483                 return 0;
484 
485         }
486         return 1;
487 }
488 
489 static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
490                 unsigned long **bit, int *max)
491 {
492         if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
493                 return 0;
494 
495         switch (usage->hid & HID_USAGE) {
496         case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
497         case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
498         case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
499         case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
500         case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
501         /* The following two entries are Playlist 1 and 2 on the MX3200 */
502         case 0x100f: lg_map_key_clear(KEY_FN_1);                break;
503         case 0x1010: lg_map_key_clear(KEY_FN_2);                break;
504         case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
505         case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
506         case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
507         case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
508         case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
509         case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
510         case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
511         case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
512         case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
513         case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
514         case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
515         case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
516         case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
517         case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
518         case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
519         case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
520         case 0x1027: lg_map_key_clear(KEY_MENU);                break;
521         /* this one is marked as 'Rotate' */
522         case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
523         case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
524         case 0x102a: lg_map_key_clear(KEY_BACK);                break;
525         case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
526         case 0x102d: lg_map_key_clear(KEY_WWW);                 break;
527         /* The following two are 'Start/answer call' and 'End/reject call'
528            on the MX3200 */
529         case 0x1031: lg_map_key_clear(KEY_OK);                  break;
530         case 0x1032: lg_map_key_clear(KEY_CANCEL);              break;
531         case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
532         case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
533         case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
534         case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
535         case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
536         case 0x1046: lg_map_key_clear(KEY_REDO);                break;
537         case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
538         case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
539         case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
540         case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
541         case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
542         case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
543 
544         default:
545                 return 0;
546         }
547         return 1;
548 }
549 
550 static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
551                 struct hid_field *field, struct hid_usage *usage,
552                 unsigned long **bit, int *max)
553 {
554         /* extended mapping for certain Logitech hardware (Logitech cordless
555            desktop LX500) */
556         static const u8 e_keymap[] = {
557                   0,216,  0,213,175,156,  0,  0,  0,  0,
558                 144,  0,  0,  0,  0,  0,  0,  0,  0,212,
559                 174,167,152,161,112,  0,  0,  0,154,  0,
560                   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
561                   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
562                   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
563                   0,  0,  0,  0,  0,183,184,185,186,187,
564                 188,189,190,191,192,193,194,  0,  0,  0
565         };
566         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
567         unsigned int hid = usage->hid;
568 
569         if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
570                         lg_ultrax_remote_mapping(hi, usage, bit, max))
571                 return 1;
572 
573         if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
574                         lg_dinovo_mapping(hi, usage, bit, max))
575                 return 1;
576 
577         if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
578                 return 1;
579 
580         if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
581                 return 0;
582 
583         hid &= HID_USAGE;
584 
585         /* Special handling for Logitech Cordless Desktop */
586         if (field->application == HID_GD_MOUSE) {
587                 if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
588                                 (hid == 7 || hid == 8))
589                         return -1;
590         } else {
591                 if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
592                                 hid < ARRAY_SIZE(e_keymap) &&
593                                 e_keymap[hid] != 0) {
594                         hid_map_usage(hi, usage, bit, max, EV_KEY,
595                                         e_keymap[hid]);
596                         return 1;
597                 }
598         }
599 
600         return 0;
601 }
602 
603 static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
604                 struct hid_field *field, struct hid_usage *usage,
605                 unsigned long **bit, int *max)
606 {
607         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
608 
609         if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
610                         (field->flags & HID_MAIN_ITEM_RELATIVE))
611                 field->flags &= ~HID_MAIN_ITEM_RELATIVE;
612 
613         if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
614                          usage->type == EV_REL || usage->type == EV_ABS))
615                 clear_bit(usage->code, *bit);
616 
617         /* Ensure that Logitech wheels are not given a default fuzz/flat value */
618         if (usage->type == EV_ABS && (usage->code == ABS_X ||
619                         usage->code == ABS_Y || usage->code == ABS_Z ||
620                         usage->code == ABS_RZ)) {
621                 switch (hdev->product) {
622                 case USB_DEVICE_ID_LOGITECH_WHEEL:
623                 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
624                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
625                 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
626                 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
627                 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
628                 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
629                 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
630                 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
631                         field->application = HID_GD_MULTIAXIS;
632                         break;
633                 default:
634                         break;
635                 }
636         }
637 
638         return 0;
639 }
640 
641 static int lg_event(struct hid_device *hdev, struct hid_field *field,
642                 struct hid_usage *usage, __s32 value)
643 {
644         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
645 
646         if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
647                 input_event(field->hidinput->input, usage->type, usage->code,
648                                 -value);
649                 return 1;
650         }
651         if (drv_data->quirks & LG_FF4) {
652                 return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
653         }
654 
655         return 0;
656 }
657 
658 static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
659 {
660         unsigned int connect_mask = HID_CONNECT_DEFAULT;
661         struct lg_drv_data *drv_data;
662         int ret;
663 
664         drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
665         if (!drv_data) {
666                 hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
667                 return -ENOMEM;
668         }
669         drv_data->quirks = id->driver_data;
670 
671         hid_set_drvdata(hdev, (void *)drv_data);
672 
673         if (drv_data->quirks & LG_NOGET)
674                 hdev->quirks |= HID_QUIRK_NOGET;
675 
676         ret = hid_parse(hdev);
677         if (ret) {
678                 hid_err(hdev, "parse failed\n");
679                 goto err_free;
680         }
681 
682         if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
683                 connect_mask &= ~HID_CONNECT_FF;
684 
685         ret = hid_hw_start(hdev, connect_mask);
686         if (ret) {
687                 hid_err(hdev, "hw start failed\n");
688                 goto err_free;
689         }
690 
691         /* Setup wireless link with Logitech Wii wheel */
692         if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
693                 unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
694 
695                 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
696                                         HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
697 
698                 if (ret >= 0) {
699                         /* insert a little delay of 10 jiffies ~ 40ms */
700                         wait_queue_head_t wait;
701                         init_waitqueue_head (&wait);
702                         wait_event_interruptible_timeout(wait, 0, 10);
703 
704                         /* Select random Address */
705                         buf[1] = 0xB2;
706                         get_random_bytes(&buf[2], 2);
707 
708                         ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
709                                         HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
710                 }
711         }
712 
713         if (drv_data->quirks & LG_FF)
714                 lgff_init(hdev);
715         if (drv_data->quirks & LG_FF2)
716                 lg2ff_init(hdev);
717         if (drv_data->quirks & LG_FF3)
718                 lg3ff_init(hdev);
719         if (drv_data->quirks & LG_FF4)
720                 lg4ff_init(hdev);
721 
722         return 0;
723 err_free:
724         kfree(drv_data);
725         return ret;
726 }
727 
728 static void lg_remove(struct hid_device *hdev)
729 {
730         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
731         if (drv_data->quirks & LG_FF4)
732                 lg4ff_deinit(hdev);
733 
734         hid_hw_stop(hdev);
735         kfree(drv_data);
736 }
737 
738 static const struct hid_device_id lg_devices[] = {
739         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
740                 .driver_data = LG_RDESC | LG_WIRELESS },
741         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
742                 .driver_data = LG_RDESC | LG_WIRELESS },
743         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
744                 .driver_data = LG_RDESC | LG_WIRELESS },
745 
746         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
747                 .driver_data = LG_BAD_RELATIVE_KEYS },
748 
749         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
750                 .driver_data = LG_DUPLICATE_USAGES },
751         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
752                 .driver_data = LG_DUPLICATE_USAGES },
753         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
754                 .driver_data = LG_DUPLICATE_USAGES },
755 
756         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
757                 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
758         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
759                 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
760 
761         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
762                 .driver_data = LG_NOGET },
763         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
764                 .driver_data = LG_NOGET },
765         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
766                 .driver_data = LG_NOGET | LG_FF4 },
767 
768         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
769                 .driver_data = LG_FF2 },
770         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
771                 .driver_data = LG_FF },
772         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
773                 .driver_data = LG_FF },
774         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
775                 .driver_data = LG_FF },
776         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
777                 .driver_data = LG_FF },
778         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
779                 .driver_data = LG_NOGET | LG_FF4 },
780         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
781                 .driver_data = LG_FF4 },
782         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
783                 .driver_data = LG_FF2 },
784         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
785                 .driver_data = LG_FF4 },
786         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
787                 .driver_data = LG_FF4 },
788         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
789                 .driver_data = LG_FF4 },
790         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
791                 .driver_data = LG_NOGET | LG_FF4 },
792         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
793                 .driver_data = LG_FF4 },
794         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
795                 .driver_data = LG_FF },
796         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
797                 .driver_data = LG_FF2 },
798         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
799                 .driver_data = LG_FF3 },
800         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
801                 .driver_data = LG_RDESC_REL_ABS },
802         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
803                 .driver_data = LG_RDESC_REL_ABS },
804         { }
805 };
806 
807 MODULE_DEVICE_TABLE(hid, lg_devices);
808 
809 static struct hid_driver lg_driver = {
810         .name = "logitech",
811         .id_table = lg_devices,
812         .report_fixup = lg_report_fixup,
813         .input_mapping = lg_input_mapping,
814         .input_mapped = lg_input_mapped,
815         .event = lg_event,
816         .probe = lg_probe,
817         .remove = lg_remove,
818 };
819 module_hid_driver(lg_driver);
820 
821 MODULE_LICENSE("GPL");
822 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us