0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/hid.h>
0011 #include <linux/hidraw.h>
0012 #include <linux/leds.h>
0013 #include <linux/module.h>
0014 #include <linux/mutex.h>
0015
0016 #include "hid-ids.h"
0017
0018 enum hidled_report_type {
0019 RAW_REQUEST,
0020 OUTPUT_REPORT
0021 };
0022
0023 enum hidled_type {
0024 RISO_KAGAKU,
0025 DREAM_CHEEKY,
0026 THINGM,
0027 DELCOM,
0028 LUXAFOR,
0029 };
0030
0031 static unsigned const char riso_kagaku_tbl[] = {
0032
0033 [0] = 0,
0034 [1] = 2,
0035 [2] = 1,
0036 [3] = 5,
0037 [4] = 3,
0038 [5] = 6,
0039 [6] = 4,
0040 [7] = 7
0041 };
0042
0043 #define RISO_KAGAKU_IX(r, g, b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
0044
0045 union delcom_packet {
0046 __u8 data[8];
0047 struct {
0048 __u8 major_cmd;
0049 __u8 minor_cmd;
0050 __u8 data_lsb;
0051 __u8 data_msb;
0052 } tx;
0053 struct {
0054 __u8 cmd;
0055 } rx;
0056 struct {
0057 __le16 family_code;
0058 __le16 security_code;
0059 __u8 fw_version;
0060 } fw;
0061 };
0062
0063 #define DELCOM_GREEN_LED 0
0064 #define DELCOM_RED_LED 1
0065 #define DELCOM_BLUE_LED 2
0066
0067 struct hidled_device;
0068 struct hidled_rgb;
0069
0070 struct hidled_config {
0071 enum hidled_type type;
0072 const char *name;
0073 const char *short_name;
0074 enum led_brightness max_brightness;
0075 int num_leds;
0076 size_t report_size;
0077 enum hidled_report_type report_type;
0078 int (*init)(struct hidled_device *ldev);
0079 int (*write)(struct led_classdev *cdev, enum led_brightness br);
0080 };
0081
0082 struct hidled_led {
0083 struct led_classdev cdev;
0084 struct hidled_rgb *rgb;
0085 char name[32];
0086 };
0087
0088 struct hidled_rgb {
0089 struct hidled_device *ldev;
0090 struct hidled_led red;
0091 struct hidled_led green;
0092 struct hidled_led blue;
0093 u8 num;
0094 };
0095
0096 struct hidled_device {
0097 const struct hidled_config *config;
0098 struct hid_device *hdev;
0099 struct hidled_rgb *rgb;
0100 u8 *buf;
0101 struct mutex lock;
0102 };
0103
0104 #define MAX_REPORT_SIZE 16
0105
0106 #define to_hidled_led(arg) container_of(arg, struct hidled_led, cdev)
0107
0108 static bool riso_kagaku_switch_green_blue;
0109 module_param(riso_kagaku_switch_green_blue, bool, S_IRUGO | S_IWUSR);
0110 MODULE_PARM_DESC(riso_kagaku_switch_green_blue,
0111 "switch green and blue RGB component for Riso Kagaku devices");
0112
0113 static int hidled_send(struct hidled_device *ldev, __u8 *buf)
0114 {
0115 int ret;
0116
0117 mutex_lock(&ldev->lock);
0118
0119
0120
0121
0122
0123 memcpy(ldev->buf, buf, ldev->config->report_size);
0124
0125 if (ldev->config->report_type == RAW_REQUEST)
0126 ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
0127 ldev->config->report_size,
0128 HID_FEATURE_REPORT,
0129 HID_REQ_SET_REPORT);
0130 else if (ldev->config->report_type == OUTPUT_REPORT)
0131 ret = hid_hw_output_report(ldev->hdev, ldev->buf,
0132 ldev->config->report_size);
0133 else
0134 ret = -EINVAL;
0135
0136 mutex_unlock(&ldev->lock);
0137
0138 if (ret < 0)
0139 return ret;
0140
0141 return ret == ldev->config->report_size ? 0 : -EMSGSIZE;
0142 }
0143
0144
0145 static int hidled_recv(struct hidled_device *ldev, __u8 *buf)
0146 {
0147 int ret;
0148
0149 if (ldev->config->report_type != RAW_REQUEST)
0150 return -EINVAL;
0151
0152 mutex_lock(&ldev->lock);
0153
0154 memcpy(ldev->buf, buf, ldev->config->report_size);
0155
0156 ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
0157 ldev->config->report_size,
0158 HID_FEATURE_REPORT,
0159 HID_REQ_SET_REPORT);
0160 if (ret < 0)
0161 goto err;
0162
0163 ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf,
0164 ldev->config->report_size,
0165 HID_FEATURE_REPORT,
0166 HID_REQ_GET_REPORT);
0167
0168 memcpy(buf, ldev->buf, ldev->config->report_size);
0169 err:
0170 mutex_unlock(&ldev->lock);
0171
0172 return ret < 0 ? ret : 0;
0173 }
0174
0175 static u8 riso_kagaku_index(struct hidled_rgb *rgb)
0176 {
0177 enum led_brightness r, g, b;
0178
0179 r = rgb->red.cdev.brightness;
0180 g = rgb->green.cdev.brightness;
0181 b = rgb->blue.cdev.brightness;
0182
0183 if (riso_kagaku_switch_green_blue)
0184 return RISO_KAGAKU_IX(r, b, g);
0185 else
0186 return RISO_KAGAKU_IX(r, g, b);
0187 }
0188
0189 static int riso_kagaku_write(struct led_classdev *cdev, enum led_brightness br)
0190 {
0191 struct hidled_led *led = to_hidled_led(cdev);
0192 struct hidled_rgb *rgb = led->rgb;
0193 __u8 buf[MAX_REPORT_SIZE] = {};
0194
0195 buf[1] = riso_kagaku_index(rgb);
0196
0197 return hidled_send(rgb->ldev, buf);
0198 }
0199
0200 static int dream_cheeky_write(struct led_classdev *cdev, enum led_brightness br)
0201 {
0202 struct hidled_led *led = to_hidled_led(cdev);
0203 struct hidled_rgb *rgb = led->rgb;
0204 __u8 buf[MAX_REPORT_SIZE] = {};
0205
0206 buf[1] = rgb->red.cdev.brightness;
0207 buf[2] = rgb->green.cdev.brightness;
0208 buf[3] = rgb->blue.cdev.brightness;
0209 buf[7] = 0x1a;
0210 buf[8] = 0x05;
0211
0212 return hidled_send(rgb->ldev, buf);
0213 }
0214
0215 static int dream_cheeky_init(struct hidled_device *ldev)
0216 {
0217 __u8 buf[MAX_REPORT_SIZE] = {};
0218
0219
0220 buf[1] = 0x1f;
0221 buf[2] = 0x02;
0222 buf[4] = 0x5f;
0223 buf[7] = 0x1a;
0224 buf[8] = 0x03;
0225
0226 return hidled_send(ldev, buf);
0227 }
0228
0229 static int _thingm_write(struct led_classdev *cdev, enum led_brightness br,
0230 u8 offset)
0231 {
0232 struct hidled_led *led = to_hidled_led(cdev);
0233 __u8 buf[MAX_REPORT_SIZE] = { 1, 'c' };
0234
0235 buf[2] = led->rgb->red.cdev.brightness;
0236 buf[3] = led->rgb->green.cdev.brightness;
0237 buf[4] = led->rgb->blue.cdev.brightness;
0238 buf[7] = led->rgb->num + offset;
0239
0240 return hidled_send(led->rgb->ldev, buf);
0241 }
0242
0243 static int thingm_write_v1(struct led_classdev *cdev, enum led_brightness br)
0244 {
0245 return _thingm_write(cdev, br, 0);
0246 }
0247
0248 static int thingm_write(struct led_classdev *cdev, enum led_brightness br)
0249 {
0250 return _thingm_write(cdev, br, 1);
0251 }
0252
0253 static const struct hidled_config hidled_config_thingm_v1 = {
0254 .name = "ThingM blink(1) v1",
0255 .short_name = "thingm",
0256 .max_brightness = 255,
0257 .num_leds = 1,
0258 .report_size = 9,
0259 .report_type = RAW_REQUEST,
0260 .write = thingm_write_v1,
0261 };
0262
0263 static int thingm_init(struct hidled_device *ldev)
0264 {
0265 __u8 buf[MAX_REPORT_SIZE] = { 1, 'v' };
0266 int ret;
0267
0268 ret = hidled_recv(ldev, buf);
0269 if (ret)
0270 return ret;
0271
0272
0273 if (buf[3] == '1')
0274 ldev->config = &hidled_config_thingm_v1;
0275
0276 return 0;
0277 }
0278
0279 static inline int delcom_get_lednum(const struct hidled_led *led)
0280 {
0281 if (led == &led->rgb->red)
0282 return DELCOM_RED_LED;
0283 else if (led == &led->rgb->green)
0284 return DELCOM_GREEN_LED;
0285 else
0286 return DELCOM_BLUE_LED;
0287 }
0288
0289 static int delcom_enable_led(struct hidled_led *led)
0290 {
0291 union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 12 };
0292
0293 dp.tx.data_lsb = 1 << delcom_get_lednum(led);
0294 dp.tx.data_msb = 0;
0295
0296 return hidled_send(led->rgb->ldev, dp.data);
0297 }
0298
0299 static int delcom_set_pwm(struct hidled_led *led)
0300 {
0301 union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 34 };
0302
0303 dp.tx.data_lsb = delcom_get_lednum(led);
0304 dp.tx.data_msb = led->cdev.brightness;
0305
0306 return hidled_send(led->rgb->ldev, dp.data);
0307 }
0308
0309 static int delcom_write(struct led_classdev *cdev, enum led_brightness br)
0310 {
0311 struct hidled_led *led = to_hidled_led(cdev);
0312 int ret;
0313
0314
0315
0316
0317
0318
0319 ret = delcom_enable_led(led);
0320 if (ret)
0321 return ret;
0322
0323 return delcom_set_pwm(led);
0324 }
0325
0326 static int delcom_init(struct hidled_device *ldev)
0327 {
0328 union delcom_packet dp = { .rx.cmd = 104 };
0329 int ret;
0330
0331 ret = hidled_recv(ldev, dp.data);
0332 if (ret)
0333 return ret;
0334
0335
0336
0337
0338 return le16_to_cpu(dp.fw.family_code) == 2 ? 0 : -ENODEV;
0339 }
0340
0341 static int luxafor_write(struct led_classdev *cdev, enum led_brightness br)
0342 {
0343 struct hidled_led *led = to_hidled_led(cdev);
0344 __u8 buf[MAX_REPORT_SIZE] = { [1] = 1 };
0345
0346 buf[2] = led->rgb->num + 1;
0347 buf[3] = led->rgb->red.cdev.brightness;
0348 buf[4] = led->rgb->green.cdev.brightness;
0349 buf[5] = led->rgb->blue.cdev.brightness;
0350
0351 return hidled_send(led->rgb->ldev, buf);
0352 }
0353
0354 static const struct hidled_config hidled_configs[] = {
0355 {
0356 .type = RISO_KAGAKU,
0357 .name = "Riso Kagaku Webmail Notifier",
0358 .short_name = "riso_kagaku",
0359 .max_brightness = 1,
0360 .num_leds = 1,
0361 .report_size = 6,
0362 .report_type = OUTPUT_REPORT,
0363 .write = riso_kagaku_write,
0364 },
0365 {
0366 .type = DREAM_CHEEKY,
0367 .name = "Dream Cheeky Webmail Notifier",
0368 .short_name = "dream_cheeky",
0369 .max_brightness = 63,
0370 .num_leds = 1,
0371 .report_size = 9,
0372 .report_type = RAW_REQUEST,
0373 .init = dream_cheeky_init,
0374 .write = dream_cheeky_write,
0375 },
0376 {
0377 .type = THINGM,
0378 .name = "ThingM blink(1)",
0379 .short_name = "thingm",
0380 .max_brightness = 255,
0381 .num_leds = 2,
0382 .report_size = 9,
0383 .report_type = RAW_REQUEST,
0384 .init = thingm_init,
0385 .write = thingm_write,
0386 },
0387 {
0388 .type = DELCOM,
0389 .name = "Delcom Visual Signal Indicator G2",
0390 .short_name = "delcom",
0391 .max_brightness = 100,
0392 .num_leds = 1,
0393 .report_size = 8,
0394 .report_type = RAW_REQUEST,
0395 .init = delcom_init,
0396 .write = delcom_write,
0397 },
0398 {
0399 .type = LUXAFOR,
0400 .name = "Greynut Luxafor",
0401 .short_name = "luxafor",
0402 .max_brightness = 255,
0403 .num_leds = 6,
0404 .report_size = 9,
0405 .report_type = OUTPUT_REPORT,
0406 .write = luxafor_write,
0407 },
0408 };
0409
0410 static int hidled_init_led(struct hidled_led *led, const char *color_name,
0411 struct hidled_rgb *rgb, unsigned int minor)
0412 {
0413 const struct hidled_config *config = rgb->ldev->config;
0414
0415 if (config->num_leds > 1)
0416 snprintf(led->name, sizeof(led->name), "%s%u:%s:led%u",
0417 config->short_name, minor, color_name, rgb->num);
0418 else
0419 snprintf(led->name, sizeof(led->name), "%s%u:%s",
0420 config->short_name, minor, color_name);
0421 led->cdev.name = led->name;
0422 led->cdev.max_brightness = config->max_brightness;
0423 led->cdev.brightness_set_blocking = config->write;
0424 led->cdev.flags = LED_HW_PLUGGABLE;
0425 led->rgb = rgb;
0426
0427 return devm_led_classdev_register(&rgb->ldev->hdev->dev, &led->cdev);
0428 }
0429
0430 static int hidled_init_rgb(struct hidled_rgb *rgb, unsigned int minor)
0431 {
0432 int ret;
0433
0434
0435 ret = hidled_init_led(&rgb->red, "red", rgb, minor);
0436 if (ret)
0437 return ret;
0438
0439
0440 ret = hidled_init_led(&rgb->green, "green", rgb, minor);
0441 if (ret)
0442 return ret;
0443
0444
0445 return hidled_init_led(&rgb->blue, "blue", rgb, minor);
0446 }
0447
0448 static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id)
0449 {
0450 struct hidled_device *ldev;
0451 unsigned int minor;
0452 int ret, i;
0453
0454 ldev = devm_kzalloc(&hdev->dev, sizeof(*ldev), GFP_KERNEL);
0455 if (!ldev)
0456 return -ENOMEM;
0457
0458 ldev->buf = devm_kmalloc(&hdev->dev, MAX_REPORT_SIZE, GFP_KERNEL);
0459 if (!ldev->buf)
0460 return -ENOMEM;
0461
0462 ret = hid_parse(hdev);
0463 if (ret)
0464 return ret;
0465
0466 ldev->hdev = hdev;
0467 mutex_init(&ldev->lock);
0468
0469 for (i = 0; !ldev->config && i < ARRAY_SIZE(hidled_configs); i++)
0470 if (hidled_configs[i].type == id->driver_data)
0471 ldev->config = &hidled_configs[i];
0472
0473 if (!ldev->config)
0474 return -EINVAL;
0475
0476 if (ldev->config->init) {
0477 ret = ldev->config->init(ldev);
0478 if (ret)
0479 return ret;
0480 }
0481
0482 ldev->rgb = devm_kcalloc(&hdev->dev, ldev->config->num_leds,
0483 sizeof(struct hidled_rgb), GFP_KERNEL);
0484 if (!ldev->rgb)
0485 return -ENOMEM;
0486
0487 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
0488 if (ret)
0489 return ret;
0490
0491 minor = ((struct hidraw *) hdev->hidraw)->minor;
0492
0493 for (i = 0; i < ldev->config->num_leds; i++) {
0494 ldev->rgb[i].ldev = ldev;
0495 ldev->rgb[i].num = i;
0496 ret = hidled_init_rgb(&ldev->rgb[i], minor);
0497 if (ret) {
0498 hid_hw_stop(hdev);
0499 return ret;
0500 }
0501 }
0502
0503 hid_info(hdev, "%s initialized\n", ldev->config->name);
0504
0505 return 0;
0506 }
0507
0508 static const struct hid_device_id hidled_table[] = {
0509 { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU,
0510 USB_DEVICE_ID_RI_KA_WEBMAIL), .driver_data = RISO_KAGAKU },
0511 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
0512 USB_DEVICE_ID_DREAM_CHEEKY_WN), .driver_data = DREAM_CHEEKY },
0513 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
0514 USB_DEVICE_ID_DREAM_CHEEKY_FA), .driver_data = DREAM_CHEEKY },
0515 { HID_USB_DEVICE(USB_VENDOR_ID_THINGM,
0516 USB_DEVICE_ID_BLINK1), .driver_data = THINGM },
0517 { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM,
0518 USB_DEVICE_ID_DELCOM_VISUAL_IND), .driver_data = DELCOM },
0519 { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP,
0520 USB_DEVICE_ID_LUXAFOR), .driver_data = LUXAFOR },
0521 { }
0522 };
0523 MODULE_DEVICE_TABLE(hid, hidled_table);
0524
0525 static struct hid_driver hidled_driver = {
0526 .name = "hid-led",
0527 .probe = hidled_probe,
0528 .id_table = hidled_table,
0529 };
0530
0531 module_hid_driver(hidled_driver);
0532
0533 MODULE_LICENSE("GPL");
0534 MODULE_AUTHOR("Heiner Kallweit <hkallweit1@gmail.com>");
0535 MODULE_DESCRIPTION("Simple USB RGB LED driver");