1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * HID driver for some samsung "special" devices |
4 | * |
5 | * Copyright (c) 1999 Andreas Gal |
6 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
7 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
8 | * Copyright (c) 2006-2007 Jiri Kosina |
9 | * Copyright (c) 2008 Jiri Slaby |
10 | * Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk> |
11 | * |
12 | * This driver supports several HID devices: |
13 | * |
14 | * [0419:0001] Samsung IrDA remote controller (reports as Cypress USB Mouse). |
15 | * various hid report fixups for different variants. |
16 | * |
17 | * [0419:0600] Creative Desktop Wireless 6000 keyboard/mouse combo |
18 | * several key mappings used from the consumer usage page |
19 | * deviate from the USB HUT 1.12 standard. |
20 | */ |
21 | |
22 | /* |
23 | */ |
24 | |
25 | #include <linux/device.h> |
26 | #include <linux/usb.h> |
27 | #include <linux/hid.h> |
28 | #include <linux/module.h> |
29 | |
30 | #include "hid-ids.h" |
31 | |
32 | /* |
33 | * There are several variants for 0419:0001: |
34 | * |
35 | * 1. 184 byte report descriptor |
36 | * Vendor specific report #4 has a size of 48 bit, |
37 | * and therefore is not accepted when inspecting the descriptors. |
38 | * As a workaround we reinterpret the report as: |
39 | * Variable type, count 6, size 8 bit, log. maximum 255 |
40 | * The burden to reconstruct the data is moved into user space. |
41 | * |
42 | * 2. 203 byte report descriptor |
43 | * Report #4 has an array field with logical range 0..18 instead of 1..15. |
44 | * |
45 | * 3. 135 byte report descriptor |
46 | * Report #4 has an array field with logical range 0..17 instead of 1..14. |
47 | * |
48 | * 4. 171 byte report descriptor |
49 | * Report #3 has an array field with logical range 0..1 instead of 1..3. |
50 | */ |
51 | static inline void samsung_irda_dev_trace(struct hid_device *hdev, |
52 | unsigned int rsize) |
53 | { |
54 | hid_info(hdev, "fixing up Samsung IrDA %d byte report descriptor\n" , |
55 | rsize); |
56 | } |
57 | |
58 | static __u8 *samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
59 | unsigned int *rsize) |
60 | { |
61 | if (*rsize == 184 && !memcmp(p: &rdesc[175], q: "\x25\x40\x75\x30\x95\x01" , size: 6) && |
62 | rdesc[182] == 0x40) { |
63 | samsung_irda_dev_trace(hdev, rsize: 184); |
64 | rdesc[176] = 0xff; |
65 | rdesc[178] = 0x08; |
66 | rdesc[180] = 0x06; |
67 | rdesc[182] = 0x42; |
68 | } else if (*rsize == 203 && !memcmp(p: &rdesc[192], q: "\x15\x00\x25\x12" , size: 4)) { |
69 | samsung_irda_dev_trace(hdev, rsize: 203); |
70 | rdesc[193] = 0x01; |
71 | rdesc[195] = 0x0f; |
72 | } else if (*rsize == 135 && !memcmp(p: &rdesc[124], q: "\x15\x00\x25\x11" , size: 4)) { |
73 | samsung_irda_dev_trace(hdev, rsize: 135); |
74 | rdesc[125] = 0x01; |
75 | rdesc[127] = 0x0e; |
76 | } else if (*rsize == 171 && !memcmp(p: &rdesc[160], q: "\x15\x00\x25\x01" , size: 4)) { |
77 | samsung_irda_dev_trace(hdev, rsize: 171); |
78 | rdesc[161] = 0x01; |
79 | rdesc[163] = 0x03; |
80 | } |
81 | return rdesc; |
82 | } |
83 | |
84 | #define samsung_kbd_mouse_map_key_clear(c) \ |
85 | hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) |
86 | |
87 | static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev, |
88 | struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, |
89 | unsigned long **bit, int *max) |
90 | { |
91 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
92 | unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber; |
93 | |
94 | if (ifnum != 1 || HID_UP_CONSUMER != (usage->hid & HID_USAGE_PAGE)) |
95 | return 0; |
96 | |
97 | dbg_hid("samsung wireless keyboard/mouse input mapping event [0x%x]\n" , |
98 | usage->hid & HID_USAGE); |
99 | |
100 | switch (usage->hid & HID_USAGE) { |
101 | /* report 2 */ |
102 | case 0x183: |
103 | samsung_kbd_mouse_map_key_clear(KEY_MEDIA); |
104 | break; |
105 | case 0x195: |
106 | samsung_kbd_mouse_map_key_clear(KEY_EMAIL); |
107 | break; |
108 | case 0x196: |
109 | samsung_kbd_mouse_map_key_clear(KEY_CALC); |
110 | break; |
111 | case 0x197: |
112 | samsung_kbd_mouse_map_key_clear(KEY_COMPUTER); |
113 | break; |
114 | case 0x22b: |
115 | samsung_kbd_mouse_map_key_clear(KEY_SEARCH); |
116 | break; |
117 | case 0x22c: |
118 | samsung_kbd_mouse_map_key_clear(KEY_WWW); |
119 | break; |
120 | case 0x22d: |
121 | samsung_kbd_mouse_map_key_clear(KEY_BACK); |
122 | break; |
123 | case 0x22e: |
124 | samsung_kbd_mouse_map_key_clear(KEY_FORWARD); |
125 | break; |
126 | case 0x22f: |
127 | samsung_kbd_mouse_map_key_clear(KEY_FAVORITES); |
128 | break; |
129 | case 0x230: |
130 | samsung_kbd_mouse_map_key_clear(KEY_REFRESH); |
131 | break; |
132 | case 0x231: |
133 | samsung_kbd_mouse_map_key_clear(KEY_STOP); |
134 | break; |
135 | default: |
136 | return 0; |
137 | } |
138 | |
139 | return 1; |
140 | } |
141 | |
142 | static int samsung_kbd_input_mapping(struct hid_device *hdev, |
143 | struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, |
144 | unsigned long **bit, int *max) |
145 | { |
146 | if (!(HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE) || |
147 | HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE))) |
148 | return 0; |
149 | |
150 | dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n" , |
151 | usage->hid & HID_USAGE); |
152 | |
153 | if (HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)) { |
154 | set_bit(EV_REP, addr: hi->input->evbit); |
155 | switch (usage->hid & HID_USAGE) { |
156 | case 0x32: |
157 | samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH); |
158 | break; |
159 | case 0x64: |
160 | samsung_kbd_mouse_map_key_clear(KEY_102ND); |
161 | break; |
162 | /* Only for BR keyboard */ |
163 | case 0x87: |
164 | samsung_kbd_mouse_map_key_clear(KEY_RO); |
165 | break; |
166 | default: |
167 | return 0; |
168 | } |
169 | } |
170 | |
171 | if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) { |
172 | switch (usage->hid & HID_USAGE) { |
173 | /* report 2 */ |
174 | /* MENU */ |
175 | case 0x040: |
176 | samsung_kbd_mouse_map_key_clear(KEY_MENU); |
177 | break; |
178 | case 0x18a: |
179 | samsung_kbd_mouse_map_key_clear(KEY_MAIL); |
180 | break; |
181 | case 0x196: |
182 | samsung_kbd_mouse_map_key_clear(KEY_WWW); |
183 | break; |
184 | case 0x19e: |
185 | samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK); |
186 | break; |
187 | case 0x221: |
188 | samsung_kbd_mouse_map_key_clear(KEY_SEARCH); |
189 | break; |
190 | case 0x223: |
191 | samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE); |
192 | break; |
193 | /* Smtart Voice Key */ |
194 | case 0x300: |
195 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY13); |
196 | break; |
197 | /* RECENTAPPS */ |
198 | case 0x301: |
199 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1); |
200 | break; |
201 | /* APPLICATION */ |
202 | case 0x302: |
203 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2); |
204 | break; |
205 | /* Voice search */ |
206 | case 0x305: |
207 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4); |
208 | break; |
209 | /* QPANEL on/off */ |
210 | case 0x306: |
211 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5); |
212 | break; |
213 | /* SIP on/off */ |
214 | case 0x307: |
215 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3); |
216 | break; |
217 | /* LANG */ |
218 | case 0x308: |
219 | samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE); |
220 | break; |
221 | case 0x30a: |
222 | samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN); |
223 | break; |
224 | case 0x30b: |
225 | samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP); |
226 | break; |
227 | default: |
228 | return 0; |
229 | } |
230 | } |
231 | |
232 | return 1; |
233 | } |
234 | |
235 | static int samsung_gamepad_input_mapping(struct hid_device *hdev, |
236 | struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, |
237 | unsigned long **bit, int *max) |
238 | { |
239 | if (!(HID_UP_BUTTON == (usage->hid & HID_USAGE_PAGE) || |
240 | HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE))) |
241 | return 0; |
242 | |
243 | dbg_hid("samsung wireless gamepad input mapping event [0x%x], %ld, %ld, [0x%x]\n" , |
244 | usage->hid & HID_USAGE, hi->input->evbit[0], hi->input->absbit[0], usage->hid & HID_USAGE_PAGE); |
245 | |
246 | if (HID_UP_BUTTON == (usage->hid & HID_USAGE_PAGE)) { |
247 | switch (usage->hid & HID_USAGE) { |
248 | case 0x01: |
249 | samsung_kbd_mouse_map_key_clear(BTN_A); |
250 | break; |
251 | case 0x02: |
252 | samsung_kbd_mouse_map_key_clear(BTN_B); |
253 | break; |
254 | case 0x03: |
255 | samsung_kbd_mouse_map_key_clear(BTN_C); |
256 | break; |
257 | case 0x04: |
258 | samsung_kbd_mouse_map_key_clear(BTN_X); |
259 | break; |
260 | case 0x05: |
261 | samsung_kbd_mouse_map_key_clear(BTN_Y); |
262 | break; |
263 | case 0x06: |
264 | samsung_kbd_mouse_map_key_clear(BTN_Z); |
265 | break; |
266 | case 0x07: |
267 | samsung_kbd_mouse_map_key_clear(BTN_TL); |
268 | break; |
269 | case 0x08: |
270 | samsung_kbd_mouse_map_key_clear(BTN_TR); |
271 | break; |
272 | case 0x09: |
273 | samsung_kbd_mouse_map_key_clear(BTN_TL2); |
274 | break; |
275 | case 0x0a: |
276 | samsung_kbd_mouse_map_key_clear(BTN_TR2); |
277 | break; |
278 | case 0x0b: |
279 | samsung_kbd_mouse_map_key_clear(BTN_SELECT); |
280 | break; |
281 | case 0x0c: |
282 | samsung_kbd_mouse_map_key_clear(BTN_START); |
283 | break; |
284 | case 0x0d: |
285 | samsung_kbd_mouse_map_key_clear(BTN_MODE); |
286 | break; |
287 | case 0x0e: |
288 | samsung_kbd_mouse_map_key_clear(BTN_THUMBL); |
289 | break; |
290 | case 0x0f: |
291 | samsung_kbd_mouse_map_key_clear(BTN_THUMBR); |
292 | break; |
293 | case 0x10: |
294 | samsung_kbd_mouse_map_key_clear(0x13f); |
295 | break; |
296 | default: |
297 | return 0; |
298 | } |
299 | } |
300 | |
301 | if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) { |
302 | switch (usage->hid & HID_USAGE) { |
303 | case 0x040: |
304 | samsung_kbd_mouse_map_key_clear(KEY_MENU); |
305 | break; |
306 | case 0x223: |
307 | samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE); |
308 | break; |
309 | case 0x224: |
310 | samsung_kbd_mouse_map_key_clear(KEY_BACK); |
311 | break; |
312 | |
313 | /* Screen Capture */ |
314 | case 0x303: |
315 | samsung_kbd_mouse_map_key_clear(KEY_SYSRQ); |
316 | break; |
317 | |
318 | default: |
319 | return 0; |
320 | } |
321 | } |
322 | |
323 | return 1; |
324 | } |
325 | |
326 | static int samsung_actionmouse_input_mapping(struct hid_device *hdev, |
327 | struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, |
328 | unsigned long **bit, int *max) |
329 | { |
330 | |
331 | dbg_hid("samsung wireless actionmouse input mapping event [0x%x], [0x%x], %ld, %ld, [0x%x]\n" , |
332 | usage->hid, usage->hid & HID_USAGE, hi->input->evbit[0], hi->input->absbit[0], |
333 | usage->hid & HID_USAGE_PAGE); |
334 | |
335 | if (((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) && ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)) |
336 | return 0; |
337 | |
338 | switch (usage->hid & HID_USAGE) { |
339 | case 0x301: |
340 | samsung_kbd_mouse_map_key_clear(254); |
341 | break; |
342 | default: |
343 | return 0; |
344 | } |
345 | |
346 | return 1; |
347 | } |
348 | |
349 | static int samsung_universal_kbd_input_mapping(struct hid_device *hdev, |
350 | struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, |
351 | unsigned long **bit, int *max) |
352 | { |
353 | if (!(HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE) || |
354 | HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE))) |
355 | return 0; |
356 | |
357 | dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n" , |
358 | usage->hid & HID_USAGE); |
359 | |
360 | if (HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)) { |
361 | set_bit(EV_REP, addr: hi->input->evbit); |
362 | switch (usage->hid & HID_USAGE) { |
363 | case 0x32: |
364 | samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH); |
365 | break; |
366 | case 0x64: |
367 | samsung_kbd_mouse_map_key_clear(KEY_102ND); |
368 | break; |
369 | /* Only for BR keyboard */ |
370 | case 0x87: |
371 | samsung_kbd_mouse_map_key_clear(KEY_RO); |
372 | break; |
373 | default: |
374 | return 0; |
375 | } |
376 | } |
377 | |
378 | if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) { |
379 | switch (usage->hid & HID_USAGE) { |
380 | /* report 2 */ |
381 | /* MENU */ |
382 | case 0x040: |
383 | samsung_kbd_mouse_map_key_clear(KEY_MENU); |
384 | break; |
385 | case 0x18a: |
386 | samsung_kbd_mouse_map_key_clear(KEY_MAIL); |
387 | break; |
388 | case 0x196: |
389 | samsung_kbd_mouse_map_key_clear(KEY_WWW); |
390 | break; |
391 | case 0x19e: |
392 | samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK); |
393 | break; |
394 | case 0x221: |
395 | samsung_kbd_mouse_map_key_clear(KEY_SEARCH); |
396 | break; |
397 | case 0x223: |
398 | samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE); |
399 | break; |
400 | /* RECENTAPPS */ |
401 | case 0x301: |
402 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1); |
403 | break; |
404 | /* APPLICATION */ |
405 | case 0x302: |
406 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2); |
407 | break; |
408 | /* Voice search */ |
409 | case 0x305: |
410 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4); |
411 | break; |
412 | /* QPANEL on/off */ |
413 | case 0x306: |
414 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5); |
415 | break; |
416 | /* SIP on/off */ |
417 | case 0x307: |
418 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3); |
419 | break; |
420 | /* LANG */ |
421 | case 0x308: |
422 | samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE); |
423 | break; |
424 | case 0x30a: |
425 | samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN); |
426 | break; |
427 | case 0x070: |
428 | samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN); |
429 | break; |
430 | case 0x30b: |
431 | samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP); |
432 | break; |
433 | case 0x06f: |
434 | samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP); |
435 | break; |
436 | /* S-Finder */ |
437 | case 0x304: |
438 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY7); |
439 | break; |
440 | /* Screen Capture */ |
441 | case 0x303: |
442 | samsung_kbd_mouse_map_key_clear(KEY_SYSRQ); |
443 | break; |
444 | /* Multi Window */ |
445 | case 0x309: |
446 | samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY9); |
447 | break; |
448 | /* HotKey App 1 */ |
449 | case 0x071: |
450 | samsung_kbd_mouse_map_key_clear(0x2f5); |
451 | break; |
452 | /* HotKey App 2 */ |
453 | case 0x072: |
454 | samsung_kbd_mouse_map_key_clear(0x2f6); |
455 | break; |
456 | /* HotKey App 3 */ |
457 | case 0x073: |
458 | samsung_kbd_mouse_map_key_clear(0x2f7); |
459 | break; |
460 | /* Dex */ |
461 | case 0x06e: |
462 | samsung_kbd_mouse_map_key_clear(0x2bd); |
463 | break; |
464 | default: |
465 | return 0; |
466 | } |
467 | } |
468 | |
469 | return 1; |
470 | } |
471 | |
472 | static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
473 | unsigned int *rsize) |
474 | { |
475 | if (hdev->product == USB_DEVICE_ID_SAMSUNG_IR_REMOTE && hid_is_usb(hdev)) |
476 | rdesc = samsung_irda_report_fixup(hdev, rdesc, rsize); |
477 | return rdesc; |
478 | } |
479 | |
480 | static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi, |
481 | struct hid_field *field, struct hid_usage *usage, |
482 | unsigned long **bit, int *max) |
483 | { |
484 | int ret = 0; |
485 | |
486 | if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE && hid_is_usb(hdev)) |
487 | ret = samsung_kbd_mouse_input_mapping(hdev, |
488 | hi, field, usage, bit, max); |
489 | else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD) |
490 | ret = samsung_kbd_input_mapping(hdev, |
491 | hi, field, usage, bit, max); |
492 | else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_GAMEPAD) |
493 | ret = samsung_gamepad_input_mapping(hdev, |
494 | hi, field, usage, bit, max); |
495 | else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_ACTIONMOUSE) |
496 | ret = samsung_actionmouse_input_mapping(hdev, |
497 | hi, field, usage, bit, max); |
498 | else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD) |
499 | ret = samsung_universal_kbd_input_mapping(hdev, |
500 | hi, field, usage, bit, max); |
501 | else if (hdev->product == USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD) |
502 | ret = samsung_universal_kbd_input_mapping(hdev, |
503 | hi, field, usage, bit, max); |
504 | |
505 | return ret; |
506 | } |
507 | |
508 | static int samsung_probe(struct hid_device *hdev, |
509 | const struct hid_device_id *id) |
510 | { |
511 | int ret; |
512 | unsigned int cmask = HID_CONNECT_DEFAULT; |
513 | |
514 | ret = hid_parse(hdev); |
515 | if (ret) { |
516 | hid_err(hdev, "parse failed\n" ); |
517 | goto err_free; |
518 | } |
519 | |
520 | if (hdev->product == USB_DEVICE_ID_SAMSUNG_IR_REMOTE) { |
521 | if (!hid_is_usb(hdev)) { |
522 | ret = -EINVAL; |
523 | goto err_free; |
524 | } |
525 | if (hdev->rsize == 184) { |
526 | /* disable hidinput, force hiddev */ |
527 | cmask = (cmask & ~HID_CONNECT_HIDINPUT) | |
528 | HID_CONNECT_HIDDEV_FORCE; |
529 | } |
530 | } |
531 | |
532 | ret = hid_hw_start(hdev, connect_mask: cmask); |
533 | if (ret) { |
534 | hid_err(hdev, "hw start failed\n" ); |
535 | goto err_free; |
536 | } |
537 | |
538 | return 0; |
539 | err_free: |
540 | return ret; |
541 | } |
542 | |
543 | static const struct hid_device_id samsung_devices[] = { |
544 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, |
545 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, |
546 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD) }, |
547 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_GAMEPAD) }, |
548 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_ACTIONMOUSE) }, |
549 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD) }, |
550 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD) }, |
551 | { } |
552 | }; |
553 | MODULE_DEVICE_TABLE(hid, samsung_devices); |
554 | |
555 | static struct hid_driver samsung_driver = { |
556 | .name = "samsung" , |
557 | .id_table = samsung_devices, |
558 | .report_fixup = samsung_report_fixup, |
559 | .input_mapping = samsung_input_mapping, |
560 | .probe = samsung_probe, |
561 | }; |
562 | module_hid_driver(samsung_driver); |
563 | |
564 | MODULE_LICENSE("GPL" ); |
565 | |