1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26c7ad07eSHeiner Kallweit /* 36c7ad07eSHeiner Kallweit * Simple USB RGB LED driver 46c7ad07eSHeiner Kallweit * 56c7ad07eSHeiner Kallweit * Copyright 2016 Heiner Kallweit <hkallweit1@gmail.com> 66c7ad07eSHeiner Kallweit * Based on drivers/hid/hid-thingm.c and 76c7ad07eSHeiner Kallweit * drivers/usb/misc/usbled.c 86c7ad07eSHeiner Kallweit */ 96c7ad07eSHeiner Kallweit 106c7ad07eSHeiner Kallweit #include <linux/hid.h> 116c7ad07eSHeiner Kallweit #include <linux/hidraw.h> 126c7ad07eSHeiner Kallweit #include <linux/leds.h> 136c7ad07eSHeiner Kallweit #include <linux/module.h> 146c7ad07eSHeiner Kallweit #include <linux/mutex.h> 156c7ad07eSHeiner Kallweit 166c7ad07eSHeiner Kallweit #include "hid-ids.h" 176c7ad07eSHeiner Kallweit 186c7ad07eSHeiner Kallweit enum hidled_report_type { 196c7ad07eSHeiner Kallweit RAW_REQUEST, 206c7ad07eSHeiner Kallweit OUTPUT_REPORT 216c7ad07eSHeiner Kallweit }; 226c7ad07eSHeiner Kallweit 236c7ad07eSHeiner Kallweit enum hidled_type { 246c7ad07eSHeiner Kallweit RISO_KAGAKU, 256c7ad07eSHeiner Kallweit DREAM_CHEEKY, 26007414e8SHeiner Kallweit THINGM, 27de908650SHeiner Kallweit DELCOM, 289d1e048cSHeiner Kallweit LUXAFOR, 296c7ad07eSHeiner Kallweit }; 306c7ad07eSHeiner Kallweit 316c7ad07eSHeiner Kallweit static unsigned const char riso_kagaku_tbl[] = { 326c7ad07eSHeiner Kallweit /* R+2G+4B -> riso kagaku color index */ 336c7ad07eSHeiner Kallweit [0] = 0, /* black */ 346c7ad07eSHeiner Kallweit [1] = 2, /* red */ 356c7ad07eSHeiner Kallweit [2] = 1, /* green */ 366c7ad07eSHeiner Kallweit [3] = 5, /* yellow */ 376c7ad07eSHeiner Kallweit [4] = 3, /* blue */ 386c7ad07eSHeiner Kallweit [5] = 6, /* magenta */ 396c7ad07eSHeiner Kallweit [6] = 4, /* cyan */ 406c7ad07eSHeiner Kallweit [7] = 7 /* white */ 416c7ad07eSHeiner Kallweit }; 426c7ad07eSHeiner Kallweit 436c7ad07eSHeiner Kallweit #define RISO_KAGAKU_IX(r, g, b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)] 446c7ad07eSHeiner Kallweit 45de908650SHeiner Kallweit union delcom_packet { 46de908650SHeiner Kallweit __u8 data[8]; 47de908650SHeiner Kallweit struct { 48de908650SHeiner Kallweit __u8 major_cmd; 49de908650SHeiner Kallweit __u8 minor_cmd; 50de908650SHeiner Kallweit __u8 data_lsb; 51de908650SHeiner Kallweit __u8 data_msb; 52de908650SHeiner Kallweit } tx; 53de908650SHeiner Kallweit struct { 54de908650SHeiner Kallweit __u8 cmd; 55de908650SHeiner Kallweit } rx; 56de908650SHeiner Kallweit struct { 57de908650SHeiner Kallweit __le16 family_code; 58de908650SHeiner Kallweit __le16 security_code; 59de908650SHeiner Kallweit __u8 fw_version; 60de908650SHeiner Kallweit } fw; 61de908650SHeiner Kallweit }; 62de908650SHeiner Kallweit 63de908650SHeiner Kallweit #define DELCOM_GREEN_LED 0 64de908650SHeiner Kallweit #define DELCOM_RED_LED 1 65de908650SHeiner Kallweit #define DELCOM_BLUE_LED 2 66de908650SHeiner Kallweit 676c7ad07eSHeiner Kallweit struct hidled_device; 685bc83936SHeiner Kallweit struct hidled_rgb; 696c7ad07eSHeiner Kallweit 706c7ad07eSHeiner Kallweit struct hidled_config { 716c7ad07eSHeiner Kallweit enum hidled_type type; 726c7ad07eSHeiner Kallweit const char *name; 736c7ad07eSHeiner Kallweit const char *short_name; 746c7ad07eSHeiner Kallweit enum led_brightness max_brightness; 755bc83936SHeiner Kallweit int num_leds; 766c7ad07eSHeiner Kallweit size_t report_size; 776c7ad07eSHeiner Kallweit enum hidled_report_type report_type; 786c7ad07eSHeiner Kallweit int (*init)(struct hidled_device *ldev); 796c7ad07eSHeiner Kallweit int (*write)(struct led_classdev *cdev, enum led_brightness br); 806c7ad07eSHeiner Kallweit }; 816c7ad07eSHeiner Kallweit 826c7ad07eSHeiner Kallweit struct hidled_led { 836c7ad07eSHeiner Kallweit struct led_classdev cdev; 845bc83936SHeiner Kallweit struct hidled_rgb *rgb; 856c7ad07eSHeiner Kallweit char name[32]; 866c7ad07eSHeiner Kallweit }; 876c7ad07eSHeiner Kallweit 885bc83936SHeiner Kallweit struct hidled_rgb { 895bc83936SHeiner Kallweit struct hidled_device *ldev; 905bc83936SHeiner Kallweit struct hidled_led red; 915bc83936SHeiner Kallweit struct hidled_led green; 925bc83936SHeiner Kallweit struct hidled_led blue; 935bc83936SHeiner Kallweit u8 num; 945bc83936SHeiner Kallweit }; 955bc83936SHeiner Kallweit 966c7ad07eSHeiner Kallweit struct hidled_device { 976c7ad07eSHeiner Kallweit const struct hidled_config *config; 986c7ad07eSHeiner Kallweit struct hid_device *hdev; 995bc83936SHeiner Kallweit struct hidled_rgb *rgb; 1003d1355b3SHeiner Kallweit u8 *buf; 1016c7ad07eSHeiner Kallweit struct mutex lock; 1026c7ad07eSHeiner Kallweit }; 1036c7ad07eSHeiner Kallweit 1046c7ad07eSHeiner Kallweit #define MAX_REPORT_SIZE 16 1056c7ad07eSHeiner Kallweit 1066c7ad07eSHeiner Kallweit #define to_hidled_led(arg) container_of(arg, struct hidled_led, cdev) 1076c7ad07eSHeiner Kallweit 1086c7ad07eSHeiner Kallweit static bool riso_kagaku_switch_green_blue; 1096c7ad07eSHeiner Kallweit module_param(riso_kagaku_switch_green_blue, bool, S_IRUGO | S_IWUSR); 1106c7ad07eSHeiner Kallweit MODULE_PARM_DESC(riso_kagaku_switch_green_blue, 1116c7ad07eSHeiner Kallweit "switch green and blue RGB component for Riso Kagaku devices"); 1126c7ad07eSHeiner Kallweit 1136c7ad07eSHeiner Kallweit static int hidled_send(struct hidled_device *ldev, __u8 *buf) 1146c7ad07eSHeiner Kallweit { 1156c7ad07eSHeiner Kallweit int ret; 1166c7ad07eSHeiner Kallweit 1176c7ad07eSHeiner Kallweit mutex_lock(&ldev->lock); 1186c7ad07eSHeiner Kallweit 1193d1355b3SHeiner Kallweit /* 1203d1355b3SHeiner Kallweit * buffer provided to hid_hw_raw_request must not be on the stack 1213d1355b3SHeiner Kallweit * and must not be part of a data structure 1223d1355b3SHeiner Kallweit */ 1233d1355b3SHeiner Kallweit memcpy(ldev->buf, buf, ldev->config->report_size); 1243d1355b3SHeiner Kallweit 1256c7ad07eSHeiner Kallweit if (ldev->config->report_type == RAW_REQUEST) 1263d1355b3SHeiner Kallweit ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf, 1276c7ad07eSHeiner Kallweit ldev->config->report_size, 1286c7ad07eSHeiner Kallweit HID_FEATURE_REPORT, 1296c7ad07eSHeiner Kallweit HID_REQ_SET_REPORT); 1306c7ad07eSHeiner Kallweit else if (ldev->config->report_type == OUTPUT_REPORT) 1313d1355b3SHeiner Kallweit ret = hid_hw_output_report(ldev->hdev, ldev->buf, 1326c7ad07eSHeiner Kallweit ldev->config->report_size); 1336c7ad07eSHeiner Kallweit else 1346c7ad07eSHeiner Kallweit ret = -EINVAL; 1356c7ad07eSHeiner Kallweit 1366c7ad07eSHeiner Kallweit mutex_unlock(&ldev->lock); 1376c7ad07eSHeiner Kallweit 1386c7ad07eSHeiner Kallweit if (ret < 0) 1396c7ad07eSHeiner Kallweit return ret; 1406c7ad07eSHeiner Kallweit 1416c7ad07eSHeiner Kallweit return ret == ldev->config->report_size ? 0 : -EMSGSIZE; 1426c7ad07eSHeiner Kallweit } 1436c7ad07eSHeiner Kallweit 14443745730SHeiner Kallweit /* reading data is supported for report type RAW_REQUEST only */ 14543745730SHeiner Kallweit static int hidled_recv(struct hidled_device *ldev, __u8 *buf) 14643745730SHeiner Kallweit { 14743745730SHeiner Kallweit int ret; 14843745730SHeiner Kallweit 14943745730SHeiner Kallweit if (ldev->config->report_type != RAW_REQUEST) 15043745730SHeiner Kallweit return -EINVAL; 15143745730SHeiner Kallweit 15243745730SHeiner Kallweit mutex_lock(&ldev->lock); 15343745730SHeiner Kallweit 1543d1355b3SHeiner Kallweit memcpy(ldev->buf, buf, ldev->config->report_size); 1553d1355b3SHeiner Kallweit 1563d1355b3SHeiner Kallweit ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf, 15743745730SHeiner Kallweit ldev->config->report_size, 15843745730SHeiner Kallweit HID_FEATURE_REPORT, 15943745730SHeiner Kallweit HID_REQ_SET_REPORT); 16043745730SHeiner Kallweit if (ret < 0) 16143745730SHeiner Kallweit goto err; 16243745730SHeiner Kallweit 1633d1355b3SHeiner Kallweit ret = hid_hw_raw_request(ldev->hdev, buf[0], ldev->buf, 16443745730SHeiner Kallweit ldev->config->report_size, 16543745730SHeiner Kallweit HID_FEATURE_REPORT, 16643745730SHeiner Kallweit HID_REQ_GET_REPORT); 1673d1355b3SHeiner Kallweit 1683d1355b3SHeiner Kallweit memcpy(buf, ldev->buf, ldev->config->report_size); 16943745730SHeiner Kallweit err: 17043745730SHeiner Kallweit mutex_unlock(&ldev->lock); 17143745730SHeiner Kallweit 17243745730SHeiner Kallweit return ret < 0 ? ret : 0; 17343745730SHeiner Kallweit } 17443745730SHeiner Kallweit 1755bc83936SHeiner Kallweit static u8 riso_kagaku_index(struct hidled_rgb *rgb) 1766c7ad07eSHeiner Kallweit { 1776c7ad07eSHeiner Kallweit enum led_brightness r, g, b; 1786c7ad07eSHeiner Kallweit 1795bc83936SHeiner Kallweit r = rgb->red.cdev.brightness; 1805bc83936SHeiner Kallweit g = rgb->green.cdev.brightness; 1815bc83936SHeiner Kallweit b = rgb->blue.cdev.brightness; 1826c7ad07eSHeiner Kallweit 1836c7ad07eSHeiner Kallweit if (riso_kagaku_switch_green_blue) 1846c7ad07eSHeiner Kallweit return RISO_KAGAKU_IX(r, b, g); 1856c7ad07eSHeiner Kallweit else 1866c7ad07eSHeiner Kallweit return RISO_KAGAKU_IX(r, g, b); 1876c7ad07eSHeiner Kallweit } 1886c7ad07eSHeiner Kallweit 1896c7ad07eSHeiner Kallweit static int riso_kagaku_write(struct led_classdev *cdev, enum led_brightness br) 1906c7ad07eSHeiner Kallweit { 1916c7ad07eSHeiner Kallweit struct hidled_led *led = to_hidled_led(cdev); 1925bc83936SHeiner Kallweit struct hidled_rgb *rgb = led->rgb; 1936c7ad07eSHeiner Kallweit __u8 buf[MAX_REPORT_SIZE] = {}; 1946c7ad07eSHeiner Kallweit 1955bc83936SHeiner Kallweit buf[1] = riso_kagaku_index(rgb); 1966c7ad07eSHeiner Kallweit 1975bc83936SHeiner Kallweit return hidled_send(rgb->ldev, buf); 1986c7ad07eSHeiner Kallweit } 1996c7ad07eSHeiner Kallweit 2006c7ad07eSHeiner Kallweit static int dream_cheeky_write(struct led_classdev *cdev, enum led_brightness br) 2016c7ad07eSHeiner Kallweit { 2026c7ad07eSHeiner Kallweit struct hidled_led *led = to_hidled_led(cdev); 2035bc83936SHeiner Kallweit struct hidled_rgb *rgb = led->rgb; 2046c7ad07eSHeiner Kallweit __u8 buf[MAX_REPORT_SIZE] = {}; 2056c7ad07eSHeiner Kallweit 2065bc83936SHeiner Kallweit buf[1] = rgb->red.cdev.brightness; 2075bc83936SHeiner Kallweit buf[2] = rgb->green.cdev.brightness; 2085bc83936SHeiner Kallweit buf[3] = rgb->blue.cdev.brightness; 2096c7ad07eSHeiner Kallweit buf[7] = 0x1a; 2106c7ad07eSHeiner Kallweit buf[8] = 0x05; 2116c7ad07eSHeiner Kallweit 2125bc83936SHeiner Kallweit return hidled_send(rgb->ldev, buf); 2136c7ad07eSHeiner Kallweit } 2146c7ad07eSHeiner Kallweit 2156c7ad07eSHeiner Kallweit static int dream_cheeky_init(struct hidled_device *ldev) 2166c7ad07eSHeiner Kallweit { 2176c7ad07eSHeiner Kallweit __u8 buf[MAX_REPORT_SIZE] = {}; 2186c7ad07eSHeiner Kallweit 2196c7ad07eSHeiner Kallweit /* Dream Cheeky magic */ 2206c7ad07eSHeiner Kallweit buf[1] = 0x1f; 2216c7ad07eSHeiner Kallweit buf[2] = 0x02; 2226c7ad07eSHeiner Kallweit buf[4] = 0x5f; 2236c7ad07eSHeiner Kallweit buf[7] = 0x1a; 2246c7ad07eSHeiner Kallweit buf[8] = 0x03; 2256c7ad07eSHeiner Kallweit 2266c7ad07eSHeiner Kallweit return hidled_send(ldev, buf); 2276c7ad07eSHeiner Kallweit } 2286c7ad07eSHeiner Kallweit 229007414e8SHeiner Kallweit static int _thingm_write(struct led_classdev *cdev, enum led_brightness br, 230007414e8SHeiner Kallweit u8 offset) 231007414e8SHeiner Kallweit { 232007414e8SHeiner Kallweit struct hidled_led *led = to_hidled_led(cdev); 23334d9810bSHeiner Kallweit __u8 buf[MAX_REPORT_SIZE] = { 1, 'c' }; 234007414e8SHeiner Kallweit 235007414e8SHeiner Kallweit buf[2] = led->rgb->red.cdev.brightness; 236007414e8SHeiner Kallweit buf[3] = led->rgb->green.cdev.brightness; 237007414e8SHeiner Kallweit buf[4] = led->rgb->blue.cdev.brightness; 238007414e8SHeiner Kallweit buf[7] = led->rgb->num + offset; 239007414e8SHeiner Kallweit 240007414e8SHeiner Kallweit return hidled_send(led->rgb->ldev, buf); 241007414e8SHeiner Kallweit } 242007414e8SHeiner Kallweit 243007414e8SHeiner Kallweit static int thingm_write_v1(struct led_classdev *cdev, enum led_brightness br) 244007414e8SHeiner Kallweit { 245007414e8SHeiner Kallweit return _thingm_write(cdev, br, 0); 246007414e8SHeiner Kallweit } 247007414e8SHeiner Kallweit 248007414e8SHeiner Kallweit static int thingm_write(struct led_classdev *cdev, enum led_brightness br) 249007414e8SHeiner Kallweit { 250007414e8SHeiner Kallweit return _thingm_write(cdev, br, 1); 251007414e8SHeiner Kallweit } 252007414e8SHeiner Kallweit 253007414e8SHeiner Kallweit static const struct hidled_config hidled_config_thingm_v1 = { 254007414e8SHeiner Kallweit .name = "ThingM blink(1) v1", 255007414e8SHeiner Kallweit .short_name = "thingm", 256007414e8SHeiner Kallweit .max_brightness = 255, 257007414e8SHeiner Kallweit .num_leds = 1, 258007414e8SHeiner Kallweit .report_size = 9, 259007414e8SHeiner Kallweit .report_type = RAW_REQUEST, 260007414e8SHeiner Kallweit .write = thingm_write_v1, 261007414e8SHeiner Kallweit }; 262007414e8SHeiner Kallweit 263007414e8SHeiner Kallweit static int thingm_init(struct hidled_device *ldev) 264007414e8SHeiner Kallweit { 26534d9810bSHeiner Kallweit __u8 buf[MAX_REPORT_SIZE] = { 1, 'v' }; 266007414e8SHeiner Kallweit int ret; 267007414e8SHeiner Kallweit 268007414e8SHeiner Kallweit ret = hidled_recv(ldev, buf); 269007414e8SHeiner Kallweit if (ret) 270007414e8SHeiner Kallweit return ret; 271007414e8SHeiner Kallweit 272007414e8SHeiner Kallweit /* Check for firmware major version 1 */ 273007414e8SHeiner Kallweit if (buf[3] == '1') 274007414e8SHeiner Kallweit ldev->config = &hidled_config_thingm_v1; 275007414e8SHeiner Kallweit 276007414e8SHeiner Kallweit return 0; 277007414e8SHeiner Kallweit } 278007414e8SHeiner Kallweit 279de908650SHeiner Kallweit static inline int delcom_get_lednum(const struct hidled_led *led) 280de908650SHeiner Kallweit { 281de908650SHeiner Kallweit if (led == &led->rgb->red) 282de908650SHeiner Kallweit return DELCOM_RED_LED; 283de908650SHeiner Kallweit else if (led == &led->rgb->green) 284de908650SHeiner Kallweit return DELCOM_GREEN_LED; 285de908650SHeiner Kallweit else 286de908650SHeiner Kallweit return DELCOM_BLUE_LED; 287de908650SHeiner Kallweit } 288de908650SHeiner Kallweit 289de908650SHeiner Kallweit static int delcom_enable_led(struct hidled_led *led) 290de908650SHeiner Kallweit { 291de908650SHeiner Kallweit union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 12 }; 292de908650SHeiner Kallweit 293de908650SHeiner Kallweit dp.tx.data_lsb = 1 << delcom_get_lednum(led); 294de908650SHeiner Kallweit dp.tx.data_msb = 0; 295de908650SHeiner Kallweit 296de908650SHeiner Kallweit return hidled_send(led->rgb->ldev, dp.data); 297de908650SHeiner Kallweit } 298de908650SHeiner Kallweit 299de908650SHeiner Kallweit static int delcom_set_pwm(struct hidled_led *led) 300de908650SHeiner Kallweit { 301de908650SHeiner Kallweit union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 34 }; 302de908650SHeiner Kallweit 303de908650SHeiner Kallweit dp.tx.data_lsb = delcom_get_lednum(led); 304de908650SHeiner Kallweit dp.tx.data_msb = led->cdev.brightness; 305de908650SHeiner Kallweit 306de908650SHeiner Kallweit return hidled_send(led->rgb->ldev, dp.data); 307de908650SHeiner Kallweit } 308de908650SHeiner Kallweit 309de908650SHeiner Kallweit static int delcom_write(struct led_classdev *cdev, enum led_brightness br) 310de908650SHeiner Kallweit { 311de908650SHeiner Kallweit struct hidled_led *led = to_hidled_led(cdev); 312de908650SHeiner Kallweit int ret; 313de908650SHeiner Kallweit 314de908650SHeiner Kallweit /* 315de908650SHeiner Kallweit * enable LED 316de908650SHeiner Kallweit * We can't do this in the init function already because the device 317de908650SHeiner Kallweit * is internally reset later. 318de908650SHeiner Kallweit */ 319de908650SHeiner Kallweit ret = delcom_enable_led(led); 320de908650SHeiner Kallweit if (ret) 321de908650SHeiner Kallweit return ret; 322de908650SHeiner Kallweit 323de908650SHeiner Kallweit return delcom_set_pwm(led); 324de908650SHeiner Kallweit } 325de908650SHeiner Kallweit 326de908650SHeiner Kallweit static int delcom_init(struct hidled_device *ldev) 327de908650SHeiner Kallweit { 328de908650SHeiner Kallweit union delcom_packet dp = { .rx.cmd = 104 }; 329de908650SHeiner Kallweit int ret; 330de908650SHeiner Kallweit 331de908650SHeiner Kallweit ret = hidled_recv(ldev, dp.data); 332de908650SHeiner Kallweit if (ret) 333de908650SHeiner Kallweit return ret; 334de908650SHeiner Kallweit /* 335de908650SHeiner Kallweit * Several Delcom devices share the same USB VID/PID 336de908650SHeiner Kallweit * Check for family id 2 for Visual Signal Indicator 337de908650SHeiner Kallweit */ 338f4c109b6SHeiner Kallweit return le16_to_cpu(dp.fw.family_code) == 2 ? 0 : -ENODEV; 339de908650SHeiner Kallweit } 340de908650SHeiner Kallweit 3419d1e048cSHeiner Kallweit static int luxafor_write(struct led_classdev *cdev, enum led_brightness br) 3429d1e048cSHeiner Kallweit { 3439d1e048cSHeiner Kallweit struct hidled_led *led = to_hidled_led(cdev); 3449d1e048cSHeiner Kallweit __u8 buf[MAX_REPORT_SIZE] = { [1] = 1 }; 3459d1e048cSHeiner Kallweit 3469d1e048cSHeiner Kallweit buf[2] = led->rgb->num + 1; 3479d1e048cSHeiner Kallweit buf[3] = led->rgb->red.cdev.brightness; 3489d1e048cSHeiner Kallweit buf[4] = led->rgb->green.cdev.brightness; 3499d1e048cSHeiner Kallweit buf[5] = led->rgb->blue.cdev.brightness; 3509d1e048cSHeiner Kallweit 3519d1e048cSHeiner Kallweit return hidled_send(led->rgb->ldev, buf); 3529d1e048cSHeiner Kallweit } 3539d1e048cSHeiner Kallweit 3546c7ad07eSHeiner Kallweit static const struct hidled_config hidled_configs[] = { 3556c7ad07eSHeiner Kallweit { 3566c7ad07eSHeiner Kallweit .type = RISO_KAGAKU, 3576c7ad07eSHeiner Kallweit .name = "Riso Kagaku Webmail Notifier", 3586c7ad07eSHeiner Kallweit .short_name = "riso_kagaku", 3596c7ad07eSHeiner Kallweit .max_brightness = 1, 3605bc83936SHeiner Kallweit .num_leds = 1, 3616c7ad07eSHeiner Kallweit .report_size = 6, 3626c7ad07eSHeiner Kallweit .report_type = OUTPUT_REPORT, 3636c7ad07eSHeiner Kallweit .write = riso_kagaku_write, 3646c7ad07eSHeiner Kallweit }, 3656c7ad07eSHeiner Kallweit { 3666c7ad07eSHeiner Kallweit .type = DREAM_CHEEKY, 3676c7ad07eSHeiner Kallweit .name = "Dream Cheeky Webmail Notifier", 3686c7ad07eSHeiner Kallweit .short_name = "dream_cheeky", 369*116c3f4aSJonathan Teh .max_brightness = 63, 3705bc83936SHeiner Kallweit .num_leds = 1, 3716c7ad07eSHeiner Kallweit .report_size = 9, 3726c7ad07eSHeiner Kallweit .report_type = RAW_REQUEST, 3736c7ad07eSHeiner Kallweit .init = dream_cheeky_init, 3746c7ad07eSHeiner Kallweit .write = dream_cheeky_write, 3756c7ad07eSHeiner Kallweit }, 376007414e8SHeiner Kallweit { 377007414e8SHeiner Kallweit .type = THINGM, 378007414e8SHeiner Kallweit .name = "ThingM blink(1)", 379007414e8SHeiner Kallweit .short_name = "thingm", 380007414e8SHeiner Kallweit .max_brightness = 255, 381007414e8SHeiner Kallweit .num_leds = 2, 382007414e8SHeiner Kallweit .report_size = 9, 383007414e8SHeiner Kallweit .report_type = RAW_REQUEST, 384007414e8SHeiner Kallweit .init = thingm_init, 385007414e8SHeiner Kallweit .write = thingm_write, 386007414e8SHeiner Kallweit }, 387de908650SHeiner Kallweit { 388de908650SHeiner Kallweit .type = DELCOM, 389de908650SHeiner Kallweit .name = "Delcom Visual Signal Indicator G2", 390de908650SHeiner Kallweit .short_name = "delcom", 391de908650SHeiner Kallweit .max_brightness = 100, 392de908650SHeiner Kallweit .num_leds = 1, 393de908650SHeiner Kallweit .report_size = 8, 394de908650SHeiner Kallweit .report_type = RAW_REQUEST, 395de908650SHeiner Kallweit .init = delcom_init, 396de908650SHeiner Kallweit .write = delcom_write, 397de908650SHeiner Kallweit }, 3989d1e048cSHeiner Kallweit { 3999d1e048cSHeiner Kallweit .type = LUXAFOR, 4009d1e048cSHeiner Kallweit .name = "Greynut Luxafor", 4019d1e048cSHeiner Kallweit .short_name = "luxafor", 4029d1e048cSHeiner Kallweit .max_brightness = 255, 4039d1e048cSHeiner Kallweit .num_leds = 6, 4049d1e048cSHeiner Kallweit .report_size = 9, 4059d1e048cSHeiner Kallweit .report_type = OUTPUT_REPORT, 4069d1e048cSHeiner Kallweit .write = luxafor_write, 4079d1e048cSHeiner Kallweit }, 4086c7ad07eSHeiner Kallweit }; 4096c7ad07eSHeiner Kallweit 4106c7ad07eSHeiner Kallweit static int hidled_init_led(struct hidled_led *led, const char *color_name, 4115bc83936SHeiner Kallweit struct hidled_rgb *rgb, unsigned int minor) 4126c7ad07eSHeiner Kallweit { 4135bc83936SHeiner Kallweit const struct hidled_config *config = rgb->ldev->config; 4146c7ad07eSHeiner Kallweit 4155bc83936SHeiner Kallweit if (config->num_leds > 1) 4165bc83936SHeiner Kallweit snprintf(led->name, sizeof(led->name), "%s%u:%s:led%u", 4175bc83936SHeiner Kallweit config->short_name, minor, color_name, rgb->num); 4185bc83936SHeiner Kallweit else 4195bc83936SHeiner Kallweit snprintf(led->name, sizeof(led->name), "%s%u:%s", 4205bc83936SHeiner Kallweit config->short_name, minor, color_name); 4215bc83936SHeiner Kallweit led->cdev.name = led->name; 4225bc83936SHeiner Kallweit led->cdev.max_brightness = config->max_brightness; 4235bc83936SHeiner Kallweit led->cdev.brightness_set_blocking = config->write; 4245bc83936SHeiner Kallweit led->cdev.flags = LED_HW_PLUGGABLE; 4255bc83936SHeiner Kallweit led->rgb = rgb; 4265bc83936SHeiner Kallweit 4275bc83936SHeiner Kallweit return devm_led_classdev_register(&rgb->ldev->hdev->dev, &led->cdev); 4286c7ad07eSHeiner Kallweit } 4296c7ad07eSHeiner Kallweit 4305bc83936SHeiner Kallweit static int hidled_init_rgb(struct hidled_rgb *rgb, unsigned int minor) 4316c7ad07eSHeiner Kallweit { 4326c7ad07eSHeiner Kallweit int ret; 4336c7ad07eSHeiner Kallweit 4346c7ad07eSHeiner Kallweit /* Register the red diode */ 4355bc83936SHeiner Kallweit ret = hidled_init_led(&rgb->red, "red", rgb, minor); 4366c7ad07eSHeiner Kallweit if (ret) 4376c7ad07eSHeiner Kallweit return ret; 4386c7ad07eSHeiner Kallweit 4396c7ad07eSHeiner Kallweit /* Register the green diode */ 4405bc83936SHeiner Kallweit ret = hidled_init_led(&rgb->green, "green", rgb, minor); 4416c7ad07eSHeiner Kallweit if (ret) 4426c7ad07eSHeiner Kallweit return ret; 4436c7ad07eSHeiner Kallweit 4446c7ad07eSHeiner Kallweit /* Register the blue diode */ 4455bc83936SHeiner Kallweit return hidled_init_led(&rgb->blue, "blue", rgb, minor); 4466c7ad07eSHeiner Kallweit } 4476c7ad07eSHeiner Kallweit 4486c7ad07eSHeiner Kallweit static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id) 4496c7ad07eSHeiner Kallweit { 4506c7ad07eSHeiner Kallweit struct hidled_device *ldev; 4516c7ad07eSHeiner Kallweit unsigned int minor; 4526c7ad07eSHeiner Kallweit int ret, i; 4536c7ad07eSHeiner Kallweit 4546c7ad07eSHeiner Kallweit ldev = devm_kzalloc(&hdev->dev, sizeof(*ldev), GFP_KERNEL); 4556c7ad07eSHeiner Kallweit if (!ldev) 4566c7ad07eSHeiner Kallweit return -ENOMEM; 4576c7ad07eSHeiner Kallweit 4583d1355b3SHeiner Kallweit ldev->buf = devm_kmalloc(&hdev->dev, MAX_REPORT_SIZE, GFP_KERNEL); 4593d1355b3SHeiner Kallweit if (!ldev->buf) 4603d1355b3SHeiner Kallweit return -ENOMEM; 4613d1355b3SHeiner Kallweit 4626c7ad07eSHeiner Kallweit ret = hid_parse(hdev); 4636c7ad07eSHeiner Kallweit if (ret) 4646c7ad07eSHeiner Kallweit return ret; 4656c7ad07eSHeiner Kallweit 4666c7ad07eSHeiner Kallweit ldev->hdev = hdev; 4676c7ad07eSHeiner Kallweit mutex_init(&ldev->lock); 4686c7ad07eSHeiner Kallweit 4696c7ad07eSHeiner Kallweit for (i = 0; !ldev->config && i < ARRAY_SIZE(hidled_configs); i++) 4706c7ad07eSHeiner Kallweit if (hidled_configs[i].type == id->driver_data) 4716c7ad07eSHeiner Kallweit ldev->config = &hidled_configs[i]; 4726c7ad07eSHeiner Kallweit 4736c7ad07eSHeiner Kallweit if (!ldev->config) 4746c7ad07eSHeiner Kallweit return -EINVAL; 4756c7ad07eSHeiner Kallweit 4766c7ad07eSHeiner Kallweit if (ldev->config->init) { 4776c7ad07eSHeiner Kallweit ret = ldev->config->init(ldev); 4786c7ad07eSHeiner Kallweit if (ret) 4796c7ad07eSHeiner Kallweit return ret; 4806c7ad07eSHeiner Kallweit } 4816c7ad07eSHeiner Kallweit 4825bc83936SHeiner Kallweit ldev->rgb = devm_kcalloc(&hdev->dev, ldev->config->num_leds, 4835bc83936SHeiner Kallweit sizeof(struct hidled_rgb), GFP_KERNEL); 4845bc83936SHeiner Kallweit if (!ldev->rgb) 4855bc83936SHeiner Kallweit return -ENOMEM; 4865bc83936SHeiner Kallweit 4876c7ad07eSHeiner Kallweit ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); 4886c7ad07eSHeiner Kallweit if (ret) 4896c7ad07eSHeiner Kallweit return ret; 4906c7ad07eSHeiner Kallweit 4916c7ad07eSHeiner Kallweit minor = ((struct hidraw *) hdev->hidraw)->minor; 4926c7ad07eSHeiner Kallweit 4935bc83936SHeiner Kallweit for (i = 0; i < ldev->config->num_leds; i++) { 4945bc83936SHeiner Kallweit ldev->rgb[i].ldev = ldev; 4955bc83936SHeiner Kallweit ldev->rgb[i].num = i; 4965bc83936SHeiner Kallweit ret = hidled_init_rgb(&ldev->rgb[i], minor); 4976c7ad07eSHeiner Kallweit if (ret) { 4986c7ad07eSHeiner Kallweit hid_hw_stop(hdev); 4996c7ad07eSHeiner Kallweit return ret; 5006c7ad07eSHeiner Kallweit } 5015bc83936SHeiner Kallweit } 5026c7ad07eSHeiner Kallweit 5036c7ad07eSHeiner Kallweit hid_info(hdev, "%s initialized\n", ldev->config->name); 5046c7ad07eSHeiner Kallweit 5056c7ad07eSHeiner Kallweit return 0; 5066c7ad07eSHeiner Kallweit } 5076c7ad07eSHeiner Kallweit 5086c7ad07eSHeiner Kallweit static const struct hid_device_id hidled_table[] = { 5096c7ad07eSHeiner Kallweit { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, 5106c7ad07eSHeiner Kallweit USB_DEVICE_ID_RI_KA_WEBMAIL), .driver_data = RISO_KAGAKU }, 5116c7ad07eSHeiner Kallweit { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 5126c7ad07eSHeiner Kallweit USB_DEVICE_ID_DREAM_CHEEKY_WN), .driver_data = DREAM_CHEEKY }, 5136c7ad07eSHeiner Kallweit { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 5146c7ad07eSHeiner Kallweit USB_DEVICE_ID_DREAM_CHEEKY_FA), .driver_data = DREAM_CHEEKY }, 515007414e8SHeiner Kallweit { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, 516007414e8SHeiner Kallweit USB_DEVICE_ID_BLINK1), .driver_data = THINGM }, 517de908650SHeiner Kallweit { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, 518de908650SHeiner Kallweit USB_DEVICE_ID_DELCOM_VISUAL_IND), .driver_data = DELCOM }, 5199d1e048cSHeiner Kallweit { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, 5209d1e048cSHeiner Kallweit USB_DEVICE_ID_LUXAFOR), .driver_data = LUXAFOR }, 5216c7ad07eSHeiner Kallweit { } 5226c7ad07eSHeiner Kallweit }; 5236c7ad07eSHeiner Kallweit MODULE_DEVICE_TABLE(hid, hidled_table); 5246c7ad07eSHeiner Kallweit 5256c7ad07eSHeiner Kallweit static struct hid_driver hidled_driver = { 5266c7ad07eSHeiner Kallweit .name = "hid-led", 5276c7ad07eSHeiner Kallweit .probe = hidled_probe, 5286c7ad07eSHeiner Kallweit .id_table = hidled_table, 5296c7ad07eSHeiner Kallweit }; 5306c7ad07eSHeiner Kallweit 5316c7ad07eSHeiner Kallweit module_hid_driver(hidled_driver); 5326c7ad07eSHeiner Kallweit 5336c7ad07eSHeiner Kallweit MODULE_LICENSE("GPL"); 5346c7ad07eSHeiner Kallweit MODULE_AUTHOR("Heiner Kallweit <hkallweit1@gmail.com>"); 5356c7ad07eSHeiner Kallweit MODULE_DESCRIPTION("Simple USB RGB LED driver"); 536