GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / hid / hid-samsung.c
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(&rdesc[175], "\x25\x40\x75\x30\x95\x01", 6) &&
62                         rdesc[182] == 0x40) {
63                 samsung_irda_dev_trace(hdev, 184);
64                 rdesc[176] = 0xff;
65                 rdesc[178] = 0x08;
66                 rdesc[180] = 0x06;
67                 rdesc[182] = 0x42;
68         } else if (*rsize == 203 && !memcmp(&rdesc[192], "\x15\x00\x25\x12", 4)) {
69                 samsung_irda_dev_trace(hdev, 203);
70                 rdesc[193] = 0x01;
71                 rdesc[195] = 0x0f;
72         } else if (*rsize == 135 && !memcmp(&rdesc[124], "\x15\x00\x25\x11", 4)) {
73                 samsung_irda_dev_trace(hdev, 135);
74                 rdesc[125] = 0x01;
75                 rdesc[127] = 0x0e;
76         } else if (*rsize == 171 && !memcmp(&rdesc[160], "\x15\x00\x25\x01", 4)) {
77                 samsung_irda_dev_trace(hdev, 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, 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, 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, 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");