Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Simple USB RGB LED driver
0004  *
0005  * Copyright 2016 Heiner Kallweit <hkallweit1@gmail.com>
0006  * Based on drivers/hid/hid-thingm.c and
0007  * drivers/usb/misc/usbled.c
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 /* R+2G+4B -> riso kagaku color index */
0033     [0] = 0, /* black   */
0034     [1] = 2, /* red     */
0035     [2] = 1, /* green   */
0036     [3] = 5, /* yellow  */
0037     [4] = 3, /* blue    */
0038     [5] = 6, /* magenta */
0039     [6] = 4, /* cyan    */
0040     [7] = 7  /* white   */
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      * buffer provided to hid_hw_raw_request must not be on the stack
0121      * and must not be part of a data structure
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 /* reading data is supported for report type RAW_REQUEST only */
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     /* Dream Cheeky magic */
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     /* Check for firmware major version 1 */
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      * enable LED
0316      * We can't do this in the init function already because the device
0317      * is internally reset later.
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      * Several Delcom devices share the same USB VID/PID
0336      * Check for family id 2 for Visual Signal Indicator
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     /* Register the red diode */
0435     ret = hidled_init_led(&rgb->red, "red", rgb, minor);
0436     if (ret)
0437         return ret;
0438 
0439     /* Register the green diode */
0440     ret = hidled_init_led(&rgb->green, "green", rgb, minor);
0441     if (ret)
0442         return ret;
0443 
0444     /* Register the blue diode */
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");