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 struct mutex lock; 104 }; 105 106 #define MAX_REPORT_SIZE 16 107 108 #define to_hidled_led(arg) container_of(arg, struct hidled_led, cdev) 109 110 static bool riso_kagaku_switch_green_blue; 111 module_param(riso_kagaku_switch_green_blue, bool, S_IRUGO | S_IWUSR); 112 MODULE_PARM_DESC(riso_kagaku_switch_green_blue, 113 "switch green and blue RGB component for Riso Kagaku devices"); 114 115 static int hidled_send(struct hidled_device *ldev, __u8 *buf) 116 { 117 int ret; 118 119 mutex_lock(&ldev->lock); 120 121 if (ldev->config->report_type == RAW_REQUEST) 122 ret = hid_hw_raw_request(ldev->hdev, buf[0], buf, 123 ldev->config->report_size, 124 HID_FEATURE_REPORT, 125 HID_REQ_SET_REPORT); 126 else if (ldev->config->report_type == OUTPUT_REPORT) 127 ret = hid_hw_output_report(ldev->hdev, buf, 128 ldev->config->report_size); 129 else 130 ret = -EINVAL; 131 132 mutex_unlock(&ldev->lock); 133 134 if (ret < 0) 135 return ret; 136 137 return ret == ldev->config->report_size ? 0 : -EMSGSIZE; 138 } 139 140 /* reading data is supported for report type RAW_REQUEST only */ 141 static int hidled_recv(struct hidled_device *ldev, __u8 *buf) 142 { 143 int ret; 144 145 if (ldev->config->report_type != RAW_REQUEST) 146 return -EINVAL; 147 148 mutex_lock(&ldev->lock); 149 150 ret = hid_hw_raw_request(ldev->hdev, buf[0], buf, 151 ldev->config->report_size, 152 HID_FEATURE_REPORT, 153 HID_REQ_SET_REPORT); 154 if (ret < 0) 155 goto err; 156 157 ret = hid_hw_raw_request(ldev->hdev, buf[0], buf, 158 ldev->config->report_size, 159 HID_FEATURE_REPORT, 160 HID_REQ_GET_REPORT); 161 err: 162 mutex_unlock(&ldev->lock); 163 164 return ret < 0 ? ret : 0; 165 } 166 167 static u8 riso_kagaku_index(struct hidled_rgb *rgb) 168 { 169 enum led_brightness r, g, b; 170 171 r = rgb->red.cdev.brightness; 172 g = rgb->green.cdev.brightness; 173 b = rgb->blue.cdev.brightness; 174 175 if (riso_kagaku_switch_green_blue) 176 return RISO_KAGAKU_IX(r, b, g); 177 else 178 return RISO_KAGAKU_IX(r, g, b); 179 } 180 181 static int riso_kagaku_write(struct led_classdev *cdev, enum led_brightness br) 182 { 183 struct hidled_led *led = to_hidled_led(cdev); 184 struct hidled_rgb *rgb = led->rgb; 185 __u8 buf[MAX_REPORT_SIZE] = {}; 186 187 buf[1] = riso_kagaku_index(rgb); 188 189 return hidled_send(rgb->ldev, buf); 190 } 191 192 static int dream_cheeky_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] = rgb->red.cdev.brightness; 199 buf[2] = rgb->green.cdev.brightness; 200 buf[3] = rgb->blue.cdev.brightness; 201 buf[7] = 0x1a; 202 buf[8] = 0x05; 203 204 return hidled_send(rgb->ldev, buf); 205 } 206 207 static int dream_cheeky_init(struct hidled_device *ldev) 208 { 209 __u8 buf[MAX_REPORT_SIZE] = {}; 210 211 /* Dream Cheeky magic */ 212 buf[1] = 0x1f; 213 buf[2] = 0x02; 214 buf[4] = 0x5f; 215 buf[7] = 0x1a; 216 buf[8] = 0x03; 217 218 return hidled_send(ldev, buf); 219 } 220 221 static int _thingm_write(struct led_classdev *cdev, enum led_brightness br, 222 u8 offset) 223 { 224 struct hidled_led *led = to_hidled_led(cdev); 225 __u8 buf[MAX_REPORT_SIZE] = { 1, 'c' }; 226 227 buf[2] = led->rgb->red.cdev.brightness; 228 buf[3] = led->rgb->green.cdev.brightness; 229 buf[4] = led->rgb->blue.cdev.brightness; 230 buf[7] = led->rgb->num + offset; 231 232 return hidled_send(led->rgb->ldev, buf); 233 } 234 235 static int thingm_write_v1(struct led_classdev *cdev, enum led_brightness br) 236 { 237 return _thingm_write(cdev, br, 0); 238 } 239 240 static int thingm_write(struct led_classdev *cdev, enum led_brightness br) 241 { 242 return _thingm_write(cdev, br, 1); 243 } 244 245 static const struct hidled_config hidled_config_thingm_v1 = { 246 .name = "ThingM blink(1) v1", 247 .short_name = "thingm", 248 .max_brightness = 255, 249 .num_leds = 1, 250 .report_size = 9, 251 .report_type = RAW_REQUEST, 252 .write = thingm_write_v1, 253 }; 254 255 static int thingm_init(struct hidled_device *ldev) 256 { 257 __u8 buf[MAX_REPORT_SIZE] = { 1, 'v' }; 258 int ret; 259 260 ret = hidled_recv(ldev, buf); 261 if (ret) 262 return ret; 263 264 /* Check for firmware major version 1 */ 265 if (buf[3] == '1') 266 ldev->config = &hidled_config_thingm_v1; 267 268 return 0; 269 } 270 271 static inline int delcom_get_lednum(const struct hidled_led *led) 272 { 273 if (led == &led->rgb->red) 274 return DELCOM_RED_LED; 275 else if (led == &led->rgb->green) 276 return DELCOM_GREEN_LED; 277 else 278 return DELCOM_BLUE_LED; 279 } 280 281 static int delcom_enable_led(struct hidled_led *led) 282 { 283 union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 12 }; 284 285 dp.tx.data_lsb = 1 << delcom_get_lednum(led); 286 dp.tx.data_msb = 0; 287 288 return hidled_send(led->rgb->ldev, dp.data); 289 } 290 291 static int delcom_set_pwm(struct hidled_led *led) 292 { 293 union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 34 }; 294 295 dp.tx.data_lsb = delcom_get_lednum(led); 296 dp.tx.data_msb = led->cdev.brightness; 297 298 return hidled_send(led->rgb->ldev, dp.data); 299 } 300 301 static int delcom_write(struct led_classdev *cdev, enum led_brightness br) 302 { 303 struct hidled_led *led = to_hidled_led(cdev); 304 int ret; 305 306 /* 307 * enable LED 308 * We can't do this in the init function already because the device 309 * is internally reset later. 310 */ 311 ret = delcom_enable_led(led); 312 if (ret) 313 return ret; 314 315 return delcom_set_pwm(led); 316 } 317 318 static int delcom_init(struct hidled_device *ldev) 319 { 320 union delcom_packet dp = { .rx.cmd = 104 }; 321 int ret; 322 323 ret = hidled_recv(ldev, dp.data); 324 if (ret) 325 return ret; 326 /* 327 * Several Delcom devices share the same USB VID/PID 328 * Check for family id 2 for Visual Signal Indicator 329 */ 330 return le16_to_cpu(dp.fw.family_code) == 2 ? 0 : -ENODEV; 331 } 332 333 static int luxafor_write(struct led_classdev *cdev, enum led_brightness br) 334 { 335 struct hidled_led *led = to_hidled_led(cdev); 336 __u8 buf[MAX_REPORT_SIZE] = { [1] = 1 }; 337 338 buf[2] = led->rgb->num + 1; 339 buf[3] = led->rgb->red.cdev.brightness; 340 buf[4] = led->rgb->green.cdev.brightness; 341 buf[5] = led->rgb->blue.cdev.brightness; 342 343 return hidled_send(led->rgb->ldev, buf); 344 } 345 346 static const struct hidled_config hidled_configs[] = { 347 { 348 .type = RISO_KAGAKU, 349 .name = "Riso Kagaku Webmail Notifier", 350 .short_name = "riso_kagaku", 351 .max_brightness = 1, 352 .num_leds = 1, 353 .report_size = 6, 354 .report_type = OUTPUT_REPORT, 355 .write = riso_kagaku_write, 356 }, 357 { 358 .type = DREAM_CHEEKY, 359 .name = "Dream Cheeky Webmail Notifier", 360 .short_name = "dream_cheeky", 361 .max_brightness = 31, 362 .num_leds = 1, 363 .report_size = 9, 364 .report_type = RAW_REQUEST, 365 .init = dream_cheeky_init, 366 .write = dream_cheeky_write, 367 }, 368 { 369 .type = THINGM, 370 .name = "ThingM blink(1)", 371 .short_name = "thingm", 372 .max_brightness = 255, 373 .num_leds = 2, 374 .report_size = 9, 375 .report_type = RAW_REQUEST, 376 .init = thingm_init, 377 .write = thingm_write, 378 }, 379 { 380 .type = DELCOM, 381 .name = "Delcom Visual Signal Indicator G2", 382 .short_name = "delcom", 383 .max_brightness = 100, 384 .num_leds = 1, 385 .report_size = 8, 386 .report_type = RAW_REQUEST, 387 .init = delcom_init, 388 .write = delcom_write, 389 }, 390 { 391 .type = LUXAFOR, 392 .name = "Greynut Luxafor", 393 .short_name = "luxafor", 394 .max_brightness = 255, 395 .num_leds = 6, 396 .report_size = 9, 397 .report_type = OUTPUT_REPORT, 398 .write = luxafor_write, 399 }, 400 }; 401 402 static int hidled_init_led(struct hidled_led *led, const char *color_name, 403 struct hidled_rgb *rgb, unsigned int minor) 404 { 405 const struct hidled_config *config = rgb->ldev->config; 406 407 if (config->num_leds > 1) 408 snprintf(led->name, sizeof(led->name), "%s%u:%s:led%u", 409 config->short_name, minor, color_name, rgb->num); 410 else 411 snprintf(led->name, sizeof(led->name), "%s%u:%s", 412 config->short_name, minor, color_name); 413 led->cdev.name = led->name; 414 led->cdev.max_brightness = config->max_brightness; 415 led->cdev.brightness_set_blocking = config->write; 416 led->cdev.flags = LED_HW_PLUGGABLE; 417 led->rgb = rgb; 418 419 return devm_led_classdev_register(&rgb->ldev->hdev->dev, &led->cdev); 420 } 421 422 static int hidled_init_rgb(struct hidled_rgb *rgb, unsigned int minor) 423 { 424 int ret; 425 426 /* Register the red diode */ 427 ret = hidled_init_led(&rgb->red, "red", rgb, minor); 428 if (ret) 429 return ret; 430 431 /* Register the green diode */ 432 ret = hidled_init_led(&rgb->green, "green", rgb, minor); 433 if (ret) 434 return ret; 435 436 /* Register the blue diode */ 437 return hidled_init_led(&rgb->blue, "blue", rgb, minor); 438 } 439 440 static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id) 441 { 442 struct hidled_device *ldev; 443 unsigned int minor; 444 int ret, i; 445 446 ldev = devm_kzalloc(&hdev->dev, sizeof(*ldev), GFP_KERNEL); 447 if (!ldev) 448 return -ENOMEM; 449 450 ret = hid_parse(hdev); 451 if (ret) 452 return ret; 453 454 ldev->hdev = hdev; 455 mutex_init(&ldev->lock); 456 457 for (i = 0; !ldev->config && i < ARRAY_SIZE(hidled_configs); i++) 458 if (hidled_configs[i].type == id->driver_data) 459 ldev->config = &hidled_configs[i]; 460 461 if (!ldev->config) 462 return -EINVAL; 463 464 if (ldev->config->init) { 465 ret = ldev->config->init(ldev); 466 if (ret) 467 return ret; 468 } 469 470 ldev->rgb = devm_kcalloc(&hdev->dev, ldev->config->num_leds, 471 sizeof(struct hidled_rgb), GFP_KERNEL); 472 if (!ldev->rgb) 473 return -ENOMEM; 474 475 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); 476 if (ret) 477 return ret; 478 479 minor = ((struct hidraw *) hdev->hidraw)->minor; 480 481 for (i = 0; i < ldev->config->num_leds; i++) { 482 ldev->rgb[i].ldev = ldev; 483 ldev->rgb[i].num = i; 484 ret = hidled_init_rgb(&ldev->rgb[i], minor); 485 if (ret) { 486 hid_hw_stop(hdev); 487 return ret; 488 } 489 } 490 491 hid_info(hdev, "%s initialized\n", ldev->config->name); 492 493 return 0; 494 } 495 496 static const struct hid_device_id hidled_table[] = { 497 { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, 498 USB_DEVICE_ID_RI_KA_WEBMAIL), .driver_data = RISO_KAGAKU }, 499 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 500 USB_DEVICE_ID_DREAM_CHEEKY_WN), .driver_data = DREAM_CHEEKY }, 501 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 502 USB_DEVICE_ID_DREAM_CHEEKY_FA), .driver_data = DREAM_CHEEKY }, 503 { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, 504 USB_DEVICE_ID_BLINK1), .driver_data = THINGM }, 505 { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, 506 USB_DEVICE_ID_DELCOM_VISUAL_IND), .driver_data = DELCOM }, 507 { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, 508 USB_DEVICE_ID_LUXAFOR), .driver_data = LUXAFOR }, 509 { } 510 }; 511 MODULE_DEVICE_TABLE(hid, hidled_table); 512 513 static struct hid_driver hidled_driver = { 514 .name = "hid-led", 515 .probe = hidled_probe, 516 .id_table = hidled_table, 517 }; 518 519 module_hid_driver(hidled_driver); 520 521 MODULE_LICENSE("GPL"); 522 MODULE_AUTHOR("Heiner Kallweit <hkallweit1@gmail.com>"); 523 MODULE_DESCRIPTION("Simple USB RGB LED driver"); 524