hid-samsung.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. * HID driver for some samsung "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. * Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
  11. *
  12. *
  13. * This driver supports several HID devices:
  14. *
  15. * [0419:0001] Samsung IrDA remote controller (reports as Cypress USB Mouse).
  16. * various hid report fixups for different variants.
  17. *
  18. * [0419:0600] Creative Desktop Wireless 6000 keyboard/mouse combo
  19. * several key mappings used from the consumer usage page
  20. * deviate from the USB HUT 1.12 standard.
  21. *
  22. */
  23. /*
  24. * This program is free software; you can redistribute it and/or modify it
  25. * under the terms of the GNU General Public License as published by the Free
  26. * Software Foundation; either version 2 of the License, or (at your option)
  27. * any later version.
  28. */
  29. #include <linux/device.h>
  30. #include <linux/usb.h>
  31. #include <linux/hid.h>
  32. #include <linux/module.h>
  33. #include "hid-ids.h"
  34. /*
  35. * There are several variants for 0419:0001:
  36. *
  37. * 1. 184 byte report descriptor
  38. * Vendor specific report #4 has a size of 48 bit,
  39. * and therefore is not accepted when inspecting the descriptors.
  40. * As a workaround we reinterpret the report as:
  41. * Variable type, count 6, size 8 bit, log. maximum 255
  42. * The burden to reconstruct the data is moved into user space.
  43. *
  44. * 2. 203 byte report descriptor
  45. * Report #4 has an array field with logical range 0..18 instead of 1..15.
  46. *
  47. * 3. 135 byte report descriptor
  48. * Report #4 has an array field with logical range 0..17 instead of 1..14.
  49. *
  50. * 4. 171 byte report descriptor
  51. * Report #3 has an array field with logical range 0..1 instead of 1..3.
  52. */
  53. static inline void samsung_irda_dev_trace(struct hid_device *hdev,
  54. unsigned int rsize)
  55. {
  56. hid_info(hdev, "fixing up Samsung IrDA %d byte report descriptor\n",
  57. rsize);
  58. }
  59. static __u8 *samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  60. unsigned int *rsize)
  61. {
  62. if (*rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
  63. rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
  64. rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
  65. rdesc[182] == 0x40) {
  66. samsung_irda_dev_trace(hdev, 184);
  67. rdesc[176] = 0xff;
  68. rdesc[178] = 0x08;
  69. rdesc[180] = 0x06;
  70. rdesc[182] = 0x42;
  71. } else
  72. if (*rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
  73. rdesc[194] == 0x25 && rdesc[195] == 0x12) {
  74. samsung_irda_dev_trace(hdev, 203);
  75. rdesc[193] = 0x1;
  76. rdesc[195] = 0xf;
  77. } else
  78. if (*rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
  79. rdesc[126] == 0x25 && rdesc[127] == 0x11) {
  80. samsung_irda_dev_trace(hdev, 135);
  81. rdesc[125] = 0x1;
  82. rdesc[127] = 0xe;
  83. } else
  84. if (*rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 &&
  85. rdesc[162] == 0x25 && rdesc[163] == 0x01) {
  86. samsung_irda_dev_trace(hdev, 171);
  87. rdesc[161] = 0x1;
  88. rdesc[163] = 0x3;
  89. }
  90. return rdesc;
  91. }
  92. #define samsung_kbd_mouse_map_key_clear(c) \
  93. hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
  94. static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev,
  95. struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
  96. unsigned long **bit, int *max)
  97. {
  98. struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
  99. unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
  100. if (1 != ifnum || HID_UP_CONSUMER != (usage->hid & HID_USAGE_PAGE))
  101. return 0;
  102. dbg_hid("samsung wireless keyboard/mouse input mapping event [0x%x]\n",
  103. usage->hid & HID_USAGE);
  104. switch (usage->hid & HID_USAGE) {
  105. /* report 2 */
  106. case 0x183: samsung_kbd_mouse_map_key_clear(KEY_MEDIA); break;
  107. case 0x195: samsung_kbd_mouse_map_key_clear(KEY_EMAIL); break;
  108. case 0x196: samsung_kbd_mouse_map_key_clear(KEY_CALC); break;
  109. case 0x197: samsung_kbd_mouse_map_key_clear(KEY_COMPUTER); break;
  110. case 0x22b: samsung_kbd_mouse_map_key_clear(KEY_SEARCH); break;
  111. case 0x22c: samsung_kbd_mouse_map_key_clear(KEY_WWW); break;
  112. case 0x22d: samsung_kbd_mouse_map_key_clear(KEY_BACK); break;
  113. case 0x22e: samsung_kbd_mouse_map_key_clear(KEY_FORWARD); break;
  114. case 0x22f: samsung_kbd_mouse_map_key_clear(KEY_FAVORITES); break;
  115. case 0x230: samsung_kbd_mouse_map_key_clear(KEY_REFRESH); break;
  116. case 0x231: samsung_kbd_mouse_map_key_clear(KEY_STOP); break;
  117. default:
  118. return 0;
  119. }
  120. return 1;
  121. }
  122. static int samsung_kbd_input_mapping(struct hid_device *hdev,
  123. struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
  124. unsigned long **bit, int *max)
  125. {
  126. if (!(HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE) ||
  127. HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)))
  128. return 0;
  129. dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n",
  130. usage->hid & HID_USAGE);
  131. if (HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)) {
  132. switch (usage->hid & HID_USAGE) {
  133. set_bit(EV_REP, hi->input->evbit);
  134. /* Only for UK keyboard */
  135. /* key found */
  136. #ifdef CONFIG_HID_KK_UPGRADE
  137. case 0x32: samsung_kbd_mouse_map_key_clear(KEY_KBDILLUMTOGGLE); break;
  138. case 0x64: samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH); break;
  139. #else
  140. case 0x32: samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH); break;
  141. case 0x64: samsung_kbd_mouse_map_key_clear(KEY_102ND); break;
  142. #endif
  143. /* Only for BR keyboard */
  144. case 0x87: samsung_kbd_mouse_map_key_clear(KEY_RO); break;
  145. default:
  146. return 0;
  147. }
  148. }
  149. if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) {
  150. switch (usage->hid & HID_USAGE) {
  151. /* report 2 */
  152. /* MENU */
  153. case 0x040: samsung_kbd_mouse_map_key_clear(KEY_MENU); break;
  154. case 0x18a: samsung_kbd_mouse_map_key_clear(KEY_MAIL); break;
  155. case 0x196: samsung_kbd_mouse_map_key_clear(KEY_WWW); break;
  156. case 0x19e: samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK); break;
  157. case 0x221: samsung_kbd_mouse_map_key_clear(KEY_SEARCH); break;
  158. case 0x223: samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE); break;
  159. /* RECENTAPPS */
  160. case 0x301: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1); break;
  161. /* APPLICATION */
  162. case 0x302: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2); break;
  163. /* Voice search */
  164. case 0x305: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4); break;
  165. /* QPANEL on/off */
  166. case 0x306: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5); break;
  167. /* SIP on/off */
  168. case 0x307: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3); break;
  169. /* LANG */
  170. case 0x308: samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE); break;
  171. case 0x30a: samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN); break;
  172. case 0x30b: samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP); break;
  173. default:
  174. return 0;
  175. }
  176. }
  177. return 1;
  178. }
  179. static int samsung_gamepad_input_mapping(struct hid_device *hdev,
  180. struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
  181. unsigned long **bit, int *max)
  182. {
  183. if (!(HID_UP_BUTTON == (usage->hid & HID_USAGE_PAGE) ||
  184. HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)))
  185. return 0;
  186. dbg_hid("samsung wireless gamepad input mapping event [0x%x], %ld, %ld, [0x%x]\n",
  187. usage->hid & HID_USAGE, hi->input->evbit[0], hi->input->absbit[0], usage->hid & HID_USAGE_PAGE);
  188. if (HID_UP_BUTTON == (usage->hid & HID_USAGE_PAGE)) {
  189. switch(usage->hid & HID_USAGE) {
  190. case 0x01: samsung_kbd_mouse_map_key_clear(BTN_A); break;
  191. case 0x02: samsung_kbd_mouse_map_key_clear(BTN_B); break;
  192. case 0x03: samsung_kbd_mouse_map_key_clear(BTN_C); break;
  193. case 0x04: samsung_kbd_mouse_map_key_clear(BTN_X); break;
  194. case 0x05: samsung_kbd_mouse_map_key_clear(BTN_Y); break;
  195. case 0x06: samsung_kbd_mouse_map_key_clear(BTN_Z); break;
  196. case 0x07: samsung_kbd_mouse_map_key_clear(BTN_TL); break;
  197. case 0x08: samsung_kbd_mouse_map_key_clear(BTN_TR); break;
  198. case 0x09: samsung_kbd_mouse_map_key_clear(BTN_TL2); break;
  199. case 0x0a: samsung_kbd_mouse_map_key_clear(BTN_TR2); break;
  200. case 0x0b: samsung_kbd_mouse_map_key_clear(BTN_SELECT); break;
  201. case 0x0c: samsung_kbd_mouse_map_key_clear(BTN_START); break;
  202. case 0x0d: samsung_kbd_mouse_map_key_clear(BTN_MODE); break;
  203. case 0x0e: samsung_kbd_mouse_map_key_clear(BTN_THUMBL); break;
  204. case 0x0f: samsung_kbd_mouse_map_key_clear(BTN_THUMBR); break;
  205. case 0x10: samsung_kbd_mouse_map_key_clear(BTN_GAME); break;
  206. default:
  207. return 0;
  208. }
  209. }
  210. if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) {
  211. switch (usage->hid & HID_USAGE) {
  212. case 0x040: samsung_kbd_mouse_map_key_clear(KEY_MENU); break;
  213. case 0x223: samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE); break;
  214. case 0x224: samsung_kbd_mouse_map_key_clear(KEY_BACK); break;
  215. /* Screen Capture */
  216. case 0x303: samsung_kbd_mouse_map_key_clear(KEY_SYSRQ); break;
  217. default:
  218. return 0;
  219. }
  220. }
  221. return 1;
  222. }
  223. static int samsung_actionmouse_input_mapping(struct hid_device *hdev,
  224. struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
  225. unsigned long **bit, int *max)
  226. {
  227. dbg_hid("samsung wireless actionmouse input mapping event [0x%x], [0x%x], %ld, %ld, [0x%x]\n",
  228. usage->hid, usage->hid & HID_USAGE, hi->input->evbit[0], hi->input->absbit[0], usage->hid & HID_USAGE_PAGE);
  229. if(((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) && ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON))
  230. return 0;
  231. switch (usage->hid & HID_USAGE) {
  232. case 0x301: samsung_kbd_mouse_map_key_clear(KEY_RECENT); break;
  233. default:
  234. return 0;
  235. }
  236. return 1;
  237. }
  238. static int samsung_universal_kbd_input_mapping(struct hid_device *hdev,
  239. struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
  240. unsigned long **bit, int *max)
  241. {
  242. if (!(HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE) ||
  243. HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)))
  244. return 0;
  245. dbg_hid("samsung wireless keyboard input mapping event [0x%x]\n",
  246. usage->hid & HID_USAGE);
  247. if (HID_UP_KEYBOARD == (usage->hid & HID_USAGE_PAGE)) {
  248. switch (usage->hid & HID_USAGE) {
  249. set_bit(EV_REP, hi->input->evbit);
  250. /* Only for UK keyboard */
  251. /* key found */
  252. #ifdef CONFIG_HID_KK_UPGRADE
  253. case 0x32: samsung_kbd_mouse_map_key_clear(KEY_KBDILLUMTOGGLE); break;
  254. case 0x64: samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH); break;
  255. #else
  256. case 0x32: samsung_kbd_mouse_map_key_clear(KEY_BACKSLASH); break;
  257. case 0x64: samsung_kbd_mouse_map_key_clear(KEY_102ND); break;
  258. #endif
  259. /* Only for BR keyboard */
  260. case 0x87: samsung_kbd_mouse_map_key_clear(KEY_RO); break;
  261. default:
  262. return 0;
  263. }
  264. }
  265. if (HID_UP_CONSUMER == (usage->hid & HID_USAGE_PAGE)) {
  266. switch (usage->hid & HID_USAGE) {
  267. /* report 2 */
  268. /* MENU */
  269. case 0x040: samsung_kbd_mouse_map_key_clear(KEY_MENU); break;
  270. case 0x18a: samsung_kbd_mouse_map_key_clear(KEY_MAIL); break;
  271. case 0x196: samsung_kbd_mouse_map_key_clear(KEY_WWW); break;
  272. case 0x19e: samsung_kbd_mouse_map_key_clear(KEY_SCREENLOCK); break;
  273. case 0x221: samsung_kbd_mouse_map_key_clear(KEY_SEARCH); break;
  274. case 0x223: samsung_kbd_mouse_map_key_clear(KEY_HOMEPAGE); break;
  275. /* RECENTAPPS */
  276. case 0x301: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY1); break;
  277. /* APPLICATION */
  278. case 0x302: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY2); break;
  279. /* Voice search */
  280. case 0x305: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY4); break;
  281. /* QPANEL on/off */
  282. case 0x306: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY5); break;
  283. /* SIP on/off */
  284. case 0x307: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY3); break;
  285. /* LANG */
  286. case 0x308: samsung_kbd_mouse_map_key_clear(KEY_LANGUAGE); break;
  287. case 0x30a: samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN); break;
  288. case 0x070: samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSDOWN); break;
  289. case 0x30b: samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP); break;
  290. case 0x06f: samsung_kbd_mouse_map_key_clear(KEY_BRIGHTNESSUP); break;
  291. /* S-Finder */
  292. case 0x304: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY7); break;
  293. /* Screen Capture */
  294. case 0x303: samsung_kbd_mouse_map_key_clear(KEY_SYSRQ); break;
  295. /* Multi Window */
  296. case 0x309: samsung_kbd_mouse_map_key_clear(BTN_TRIGGER_HAPPY9); break;
  297. default:
  298. return 0;
  299. }
  300. }
  301. return 1;
  302. }
  303. static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  304. unsigned int *rsize)
  305. {
  306. if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product)
  307. rdesc = samsung_irda_report_fixup(hdev, rdesc, rsize);
  308. return rdesc;
  309. }
  310. static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi,
  311. struct hid_field *field, struct hid_usage *usage,
  312. unsigned long **bit, int *max)
  313. {
  314. int ret = 0;
  315. if (USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE == hdev->product)
  316. ret = samsung_kbd_mouse_input_mapping(hdev,
  317. hi, field, usage, bit, max);
  318. else if (USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD == hdev->product)
  319. ret = samsung_kbd_input_mapping(hdev,
  320. hi, field, usage, bit, max);
  321. else if(USB_DEVICE_ID_SAMSUNG_WIRELESS_GAMEPAD == hdev->product)
  322. ret = samsung_gamepad_input_mapping(hdev,
  323. hi, field, usage, bit, max);
  324. else if(USB_DEVICE_ID_SAMSUNG_WIRELESS_ACTIONMOUSE == hdev->product)
  325. ret = samsung_actionmouse_input_mapping(hdev,
  326. hi, field, usage, bit, max);
  327. else if(USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD == hdev->product)
  328. ret = samsung_universal_kbd_input_mapping(hdev,
  329. hi, field, usage, bit, max);
  330. return ret;
  331. }
  332. static int samsung_probe(struct hid_device *hdev,
  333. const struct hid_device_id *id)
  334. {
  335. int ret;
  336. unsigned int cmask = HID_CONNECT_DEFAULT;
  337. ret = hid_parse(hdev);
  338. if (ret) {
  339. hid_err(hdev, "parse failed\n");
  340. goto err_free;
  341. }
  342. if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product) {
  343. if (hdev->rsize == 184) {
  344. /* disable hidinput, force hiddev */
  345. cmask = (cmask & ~HID_CONNECT_HIDINPUT) |
  346. HID_CONNECT_HIDDEV_FORCE;
  347. }
  348. }
  349. ret = hid_hw_start(hdev, cmask);
  350. if (ret) {
  351. hid_err(hdev, "hw start failed\n");
  352. goto err_free;
  353. }
  354. return 0;
  355. err_free:
  356. return ret;
  357. }
  358. static const struct hid_device_id samsung_devices[] = {
  359. { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
  360. { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
  361. { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD) },
  362. { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_GAMEPAD) },
  363. { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_ACTIONMOUSE) },
  364. { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SAMSUNG_ELECTRONICS, USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD) },
  365. { }
  366. };
  367. MODULE_DEVICE_TABLE(hid, samsung_devices);
  368. static struct hid_driver samsung_driver = {
  369. .name = "samsung",
  370. .id_table = samsung_devices,
  371. .report_fixup = samsung_report_fixup,
  372. .input_mapping = samsung_input_mapping,
  373. .probe = samsung_probe,
  374. };
  375. static int __init samsung_init(void)
  376. {
  377. return hid_register_driver(&samsung_driver);
  378. }
  379. static void __exit samsung_exit(void)
  380. {
  381. hid_unregister_driver(&samsung_driver);
  382. }
  383. module_init(samsung_init);
  384. module_exit(samsung_exit);
  385. MODULE_LICENSE("GPL");