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"); 565