hid-led.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Simple USB RGB LED driver
  4. *
  5. * Copyright 2016 Heiner Kallweit <hkallweit1@gmail.com>
  6. * Based on drivers/hid/hid-thingm.c and
  7. * drivers/usb/misc/usbled.c
  8. */
  9. #include <linux/hid.h>
  10. #include <linux/hidraw.h>
  11. #include <linux/leds.h>
  12. #include <linux/module.h>
  13. #include <linux/mutex.h>
  14. #include "hid-ids.h"
  15. enum hidled_report_type {
  16. RAW_REQUEST,
  17. OUTPUT_REPORT
  18. };
  19. enum hidled_type {
  20. RISO_KAGAKU,
  21. DREAM_CHEEKY,
  22. THINGM,
  23. DELCOM,
  24. LUXAFOR,
  25. };
  26. static unsigned const char riso_kagaku_tbl[] = {
  27. /* R+2G+4B -> riso kagaku color index */
  28. [0] = 0, /* black */
  29. [1] = 2, /* red */
  30. [2] = 1, /* green */
  31. [3] = 5, /* yellow */
  32. [4] = 3, /* blue */
  33. [5] = 6, /* magenta */
  34. [6] = 4, /* cyan */
  35. [7] = 7 /* white */
  36. };
  37. #define RISO_KAGAKU_IX(r, g, b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
  38. union delcom_packet {
  39. __u8 data[8];
  40. struct {
  41. __u8 major_cmd;
  42. __u8 minor_cmd;
  43. __u8 data_lsb;
  44. __u8 data_msb;
  45. } tx;
  46. struct {
  47. __u8 cmd;
  48. } rx;
  49. struct {
  50. __le16 family_code;
  51. __le16 security_code;
  52. __u8 fw_version;
  53. } fw;
  54. };
  55. #define DELCOM_GREEN_LED 0
  56. #define DELCOM_RED_LED 1
  57. #define DELCOM_BLUE_LED 2
  58. struct hidled_device;
  59. struct hidled_rgb;
  60. struct hidled_config {
  61. enum hidled_type type;
  62. const char *name;
  63. const char *short_name;
  64. enum led_brightness max_brightness;
  65. int num_leds;
  66. size_t report_size;
  67. enum hidled_report_type report_type;
  68. int (*init)(struct hidled_device *ldev);
  69. int (*write)(struct led_classdev *cdev, enum led_brightness br);
  70. };
  71. struct hidled_led {
  72. struct led_classdev cdev;
  73. struct hidled_rgb *rgb;
  74. char name[32];
  75. };
  76. struct hidled_rgb {
  77. struct hidled_device *ldev;
  78. struct hidled_led red;
  79. struct hidled_led green;
  80. struct hidled_led blue;
  81. u8 num;
  82. };
  83. struct hidled_device {
  84. const struct hidled_config *config;
  85. struct hid_device *hdev;
  86. struct hidled_rgb *rgb;
  87. u8 *buf;
  88. struct mutex lock;
  89. };
  90. #define MAX_REPORT_SIZE 16
  91. #define to_hidled_led(arg) container_of(arg, struct hidled_led, cdev)
  92. static bool riso_kagaku_switch_green_blue;
  93. module_param(riso_kagaku_switch_green_blue, bool, S_IRUGO | S_IWUSR);
  94. MODULE_PARM_DESC(riso_kagaku_switch_green_blue,
  95. "switch green and blue RGB component for Riso Kagaku devices");
  96. static int hidled_send(struct hidled_device *ldev, __u8 *buf)
  97. {
  98. int ret;
  99. mutex_lock(&ldev->lock);
  100. /*
  101. * buffer provided to hid_hw_raw_request must not be on the stack
  102. * and must not be part of a data structure
  103. */
  104. memcpy(ldev->buf, buf, ldev->config->report_size);
  105. if (ldev->config->report_type == RAW_REQUEST)
  106. ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
  107. ldev->config->report_size,
  108. HID_FEATURE_REPORT,
  109. HID_REQ_SET_REPORT);
  110. else if (ldev->config->report_type == OUTPUT_REPORT)
  111. ret = hid_hw_output_report(ldev->hdev, ldev->buf,
  112. ldev->config->report_size);
  113. else
  114. ret = -EINVAL;
  115. mutex_unlock(&ldev->lock);
  116. if (ret < 0)
  117. return ret;
  118. return ret == ldev->config->report_size ? 0 : -EMSGSIZE;
  119. }
  120. /* reading data is supported for report type RAW_REQUEST only */
  121. static int hidled_recv(struct hidled_device *ldev, __u8 *buf)
  122. {
  123. int ret;
  124. if (ldev->config->report_type != RAW_REQUEST)
  125. return -EINVAL;
  126. mutex_lock(&ldev->lock);
  127. memcpy(ldev->buf, buf, ldev->config->report_size);
  128. ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
  129. ldev->config->report_size,
  130. HID_FEATURE_REPORT,
  131. HID_REQ_SET_REPORT);
  132. if (ret < 0)
  133. goto err;
  134. ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
  135. ldev->config->report_size,
  136. HID_FEATURE_REPORT,
  137. HID_REQ_GET_REPORT);
  138. memcpy(buf, ldev->buf, ldev->config->report_size);
  139. err:
  140. mutex_unlock(&ldev->lock);
  141. return ret < 0 ? ret : 0;
  142. }
  143. static u8 riso_kagaku_index(struct hidled_rgb *rgb)
  144. {
  145. enum led_brightness r, g, b;
  146. r = rgb->red.cdev.brightness;
  147. g = rgb->green.cdev.brightness;
  148. b = rgb->blue.cdev.brightness;
  149. if (riso_kagaku_switch_green_blue)
  150. return RISO_KAGAKU_IX(r, b, g);
  151. else
  152. return RISO_KAGAKU_IX(r, g, b);
  153. }
  154. static int riso_kagaku_write(struct led_classdev *cdev, enum led_brightness br)
  155. {
  156. struct hidled_led *led = to_hidled_led(cdev);
  157. struct hidled_rgb *rgb = led->rgb;
  158. __u8 buf[MAX_REPORT_SIZE] = {};
  159. buf[1] = riso_kagaku_index(rgb);
  160. return hidled_send(rgb->ldev, buf);
  161. }
  162. static int dream_cheeky_write(struct led_classdev *cdev, enum led_brightness br)
  163. {
  164. struct hidled_led *led = to_hidled_led(cdev);
  165. struct hidled_rgb *rgb = led->rgb;
  166. __u8 buf[MAX_REPORT_SIZE] = {};
  167. buf[1] = rgb->red.cdev.brightness;
  168. buf[2] = rgb->green.cdev.brightness;
  169. buf[3] = rgb->blue.cdev.brightness;
  170. buf[7] = 0x1a;
  171. buf[8] = 0x05;
  172. return hidled_send(rgb->ldev, buf);
  173. }
  174. static int dream_cheeky_init(struct hidled_device *ldev)
  175. {
  176. __u8 buf[MAX_REPORT_SIZE] = {};
  177. /* Dream Cheeky magic */
  178. buf[1] = 0x1f;
  179. buf[2] = 0x02;
  180. buf[4] = 0x5f;
  181. buf[7] = 0x1a;
  182. buf[8] = 0x03;
  183. return hidled_send(ldev, buf);
  184. }
  185. static int _thingm_write(struct led_classdev *cdev, enum led_brightness br,
  186. u8 offset)
  187. {
  188. struct hidled_led *led = to_hidled_led(cdev);
  189. __u8 buf[MAX_REPORT_SIZE] = { 1, 'c' };
  190. buf[2] = led->rgb->red.cdev.brightness;
  191. buf[3] = led->rgb->green.cdev.brightness;
  192. buf[4] = led->rgb->blue.cdev.brightness;
  193. buf[7] = led->rgb->num + offset;
  194. return hidled_send(led->rgb->ldev, buf);
  195. }
  196. static int thingm_write_v1(struct led_classdev *cdev, enum led_brightness br)
  197. {
  198. return _thingm_write(cdev, br, 0);
  199. }
  200. static int thingm_write(struct led_classdev *cdev, enum led_brightness br)
  201. {
  202. return _thingm_write(cdev, br, 1);
  203. }
  204. static const struct hidled_config hidled_config_thingm_v1 = {
  205. .name = "ThingM blink(1) v1",
  206. .short_name = "thingm",
  207. .max_brightness = 255,
  208. .num_leds = 1,
  209. .report_size = 9,
  210. .report_type = RAW_REQUEST,
  211. .write = thingm_write_v1,
  212. };
  213. static int thingm_init(struct hidled_device *ldev)
  214. {
  215. __u8 buf[MAX_REPORT_SIZE] = { 1, 'v' };
  216. int ret;
  217. ret = hidled_recv(ldev, buf);
  218. if (ret)
  219. return ret;
  220. /* Check for firmware major version 1 */
  221. if (buf[3] == '1')
  222. ldev->config = &hidled_config_thingm_v1;
  223. return 0;
  224. }
  225. static inline int delcom_get_lednum(const struct hidled_led *led)
  226. {
  227. if (led == &led->rgb->red)
  228. return DELCOM_RED_LED;
  229. else if (led == &led->rgb->green)
  230. return DELCOM_GREEN_LED;
  231. else
  232. return DELCOM_BLUE_LED;
  233. }
  234. static int delcom_enable_led(struct hidled_led *led)
  235. {
  236. union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 12 };
  237. dp.tx.data_lsb = 1 << delcom_get_lednum(led);
  238. dp.tx.data_msb = 0;
  239. return hidled_send(led->rgb->ldev, dp.data);
  240. }
  241. static int delcom_set_pwm(struct hidled_led *led)
  242. {
  243. union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 34 };
  244. dp.tx.data_lsb = delcom_get_lednum(led);
  245. dp.tx.data_msb = led->cdev.brightness;
  246. return hidled_send(led->rgb->ldev, dp.data);
  247. }
  248. static int delcom_write(struct led_classdev *cdev, enum led_brightness br)
  249. {
  250. struct hidled_led *led = to_hidled_led(cdev);
  251. int ret;
  252. /*
  253. * enable LED
  254. * We can't do this in the init function already because the device
  255. * is internally reset later.
  256. */
  257. ret = delcom_enable_led(led);
  258. if (ret)
  259. return ret;
  260. return delcom_set_pwm(led);
  261. }
  262. static int delcom_init(struct hidled_device *ldev)
  263. {
  264. union delcom_packet dp = { .rx.cmd = 104 };
  265. int ret;
  266. ret = hidled_recv(ldev, dp.data);
  267. if (ret)
  268. return ret;
  269. /*
  270. * Several Delcom devices share the same USB VID/PID
  271. * Check for family id 2 for Visual Signal Indicator
  272. */
  273. return le16_to_cpu(dp.fw.family_code) == 2 ? 0 : -ENODEV;
  274. }
  275. static int luxafor_write(struct led_classdev *cdev, enum led_brightness br)
  276. {
  277. struct hidled_led *led = to_hidled_led(cdev);
  278. __u8 buf[MAX_REPORT_SIZE] = { [1] = 1 };
  279. buf[2] = led->rgb->num + 1;
  280. buf[3] = led->rgb->red.cdev.brightness;
  281. buf[4] = led->rgb->green.cdev.brightness;
  282. buf[5] = led->rgb->blue.cdev.brightness;
  283. return hidled_send(led->rgb->ldev, buf);
  284. }
  285. static const struct hidled_config hidled_configs[] = {
  286. {
  287. .type = RISO_KAGAKU,
  288. .name = "Riso Kagaku Webmail Notifier",
  289. .short_name = "riso_kagaku",
  290. .max_brightness = 1,
  291. .num_leds = 1,
  292. .report_size = 6,
  293. .report_type = OUTPUT_REPORT,
  294. .write = riso_kagaku_write,
  295. },
  296. {
  297. .type = DREAM_CHEEKY,
  298. .name = "Dream Cheeky Webmail Notifier",
  299. .short_name = "dream_cheeky",
  300. .max_brightness = 31,
  301. .num_leds = 1,
  302. .report_size = 9,
  303. .report_type = RAW_REQUEST,
  304. .init = dream_cheeky_init,
  305. .write = dream_cheeky_write,
  306. },
  307. {
  308. .type = THINGM,
  309. .name = "ThingM blink(1)",
  310. .short_name = "thingm",
  311. .max_brightness = 255,
  312. .num_leds = 2,
  313. .report_size = 9,
  314. .report_type = RAW_REQUEST,
  315. .init = thingm_init,
  316. .write = thingm_write,
  317. },
  318. {
  319. .type = DELCOM,
  320. .name = "Delcom Visual Signal Indicator G2",
  321. .short_name = "delcom",
  322. .max_brightness = 100,
  323. .num_leds = 1,
  324. .report_size = 8,
  325. .report_type = RAW_REQUEST,
  326. .init = delcom_init,
  327. .write = delcom_write,
  328. },
  329. {
  330. .type = LUXAFOR,
  331. .name = "Greynut Luxafor",
  332. .short_name = "luxafor",
  333. .max_brightness = 255,
  334. .num_leds = 6,
  335. .report_size = 9,
  336. .report_type = OUTPUT_REPORT,
  337. .write = luxafor_write,
  338. },
  339. };
  340. static int hidled_init_led(struct hidled_led *led, const char *color_name,
  341. struct hidled_rgb *rgb, unsigned int minor)
  342. {
  343. const struct hidled_config *config = rgb->ldev->config;
  344. if (config->num_leds > 1)
  345. snprintf(led->name, sizeof(led->name), "%s%u:%s:led%u",
  346. config->short_name, minor, color_name, rgb->num);
  347. else
  348. snprintf(led->name, sizeof(led->name), "%s%u:%s",
  349. config->short_name, minor, color_name);
  350. led->cdev.name = led->name;
  351. led->cdev.max_brightness = config->max_brightness;
  352. led->cdev.brightness_set_blocking = config->write;
  353. led->cdev.flags = LED_HW_PLUGGABLE;
  354. led->rgb = rgb;
  355. return devm_led_classdev_register(&rgb->ldev->hdev->dev, &led->cdev);
  356. }
  357. static int hidled_init_rgb(struct hidled_rgb *rgb, unsigned int minor)
  358. {
  359. int ret;
  360. /* Register the red diode */
  361. ret = hidled_init_led(&rgb->red, "red", rgb, minor);
  362. if (ret)
  363. return ret;
  364. /* Register the green diode */
  365. ret = hidled_init_led(&rgb->green, "green", rgb, minor);
  366. if (ret)
  367. return ret;
  368. /* Register the blue diode */
  369. return hidled_init_led(&rgb->blue, "blue", rgb, minor);
  370. }
  371. static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id)
  372. {
  373. struct hidled_device *ldev;
  374. unsigned int minor;
  375. int ret, i;
  376. ldev = devm_kzalloc(&hdev->dev, sizeof(*ldev), GFP_KERNEL);
  377. if (!ldev)
  378. return -ENOMEM;
  379. ldev->buf = devm_kmalloc(&hdev->dev, MAX_REPORT_SIZE, GFP_KERNEL);
  380. if (!ldev->buf)
  381. return -ENOMEM;
  382. ret = hid_parse(hdev);
  383. if (ret)
  384. return ret;
  385. ldev->hdev = hdev;
  386. mutex_init(&ldev->lock);
  387. for (i = 0; !ldev->config && i < ARRAY_SIZE(hidled_configs); i++)
  388. if (hidled_configs[i].type == id->driver_data)
  389. ldev->config = &hidled_configs[i];
  390. if (!ldev->config)
  391. return -EINVAL;
  392. if (ldev->config->init) {
  393. ret = ldev->config->init(ldev);
  394. if (ret)
  395. return ret;
  396. }
  397. ldev->rgb = devm_kcalloc(&hdev->dev, ldev->config->num_leds,
  398. sizeof(struct hidled_rgb), GFP_KERNEL);
  399. if (!ldev->rgb)
  400. return -ENOMEM;
  401. ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
  402. if (ret)
  403. return ret;
  404. minor = ((struct hidraw *) hdev->hidraw)->minor;
  405. for (i = 0; i < ldev->config->num_leds; i++) {
  406. ldev->rgb[i].ldev = ldev;
  407. ldev->rgb[i].num = i;
  408. ret = hidled_init_rgb(&ldev->rgb[i], minor);
  409. if (ret) {
  410. hid_hw_stop(hdev);
  411. return ret;
  412. }
  413. }
  414. hid_info(hdev, "%s initialized\n", ldev->config->name);
  415. return 0;
  416. }
  417. static const struct hid_device_id hidled_table[] = {
  418. { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU,
  419. USB_DEVICE_ID_RI_KA_WEBMAIL), .driver_data = RISO_KAGAKU },
  420. { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
  421. USB_DEVICE_ID_DREAM_CHEEKY_WN), .driver_data = DREAM_CHEEKY },
  422. { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
  423. USB_DEVICE_ID_DREAM_CHEEKY_FA), .driver_data = DREAM_CHEEKY },
  424. { HID_USB_DEVICE(USB_VENDOR_ID_THINGM,
  425. USB_DEVICE_ID_BLINK1), .driver_data = THINGM },
  426. { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM,
  427. USB_DEVICE_ID_DELCOM_VISUAL_IND), .driver_data = DELCOM },
  428. { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP,
  429. USB_DEVICE_ID_LUXAFOR), .driver_data = LUXAFOR },
  430. { }
  431. };
  432. MODULE_DEVICE_TABLE(hid, hidled_table);
  433. static struct hid_driver hidled_driver = {
  434. .name = "hid-led",
  435. .probe = hidled_probe,
  436. .id_table = hidled_table,
  437. };
  438. module_hid_driver(hidled_driver);
  439. MODULE_LICENSE("GPL");
  440. MODULE_AUTHOR("Heiner Kallweit <hkallweit1@gmail.com>");
  441. MODULE_DESCRIPTION("Simple USB RGB LED driver");