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