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