hid-creative-sb0540.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * HID driver for the Creative SB0540 receiver
  4. *
  5. * Copyright (C) 2019 Red Hat Inc. All Rights Reserved
  6. *
  7. */
  8. #include <linux/device.h>
  9. #include <linux/hid.h>
  10. #include <linux/module.h>
  11. #include "hid-ids.h"
  12. MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
  13. MODULE_DESCRIPTION("HID Creative SB0540 receiver");
  14. MODULE_LICENSE("GPL");
  15. static const unsigned short creative_sb0540_key_table[] = {
  16. KEY_POWER,
  17. KEY_RESERVED, /* text: 24bit */
  18. KEY_RESERVED, /* 24bit wheel up */
  19. KEY_RESERVED, /* 24bit wheel down */
  20. KEY_RESERVED, /* text: CMSS */
  21. KEY_RESERVED, /* CMSS wheel Up */
  22. KEY_RESERVED, /* CMSS wheel Down */
  23. KEY_RESERVED, /* text: EAX */
  24. KEY_RESERVED, /* EAX wheel up */
  25. KEY_RESERVED, /* EAX wheel down */
  26. KEY_RESERVED, /* text: 3D Midi */
  27. KEY_RESERVED, /* 3D Midi wheel up */
  28. KEY_RESERVED, /* 3D Midi wheel down */
  29. KEY_MUTE,
  30. KEY_VOLUMEUP,
  31. KEY_VOLUMEDOWN,
  32. KEY_UP,
  33. KEY_LEFT,
  34. KEY_RIGHT,
  35. KEY_REWIND,
  36. KEY_OK,
  37. KEY_FASTFORWARD,
  38. KEY_DOWN,
  39. KEY_AGAIN, /* text: Return, symbol: Jump to */
  40. KEY_PLAY, /* text: Start */
  41. KEY_ESC, /* text: Cancel */
  42. KEY_RECORD,
  43. KEY_OPTION,
  44. KEY_MENU, /* text: Display */
  45. KEY_PREVIOUS,
  46. KEY_PLAYPAUSE,
  47. KEY_NEXT,
  48. KEY_SLOW,
  49. KEY_STOP,
  50. KEY_NUMERIC_1,
  51. KEY_NUMERIC_2,
  52. KEY_NUMERIC_3,
  53. KEY_NUMERIC_4,
  54. KEY_NUMERIC_5,
  55. KEY_NUMERIC_6,
  56. KEY_NUMERIC_7,
  57. KEY_NUMERIC_8,
  58. KEY_NUMERIC_9,
  59. KEY_NUMERIC_0
  60. };
  61. /*
  62. * Codes and keys from lirc's
  63. * remotes/creative/lircd.conf.alsa_usb
  64. * order and size must match creative_sb0540_key_table[] above
  65. */
  66. static const unsigned short creative_sb0540_codes[] = {
  67. 0x619E,
  68. 0x916E,
  69. 0x926D,
  70. 0x936C,
  71. 0x718E,
  72. 0x946B,
  73. 0x956A,
  74. 0x8C73,
  75. 0x9669,
  76. 0x9768,
  77. 0x9867,
  78. 0x9966,
  79. 0x9A65,
  80. 0x6E91,
  81. 0x629D,
  82. 0x639C,
  83. 0x7B84,
  84. 0x6B94,
  85. 0x728D,
  86. 0x8778,
  87. 0x817E,
  88. 0x758A,
  89. 0x8D72,
  90. 0x8E71,
  91. 0x8877,
  92. 0x7C83,
  93. 0x738C,
  94. 0x827D,
  95. 0x7689,
  96. 0x7F80,
  97. 0x7986,
  98. 0x7A85,
  99. 0x7D82,
  100. 0x857A,
  101. 0x8B74,
  102. 0x8F70,
  103. 0x906F,
  104. 0x8A75,
  105. 0x847B,
  106. 0x7887,
  107. 0x8976,
  108. 0x837C,
  109. 0x7788,
  110. 0x807F
  111. };
  112. struct creative_sb0540 {
  113. struct input_dev *input_dev;
  114. struct hid_device *hid;
  115. unsigned short keymap[ARRAY_SIZE(creative_sb0540_key_table)];
  116. };
  117. static inline u64 reverse(u64 data, int bits)
  118. {
  119. int i;
  120. u64 c;
  121. c = 0;
  122. for (i = 0; i < bits; i++) {
  123. c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
  124. << (bits - 1 - i);
  125. }
  126. return (c);
  127. }
  128. static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
  129. {
  130. int i;
  131. for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
  132. if (creative_sb0540_codes[i] == keycode)
  133. return creative_sb0540->keymap[i];
  134. }
  135. return 0;
  136. }
  137. static int creative_sb0540_raw_event(struct hid_device *hid,
  138. struct hid_report *report, u8 *data, int len)
  139. {
  140. struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
  141. u64 code, main_code;
  142. int key;
  143. if (len != 6)
  144. return 0;
  145. /* From daemons/hw_hiddev.c sb0540_rec() in lirc */
  146. code = reverse(data[5], 8);
  147. main_code = (code << 8) + ((~code) & 0xff);
  148. /*
  149. * Flip to get values in the same format as
  150. * remotes/creative/lircd.conf.alsa_usb in lirc
  151. */
  152. main_code = ((main_code & 0xff) << 8) +
  153. ((main_code & 0xff00) >> 8);
  154. key = get_key(creative_sb0540, main_code);
  155. if (key == 0 || key == KEY_RESERVED) {
  156. hid_err(hid, "Could not get a key for main_code %llX\n",
  157. main_code);
  158. return 0;
  159. }
  160. input_report_key(creative_sb0540->input_dev, key, 1);
  161. input_report_key(creative_sb0540->input_dev, key, 0);
  162. input_sync(creative_sb0540->input_dev);
  163. /* let hidraw and hiddev handle the report */
  164. return 0;
  165. }
  166. static int creative_sb0540_input_configured(struct hid_device *hid,
  167. struct hid_input *hidinput)
  168. {
  169. struct input_dev *input_dev = hidinput->input;
  170. struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
  171. int i;
  172. creative_sb0540->input_dev = input_dev;
  173. input_dev->keycode = creative_sb0540->keymap;
  174. input_dev->keycodesize = sizeof(unsigned short);
  175. input_dev->keycodemax = ARRAY_SIZE(creative_sb0540->keymap);
  176. input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
  177. memcpy(creative_sb0540->keymap, creative_sb0540_key_table,
  178. sizeof(creative_sb0540->keymap));
  179. for (i = 0; i < ARRAY_SIZE(creative_sb0540_key_table); i++)
  180. set_bit(creative_sb0540->keymap[i], input_dev->keybit);
  181. clear_bit(KEY_RESERVED, input_dev->keybit);
  182. return 0;
  183. }
  184. static int creative_sb0540_input_mapping(struct hid_device *hid,
  185. struct hid_input *hi, struct hid_field *field,
  186. struct hid_usage *usage, unsigned long **bit, int *max)
  187. {
  188. /*
  189. * We are remapping the keys ourselves, so ignore the hid-input
  190. * keymap processing.
  191. */
  192. return -1;
  193. }
  194. static int creative_sb0540_probe(struct hid_device *hid,
  195. const struct hid_device_id *id)
  196. {
  197. int ret;
  198. struct creative_sb0540 *creative_sb0540;
  199. creative_sb0540 = devm_kzalloc(&hid->dev,
  200. sizeof(struct creative_sb0540), GFP_KERNEL);
  201. if (!creative_sb0540)
  202. return -ENOMEM;
  203. creative_sb0540->hid = hid;
  204. /* force input as some remotes bypass the input registration */
  205. hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
  206. hid_set_drvdata(hid, creative_sb0540);
  207. ret = hid_parse(hid);
  208. if (ret) {
  209. hid_err(hid, "parse failed\n");
  210. return ret;
  211. }
  212. ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
  213. if (ret) {
  214. hid_err(hid, "hw start failed\n");
  215. return ret;
  216. }
  217. return ret;
  218. }
  219. static const struct hid_device_id creative_sb0540_devices[] = {
  220. { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
  221. { }
  222. };
  223. MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
  224. static struct hid_driver creative_sb0540_driver = {
  225. .name = "creative-sb0540",
  226. .id_table = creative_sb0540_devices,
  227. .raw_event = creative_sb0540_raw_event,
  228. .input_configured = creative_sb0540_input_configured,
  229. .probe = creative_sb0540_probe,
  230. .input_mapping = creative_sb0540_input_mapping,
  231. };
  232. module_hid_driver(creative_sb0540_driver);