Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/hid/hid-lg.c
15109 views
1
/*
2
* HID driver for some logitech "special" devices
3
*
4
* Copyright (c) 1999 Andreas Gal
5
* Copyright (c) 2000-2005 Vojtech Pavlik <[email protected]>
6
* Copyright (c) 2005 Michael Haboustak <[email protected]> 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
/*
45
* Certain Logitech keyboards send in report #3 keys which are far
46
* above the logical maximum described in descriptor. This extends
47
* the original value of 0x28c of logical maximum to 0x104d
48
*/
49
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
50
unsigned int *rsize)
51
{
52
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
53
54
if ((quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
55
rdesc[84] == 0x8c && rdesc[85] == 0x02) {
56
hid_info(hdev,
57
"fixing up Logitech keyboard report descriptor\n");
58
rdesc[84] = rdesc[89] = 0x4d;
59
rdesc[85] = rdesc[90] = 0x10;
60
}
61
if ((quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
62
rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
63
rdesc[49] == 0x81 && rdesc[50] == 0x06) {
64
hid_info(hdev,
65
"fixing up rel/abs in Logitech report descriptor\n");
66
rdesc[33] = rdesc[50] = 0x02;
67
}
68
if ((quirks & LG_FF4) && *rsize >= 101 &&
69
rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
70
rdesc[47] == 0x05 && rdesc[48] == 0x09) {
71
hid_info(hdev, "fixing up Logitech Speed Force Wireless button descriptor\n");
72
rdesc[41] = 0x05;
73
rdesc[42] = 0x09;
74
rdesc[47] = 0x95;
75
rdesc[48] = 0x0B;
76
}
77
return rdesc;
78
}
79
80
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
81
EV_KEY, (c))
82
83
static int lg_ultrax_remote_mapping(struct hid_input *hi,
84
struct hid_usage *usage, unsigned long **bit, int *max)
85
{
86
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
87
return 0;
88
89
set_bit(EV_REP, hi->input->evbit);
90
switch (usage->hid & HID_USAGE) {
91
/* Reported on Logitech Ultra X Media Remote */
92
case 0x004: lg_map_key_clear(KEY_AGAIN); break;
93
case 0x00d: lg_map_key_clear(KEY_HOME); break;
94
case 0x024: lg_map_key_clear(KEY_SHUFFLE); break;
95
case 0x025: lg_map_key_clear(KEY_TV); break;
96
case 0x026: lg_map_key_clear(KEY_MENU); break;
97
case 0x031: lg_map_key_clear(KEY_AUDIO); break;
98
case 0x032: lg_map_key_clear(KEY_TEXT); break;
99
case 0x033: lg_map_key_clear(KEY_LAST); break;
100
case 0x047: lg_map_key_clear(KEY_MP3); break;
101
case 0x048: lg_map_key_clear(KEY_DVD); break;
102
case 0x049: lg_map_key_clear(KEY_MEDIA); break;
103
case 0x04a: lg_map_key_clear(KEY_VIDEO); break;
104
case 0x04b: lg_map_key_clear(KEY_ANGLE); break;
105
case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break;
106
case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break;
107
case 0x051: lg_map_key_clear(KEY_RED); break;
108
case 0x052: lg_map_key_clear(KEY_CLOSE); break;
109
110
default:
111
return 0;
112
}
113
return 1;
114
}
115
116
static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
117
unsigned long **bit, int *max)
118
{
119
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
120
return 0;
121
122
switch (usage->hid & HID_USAGE) {
123
124
case 0x00d: lg_map_key_clear(KEY_MEDIA); break;
125
default:
126
return 0;
127
128
}
129
return 1;
130
}
131
132
static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
133
unsigned long **bit, int *max)
134
{
135
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
136
return 0;
137
138
switch (usage->hid & HID_USAGE) {
139
case 0x1001: lg_map_key_clear(KEY_MESSENGER); break;
140
case 0x1003: lg_map_key_clear(KEY_SOUND); break;
141
case 0x1004: lg_map_key_clear(KEY_VIDEO); break;
142
case 0x1005: lg_map_key_clear(KEY_AUDIO); break;
143
case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break;
144
/* The following two entries are Playlist 1 and 2 on the MX3200 */
145
case 0x100f: lg_map_key_clear(KEY_FN_1); break;
146
case 0x1010: lg_map_key_clear(KEY_FN_2); break;
147
case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break;
148
case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break;
149
case 0x1013: lg_map_key_clear(KEY_CAMERA); break;
150
case 0x1014: lg_map_key_clear(KEY_MESSENGER); break;
151
case 0x1015: lg_map_key_clear(KEY_RECORD); break;
152
case 0x1016: lg_map_key_clear(KEY_PLAYER); break;
153
case 0x1017: lg_map_key_clear(KEY_EJECTCD); break;
154
case 0x1018: lg_map_key_clear(KEY_MEDIA); break;
155
case 0x1019: lg_map_key_clear(KEY_PROG1); break;
156
case 0x101a: lg_map_key_clear(KEY_PROG2); break;
157
case 0x101b: lg_map_key_clear(KEY_PROG3); break;
158
case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
159
case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break;
160
case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break;
161
case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break;
162
case 0x1023: lg_map_key_clear(KEY_CLOSE); break;
163
case 0x1027: lg_map_key_clear(KEY_MENU); break;
164
/* this one is marked as 'Rotate' */
165
case 0x1028: lg_map_key_clear(KEY_ANGLE); break;
166
case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break;
167
case 0x102a: lg_map_key_clear(KEY_BACK); break;
168
case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
169
case 0x102d: lg_map_key_clear(KEY_WWW); break;
170
/* The following two are 'Start/answer call' and 'End/reject call'
171
on the MX3200 */
172
case 0x1031: lg_map_key_clear(KEY_OK); break;
173
case 0x1032: lg_map_key_clear(KEY_CANCEL); break;
174
case 0x1041: lg_map_key_clear(KEY_BATTERY); break;
175
case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break;
176
case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break;
177
case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break;
178
case 0x1045: lg_map_key_clear(KEY_UNDO); break;
179
case 0x1046: lg_map_key_clear(KEY_REDO); break;
180
case 0x1047: lg_map_key_clear(KEY_PRINT); break;
181
case 0x1048: lg_map_key_clear(KEY_SAVE); break;
182
case 0x1049: lg_map_key_clear(KEY_PROG1); break;
183
case 0x104a: lg_map_key_clear(KEY_PROG2); break;
184
case 0x104b: lg_map_key_clear(KEY_PROG3); break;
185
case 0x104c: lg_map_key_clear(KEY_PROG4); break;
186
187
default:
188
return 0;
189
}
190
return 1;
191
}
192
193
static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
194
struct hid_field *field, struct hid_usage *usage,
195
unsigned long **bit, int *max)
196
{
197
/* extended mapping for certain Logitech hardware (Logitech cordless
198
desktop LX500) */
199
static const u8 e_keymap[] = {
200
0,216, 0,213,175,156, 0, 0, 0, 0,
201
144, 0, 0, 0, 0, 0, 0, 0, 0,212,
202
174,167,152,161,112, 0, 0, 0,154, 0,
203
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
204
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
205
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
206
0, 0, 0, 0, 0,183,184,185,186,187,
207
188,189,190,191,192,193,194, 0, 0, 0
208
};
209
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
210
unsigned int hid = usage->hid;
211
212
if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
213
lg_ultrax_remote_mapping(hi, usage, bit, max))
214
return 1;
215
216
if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
217
lg_dinovo_mapping(hi, usage, bit, max))
218
return 1;
219
220
if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
221
return 1;
222
223
if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
224
return 0;
225
226
hid &= HID_USAGE;
227
228
/* Special handling for Logitech Cordless Desktop */
229
if (field->application == HID_GD_MOUSE) {
230
if ((quirks & LG_IGNORE_DOUBLED_WHEEL) &&
231
(hid == 7 || hid == 8))
232
return -1;
233
} else {
234
if ((quirks & LG_EXPANDED_KEYMAP) &&
235
hid < ARRAY_SIZE(e_keymap) &&
236
e_keymap[hid] != 0) {
237
hid_map_usage(hi, usage, bit, max, EV_KEY,
238
e_keymap[hid]);
239
return 1;
240
}
241
}
242
243
return 0;
244
}
245
246
static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
247
struct hid_field *field, struct hid_usage *usage,
248
unsigned long **bit, int *max)
249
{
250
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
251
252
if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
253
(field->flags & HID_MAIN_ITEM_RELATIVE))
254
field->flags &= ~HID_MAIN_ITEM_RELATIVE;
255
256
if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
257
usage->type == EV_REL || usage->type == EV_ABS))
258
clear_bit(usage->code, *bit);
259
260
return 0;
261
}
262
263
static int lg_event(struct hid_device *hdev, struct hid_field *field,
264
struct hid_usage *usage, __s32 value)
265
{
266
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
267
268
if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
269
input_event(field->hidinput->input, usage->type, usage->code,
270
-value);
271
return 1;
272
}
273
274
return 0;
275
}
276
277
static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
278
{
279
unsigned long quirks = id->driver_data;
280
unsigned int connect_mask = HID_CONNECT_DEFAULT;
281
int ret;
282
283
hid_set_drvdata(hdev, (void *)quirks);
284
285
if (quirks & LG_NOGET)
286
hdev->quirks |= HID_QUIRK_NOGET;
287
288
ret = hid_parse(hdev);
289
if (ret) {
290
hid_err(hdev, "parse failed\n");
291
goto err_free;
292
}
293
294
if (quirks & (LG_FF | LG_FF2 | LG_FF3))
295
connect_mask &= ~HID_CONNECT_FF;
296
297
ret = hid_hw_start(hdev, connect_mask);
298
if (ret) {
299
hid_err(hdev, "hw start failed\n");
300
goto err_free;
301
}
302
303
if (quirks & LG_FF4) {
304
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
305
306
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
307
308
if (ret >= 0) {
309
/* insert a little delay of 10 jiffies ~ 40ms */
310
wait_queue_head_t wait;
311
init_waitqueue_head (&wait);
312
wait_event_interruptible_timeout(wait, 0, 10);
313
314
/* Select random Address */
315
buf[1] = 0xB2;
316
get_random_bytes(&buf[2], 2);
317
318
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
319
}
320
}
321
322
if (quirks & LG_FF)
323
lgff_init(hdev);
324
if (quirks & LG_FF2)
325
lg2ff_init(hdev);
326
if (quirks & LG_FF3)
327
lg3ff_init(hdev);
328
if (quirks & LG_FF4)
329
lg4ff_init(hdev);
330
331
return 0;
332
err_free:
333
return ret;
334
}
335
336
static const struct hid_device_id lg_devices[] = {
337
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
338
.driver_data = LG_RDESC | LG_WIRELESS },
339
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
340
.driver_data = LG_RDESC | LG_WIRELESS },
341
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
342
.driver_data = LG_RDESC | LG_WIRELESS },
343
344
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
345
.driver_data = LG_BAD_RELATIVE_KEYS },
346
347
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
348
.driver_data = LG_DUPLICATE_USAGES },
349
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
350
.driver_data = LG_DUPLICATE_USAGES },
351
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
352
.driver_data = LG_DUPLICATE_USAGES },
353
354
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
355
.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
356
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
357
.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
358
359
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
360
.driver_data = LG_NOGET },
361
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
362
.driver_data = LG_NOGET | LG_FF },
363
364
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
365
.driver_data = LG_FF2 },
366
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
367
.driver_data = LG_FF },
368
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
369
.driver_data = LG_FF },
370
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
371
.driver_data = LG_FF },
372
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
373
.driver_data = LG_FF },
374
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
375
.driver_data = LG_FF },
376
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
377
.driver_data = LG_FF },
378
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
379
.driver_data = LG_FF },
380
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
381
.driver_data = LG_FF },
382
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
383
.driver_data = LG_FF },
384
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
385
.driver_data = LG_FF4 },
386
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
387
.driver_data = LG_FF },
388
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
389
.driver_data = LG_FF2 },
390
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
391
.driver_data = LG_FF3 },
392
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
393
.driver_data = LG_RDESC_REL_ABS },
394
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
395
.driver_data = LG_RDESC_REL_ABS },
396
{ }
397
};
398
399
MODULE_DEVICE_TABLE(hid, lg_devices);
400
401
static struct hid_driver lg_driver = {
402
.name = "logitech",
403
.id_table = lg_devices,
404
.report_fixup = lg_report_fixup,
405
.input_mapping = lg_input_mapping,
406
.input_mapped = lg_input_mapped,
407
.event = lg_event,
408
.probe = lg_probe,
409
};
410
411
static int __init lg_init(void)
412
{
413
return hid_register_driver(&lg_driver);
414
}
415
416
static void __exit lg_exit(void)
417
{
418
hid_unregister_driver(&lg_driver);
419
}
420
421
module_init(lg_init);
422
module_exit(lg_exit);
423
MODULE_LICENSE("GPL");
424
425