1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * HID driver for some samsung "special" devices
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>
12 * This driver supports several HID devices:
14 * [0419:0001] Samsung IrDA remote controller (reports as Cypress USB Mouse).
15 * various hid report fixups for different variants.
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.
25 #include <linux/device.h>
26 #include <linux/usb.h>
27 #include <linux/hid.h>
28 #include <linux/module.h>
33 * There are several variants for 0419:0001:
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.
42 * 2. 203 byte report descriptor
43 * Report #4 has an array field with logical range 0..18 instead of 1..15.
45 * 3. 135 byte report descriptor
46 * Report #4 has an array field with logical range 0..17 instead of 1..14.
48 * 4. 171 byte report descriptor
49 * Report #3 has an array field with logical range 0..1 instead of 1..3.
51 static inline void samsung_irda_dev_trace(struct hid_device
*hdev
,
54 hid_info(hdev
, "fixing up Samsung IrDA %d byte report descriptor\n",
58 static __u8
*samsung_irda_report_fixup(struct hid_device
*hdev
, __u8
*rdesc
,
61 if (*rsize
== 184 && !memcmp(&rdesc
[175], "\x25\x40\x75\x30\x95\x01", 6) &&
63 samsung_irda_dev_trace(hdev
, 184);
68 } else if (*rsize
== 203 && !memcmp(&rdesc
[192], "\x15\x00\x25\x12", 4)) {
69 samsung_irda_dev_trace(hdev
, 203);
72 } else if (*rsize
== 135 && !memcmp(&rdesc
[124], "\x15\x00\x25\x11", 4)) {
73 samsung_irda_dev_trace(hdev
, 135);
76 } else if (*rsize
== 171 && !memcmp(&rdesc
[160], "\x15\x00\x25\x01", 4)) {
77 samsung_irda_dev_trace(hdev
, 171);
84 #define samsung_kbd_mouse_map_key_clear(c) \
85 hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
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
)
91 struct usb_interface
*intf
= to_usb_interface(hdev
->dev
.parent
);
92 unsigned short ifnum
= intf
->cur_altsetting
->desc
.bInterfaceNumber
;
94 if (ifnum
!= 1 || HID_UP_CONSUMER
!= (usage
->hid
& HID_USAGE_PAGE
))
97 dbg_hid("samsung wireless keyboard/mouse input mapping event [0x%x]\n",
98 usage
->hid
& HID_USAGE
);
100 switch (usage
->hid
& HID_USAGE
) {
103 samsung_kbd_mouse_map_key_clear(KEY_MEDIA
);
106 samsung_kbd_mouse_map_key_clear(KEY_EMAIL
);
109 samsung_kbd_mouse_map_key_clear(KEY_CALC
);
112 samsung_kbd_mouse_map_key_clear(KEY_COMPUTER
);
115 samsung_kbd_mouse_map_key_clear(KEY_SEARCH
);
118 samsung_kbd_mouse_map_key_clear(KEY_WWW
);
121 samsung_kbd_mouse_map_key_clear(KEY_BACK
);
124 samsung_kbd_mouse_map_key_clear(KEY_FORWARD
);
127 samsung_kbd_mouse_map_key_clear(KEY_FAVORITES
);
130 samsung_kbd_mouse_map_key_clear(KEY_REFRESH
);
133 samsung_kbd_mouse_map_key_clear(KEY_STOP
);
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
)
146 if (!(HID_UP_CONSUMER
== (usage
->hid
& HID_USAGE_PAGE
) ||
147 HID_UP_KEYBOARD
== (usage
->hid
& HID_USAGE_PAGE
)))
150 dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n",
151 usage
->hid
& HID_USAGE
);
153 if (HID_UP_KEYBOARD
== (usage
->hid
& HID_USAGE_PAGE
)) {
154 set_bit(EV_REP
, hi
->input
->evbit
);
155 switch (usage
->hid
& HID_USAGE
) {
157 samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH
);
160 samsung_kbd_mouse_map_key_clear(KEY_102ND
);
162 /* Only for BR keyboard */
164 samsung_kbd_mouse_map_key_clear(KEY_RO
);
171 if (HID_UP_CONSUMER
== (usage
->hid
& HID_USAGE_PAGE
)) {
172 switch (usage
->hid
& HID_USAGE
) {
176 samsung_kbd_mouse_map_key_clear(KEY_MENU
);
179 samsung_kbd_mouse_map_key_clear(KEY_MAIL
);
182 samsung_kbd_mouse_map_key_clear(KEY_WWW
);
185 samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK
);
188 samsung_kbd_mouse_map_key_clear(KEY_SEARCH
);
191 samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE
);
193 /* Smtart Voice Key */
195 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY13
);
199 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1
);
203 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2
);
207 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4
);
211 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5
);
215 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3
);
219 samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE
);
222 samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN
);
225 samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP
);
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
)
239 if (!(HID_UP_BUTTON
== (usage
->hid
& HID_USAGE_PAGE
) ||
240 HID_UP_CONSUMER
== (usage
->hid
& HID_USAGE_PAGE
)))
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
);
246 if (HID_UP_BUTTON
== (usage
->hid
& HID_USAGE_PAGE
)) {
247 switch (usage
->hid
& HID_USAGE
) {
249 samsung_kbd_mouse_map_key_clear(BTN_A
);
252 samsung_kbd_mouse_map_key_clear(BTN_B
);
255 samsung_kbd_mouse_map_key_clear(BTN_C
);
258 samsung_kbd_mouse_map_key_clear(BTN_X
);
261 samsung_kbd_mouse_map_key_clear(BTN_Y
);
264 samsung_kbd_mouse_map_key_clear(BTN_Z
);
267 samsung_kbd_mouse_map_key_clear(BTN_TL
);
270 samsung_kbd_mouse_map_key_clear(BTN_TR
);
273 samsung_kbd_mouse_map_key_clear(BTN_TL2
);
276 samsung_kbd_mouse_map_key_clear(BTN_TR2
);
279 samsung_kbd_mouse_map_key_clear(BTN_SELECT
);
282 samsung_kbd_mouse_map_key_clear(BTN_START
);
285 samsung_kbd_mouse_map_key_clear(BTN_MODE
);
288 samsung_kbd_mouse_map_key_clear(BTN_THUMBL
);
291 samsung_kbd_mouse_map_key_clear(BTN_THUMBR
);
294 samsung_kbd_mouse_map_key_clear(0x13f);
301 if (HID_UP_CONSUMER
== (usage
->hid
& HID_USAGE_PAGE
)) {
302 switch (usage
->hid
& HID_USAGE
) {
304 samsung_kbd_mouse_map_key_clear(KEY_MENU
);
307 samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE
);
310 samsung_kbd_mouse_map_key_clear(KEY_BACK
);
315 samsung_kbd_mouse_map_key_clear(KEY_SYSRQ
);
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
)
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
);
335 if (((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_CONSUMER
) && ((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_BUTTON
))
338 switch (usage
->hid
& HID_USAGE
) {
340 samsung_kbd_mouse_map_key_clear(254);
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
)
353 if (!(HID_UP_CONSUMER
== (usage
->hid
& HID_USAGE_PAGE
) ||
354 HID_UP_KEYBOARD
== (usage
->hid
& HID_USAGE_PAGE
)))
357 dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n",
358 usage
->hid
& HID_USAGE
);
360 if (HID_UP_KEYBOARD
== (usage
->hid
& HID_USAGE_PAGE
)) {
361 set_bit(EV_REP
, hi
->input
->evbit
);
362 switch (usage
->hid
& HID_USAGE
) {
364 samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH
);
367 samsung_kbd_mouse_map_key_clear(KEY_102ND
);
369 /* Only for BR keyboard */
371 samsung_kbd_mouse_map_key_clear(KEY_RO
);
378 if (HID_UP_CONSUMER
== (usage
->hid
& HID_USAGE_PAGE
)) {
379 switch (usage
->hid
& HID_USAGE
) {
383 samsung_kbd_mouse_map_key_clear(KEY_MENU
);
386 samsung_kbd_mouse_map_key_clear(KEY_MAIL
);
389 samsung_kbd_mouse_map_key_clear(KEY_WWW
);
392 samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK
);
395 samsung_kbd_mouse_map_key_clear(KEY_SEARCH
);
398 samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE
);
402 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1
);
406 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2
);
410 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4
);
414 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5
);
418 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3
);
422 samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE
);
425 samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN
);
428 samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN
);
431 samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP
);
434 samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP
);
438 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY7
);
442 samsung_kbd_mouse_map_key_clear(KEY_SYSRQ
);
446 samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY9
);
450 samsung_kbd_mouse_map_key_clear(0x2f5);
454 samsung_kbd_mouse_map_key_clear(0x2f6);
458 samsung_kbd_mouse_map_key_clear(0x2f7);
462 samsung_kbd_mouse_map_key_clear(0x2bd);
472 static __u8
*samsung_report_fixup(struct hid_device
*hdev
, __u8
*rdesc
,
475 if (hdev
->product
== USB_DEVICE_ID_SAMSUNG_IR_REMOTE
&& hid_is_usb(hdev
))
476 rdesc
= samsung_irda_report_fixup(hdev
, rdesc
, rsize
);
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
)
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
);
508 static int samsung_probe(struct hid_device
*hdev
,
509 const struct hid_device_id
*id
)
512 unsigned int cmask
= HID_CONNECT_DEFAULT
;
514 ret
= hid_parse(hdev
);
516 hid_err(hdev
, "parse failed\n");
520 if (hdev
->product
== USB_DEVICE_ID_SAMSUNG_IR_REMOTE
) {
521 if (!hid_is_usb(hdev
)) {
525 if (hdev
->rsize
== 184) {
526 /* disable hidinput, force hiddev */
527 cmask
= (cmask
& ~HID_CONNECT_HIDINPUT
) |
528 HID_CONNECT_HIDDEV_FORCE
;
532 ret
= hid_hw_start(hdev
, cmask
);
534 hid_err(hdev
, "hw start failed\n");
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
) },
553 MODULE_DEVICE_TABLE(hid
, samsung_devices
);
555 static struct hid_driver samsung_driver
= {
557 .id_table
= samsung_devices
,
558 .report_fixup
= samsung_report_fixup
,
559 .input_mapping
= samsung_input_mapping
,
560 .probe
= samsung_probe
,
562 module_hid_driver(samsung_driver
);
564 MODULE_DESCRIPTION("HID driver for some samsung \"special\" devices");
565 MODULE_LICENSE("GPL");