Merge branch 'for-4.8/hid-led' into for-linus
authorJiri Kosina <jkosina@suse.cz>
Thu, 28 Jul 2016 08:49:23 +0000 (10:49 +0200)
committerJiri Kosina <jkosina@suse.cz>
Thu, 28 Jul 2016 08:49:23 +0000 (10:49 +0200)
Conflicts:
drivers/hid/hid-thingm.c

MAINTAINERS
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hid/hid-led.c [new file with mode: 0644]
drivers/hid/hid-thingm.c [deleted file]
drivers/usb/misc/Kconfig
drivers/usb/misc/Makefile
drivers/usb/misc/usbled.c [deleted file]

index 0f148d3..96158b9 100644 (file)
@@ -11314,11 +11314,6 @@ F:     Documentation/thermal/cpu-cooling-api.txt
 F:     drivers/thermal/cpu_cooling.c
 F:     include/linux/cpu_cooling.h
 
-THINGM BLINK(1) USB RGB LED DRIVER
-M:     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
-S:     Maintained
-F:     drivers/hid/hid-thingm.c
-
 THINKPAD ACPI EXTRAS DRIVER
 M:     Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
 L:     ibm-acpi-devel@lists.sourceforge.net
index a8e4808..78ac481 100644 (file)
@@ -388,6 +388,21 @@ config HID_LCPOWER
        ---help---
        Support for LC-Power RC1000MCE RF remote control.
 
+config HID_LED
+       tristate "Simple RGB LED support"
+       depends on HID
+       depends on LEDS_CLASS
+       ---help---
+       Support for simple RGB LED devices. Currently supported are:
+       - Riso Kagaku Webmail Notifier
+       - Dream Cheeky Webmail Notifier and Friends Alert
+       - ThingM blink(1)
+       - Delcom Visual Signal Indicator Generation 2
+       - Greynut Luxafor
+
+       To compile this driver as a module, choose M here: the
+       module will be called hid-led.
+
 config HID_LENOVO
        tristate "Lenovo / Thinkpad devices"
        depends on HID
@@ -819,11 +834,11 @@ config HID_THINGM
        tristate "ThingM blink(1) USB RGB LED"
        depends on HID
        depends on LEDS_CLASS
+       select HID_LED
        ---help---
-       Support for the ThingM blink(1) USB RGB LED. This driver registers a
-       Linux LED class instance, plus additional sysfs attributes to control
-       RGB colors, fade time and playing. The device is exposed through hidraw
-       to access other functions.
+       Support for the ThingM blink(1) USB RGB LED. This driver has been
+       merged into the generic hid led driver. Config symbol HID_THINGM
+       just selects HID_LED and will be removed soon.
 
 config HID_THRUSTMASTER
        tristate "ThrustMaster devices support"
index a55ec29..fc4b2aa 100644 (file)
@@ -91,12 +91,12 @@ obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
 obj-$(CONFIG_HID_STEELSERIES)  += hid-steelseries.o
 obj-$(CONFIG_HID_SUNPLUS)      += hid-sunplus.o
 obj-$(CONFIG_HID_GREENASIA)    += hid-gaff.o
-obj-$(CONFIG_HID_THINGM)       += hid-thingm.o
 obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
 obj-$(CONFIG_HID_TIVO)         += hid-tivo.o
 obj-$(CONFIG_HID_TOPSEED)      += hid-topseed.o
 obj-$(CONFIG_HID_TWINHAN)      += hid-twinhan.o
 obj-$(CONFIG_HID_UCLOGIC)      += hid-uclogic.o
+obj-$(CONFIG_HID_LED)          += hid-led.o
 obj-$(CONFIG_HID_XINMO)                += hid-xinmo.o
 obj-$(CONFIG_HID_ZEROPLUS)     += hid-zpff.o
 obj-$(CONFIG_HID_ZYDACRON)     += hid-zydacron.o
index 873a8af..08f53c7 100644 (file)
@@ -1879,8 +1879,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, USB_DEVICE_ID_DELCOM_VISUAL_IND) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) },
@@ -1964,6 +1967,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_LUXAFOR) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
@@ -2010,6 +2014,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
 #if IS_ENABLED(CONFIG_HID_ROCCAT)
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
@@ -2350,8 +2355,6 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
        { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) },
        { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
@@ -2488,7 +2491,6 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
 #endif
        { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
        { }
 };
 
index 59ff1fe..4ed9a4f 100644 (file)
 #define USB_VENDOR_ID_DEALEXTREAME     0x10c5
 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701        0x819a
 
+#define USB_VENDOR_ID_DELCOM           0x0fc5
+#define USB_DEVICE_ID_DELCOM_VISUAL_IND        0xb080
+
 #define USB_VENDOR_ID_DELORME          0x1163
 #define USB_DEVICE_ID_DELORME_EARTHMATE        0x0100
 #define USB_DEVICE_ID_DELORME_EM_LT20  0x0200
 #define USB_DEVICE_ID_ELECOM_BM084     0x0061
 
 #define USB_VENDOR_ID_DREAM_CHEEKY     0x1d34
+#define USB_DEVICE_ID_DREAM_CHEEKY_WN  0x0004
+#define USB_DEVICE_ID_DREAM_CHEEKY_FA  0x000a
 
 #define USB_VENDOR_ID_ELITEGROUP       0x03fc
 #define USB_DEVICE_ID_ELITEGROUP_05D8  0x05d8
 #define USB_DEVICE_ID_PICOLCD_BOOTLOADER       0xf002
 #define USB_DEVICE_ID_PICK16F1454      0x0042
 #define USB_DEVICE_ID_PICK16F1454_V2   0xf2f7
+#define USB_DEVICE_ID_LUXAFOR          0xf372
 
 #define USB_VENDOR_ID_MICROSOFT                0x045e
 #define USB_DEVICE_ID_SIDEWINDER_GV    0x003b
diff --git a/drivers/hid/hid-led.c b/drivers/hid/hid-led.c
new file mode 100644 (file)
index 0000000..d8d55f3
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ * Simple USB RGB LED driver
+ *
+ * Copyright 2016 Heiner Kallweit <hkallweit1@gmail.com>
+ * Based on drivers/hid/hid-thingm.c and
+ * drivers/usb/misc/usbled.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#include <linux/hid.h>
+#include <linux/hidraw.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include "hid-ids.h"
+
+enum hidled_report_type {
+       RAW_REQUEST,
+       OUTPUT_REPORT
+};
+
+enum hidled_type {
+       RISO_KAGAKU,
+       DREAM_CHEEKY,
+       THINGM,
+       DELCOM,
+       LUXAFOR,
+};
+
+static unsigned const char riso_kagaku_tbl[] = {
+/* R+2G+4B -> riso kagaku color index */
+       [0] = 0, /* black   */
+       [1] = 2, /* red     */
+       [2] = 1, /* green   */
+       [3] = 5, /* yellow  */
+       [4] = 3, /* blue    */
+       [5] = 6, /* magenta */
+       [6] = 4, /* cyan    */
+       [7] = 7  /* white   */
+};
+
+#define RISO_KAGAKU_IX(r, g, b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
+
+union delcom_packet {
+       __u8 data[8];
+       struct {
+               __u8 major_cmd;
+               __u8 minor_cmd;
+               __u8 data_lsb;
+               __u8 data_msb;
+       } tx;
+       struct {
+               __u8 cmd;
+       } rx;
+       struct {
+               __le16 family_code;
+               __le16 security_code;
+               __u8 fw_version;
+       } fw;
+};
+
+#define DELCOM_GREEN_LED       0
+#define DELCOM_RED_LED         1
+#define DELCOM_BLUE_LED                2
+
+struct hidled_device;
+struct hidled_rgb;
+
+struct hidled_config {
+       enum hidled_type        type;
+       const char              *name;
+       const char              *short_name;
+       enum led_brightness     max_brightness;
+       int                     num_leds;
+       size_t                  report_size;
+       enum hidled_report_type report_type;
+       int (*init)(struct hidled_device *ldev);
+       int (*write)(struct led_classdev *cdev, enum led_brightness br);
+};
+
+struct hidled_led {
+       struct led_classdev     cdev;
+       struct hidled_rgb       *rgb;
+       char                    name[32];
+};
+
+struct hidled_rgb {
+       struct hidled_device    *ldev;
+       struct hidled_led       red;
+       struct hidled_led       green;
+       struct hidled_led       blue;
+       u8                      num;
+};
+
+struct hidled_device {
+       const struct hidled_config *config;
+       struct hid_device       *hdev;
+       struct hidled_rgb       *rgb;
+       struct mutex            lock;
+};
+
+#define MAX_REPORT_SIZE                16
+
+#define to_hidled_led(arg) container_of(arg, struct hidled_led, cdev)
+
+static bool riso_kagaku_switch_green_blue;
+module_param(riso_kagaku_switch_green_blue, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(riso_kagaku_switch_green_blue,
+       "switch green and blue RGB component for Riso Kagaku devices");
+
+static int hidled_send(struct hidled_device *ldev, __u8 *buf)
+{
+       int ret;
+
+       mutex_lock(&ldev->lock);
+
+       if (ldev->config->report_type == RAW_REQUEST)
+               ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
+                                        ldev->config->report_size,
+                                        HID_FEATURE_REPORT,
+                                        HID_REQ_SET_REPORT);
+       else if (ldev->config->report_type == OUTPUT_REPORT)
+               ret = hid_hw_output_report(ldev->hdev, buf,
+                                          ldev->config->report_size);
+       else
+               ret = -EINVAL;
+
+       mutex_unlock(&ldev->lock);
+
+       if (ret < 0)
+               return ret;
+
+       return ret == ldev->config->report_size ? 0 : -EMSGSIZE;
+}
+
+/* reading data is supported for report type RAW_REQUEST only */
+static int hidled_recv(struct hidled_device *ldev, __u8 *buf)
+{
+       int ret;
+
+       if (ldev->config->report_type != RAW_REQUEST)
+               return -EINVAL;
+
+       mutex_lock(&ldev->lock);
+
+       ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
+                                ldev->config->report_size,
+                                HID_FEATURE_REPORT,
+                                HID_REQ_SET_REPORT);
+       if (ret < 0)
+               goto err;
+
+       ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
+                                ldev->config->report_size,
+                                HID_FEATURE_REPORT,
+                                HID_REQ_GET_REPORT);
+err:
+       mutex_unlock(&ldev->lock);
+
+       return ret < 0 ? ret : 0;
+}
+
+static u8 riso_kagaku_index(struct hidled_rgb *rgb)
+{
+       enum led_brightness r, g, b;
+
+       r = rgb->red.cdev.brightness;
+       g = rgb->green.cdev.brightness;
+       b = rgb->blue.cdev.brightness;
+
+       if (riso_kagaku_switch_green_blue)
+               return RISO_KAGAKU_IX(r, b, g);
+       else
+               return RISO_KAGAKU_IX(r, g, b);
+}
+
+static int riso_kagaku_write(struct led_classdev *cdev, enum led_brightness br)
+{
+       struct hidled_led *led = to_hidled_led(cdev);
+       struct hidled_rgb *rgb = led->rgb;
+       __u8 buf[MAX_REPORT_SIZE] = {};
+
+       buf[1] = riso_kagaku_index(rgb);
+
+       return hidled_send(rgb->ldev, buf);
+}
+
+static int dream_cheeky_write(struct led_classdev *cdev, enum led_brightness br)
+{
+       struct hidled_led *led = to_hidled_led(cdev);
+       struct hidled_rgb *rgb = led->rgb;
+       __u8 buf[MAX_REPORT_SIZE] = {};
+
+       buf[1] = rgb->red.cdev.brightness;
+       buf[2] = rgb->green.cdev.brightness;
+       buf[3] = rgb->blue.cdev.brightness;
+       buf[7] = 0x1a;
+       buf[8] = 0x05;
+
+       return hidled_send(rgb->ldev, buf);
+}
+
+static int dream_cheeky_init(struct hidled_device *ldev)
+{
+       __u8 buf[MAX_REPORT_SIZE] = {};
+
+       /* Dream Cheeky magic */
+       buf[1] = 0x1f;
+       buf[2] = 0x02;
+       buf[4] = 0x5f;
+       buf[7] = 0x1a;
+       buf[8] = 0x03;
+
+       return hidled_send(ldev, buf);
+}
+
+static int _thingm_write(struct led_classdev *cdev, enum led_brightness br,
+                        u8 offset)
+{
+       struct hidled_led *led = to_hidled_led(cdev);
+       __u8 buf[MAX_REPORT_SIZE] = { 1, 'c' };
+
+       buf[2] = led->rgb->red.cdev.brightness;
+       buf[3] = led->rgb->green.cdev.brightness;
+       buf[4] = led->rgb->blue.cdev.brightness;
+       buf[7] = led->rgb->num + offset;
+
+       return hidled_send(led->rgb->ldev, buf);
+}
+
+static int thingm_write_v1(struct led_classdev *cdev, enum led_brightness br)
+{
+       return _thingm_write(cdev, br, 0);
+}
+
+static int thingm_write(struct led_classdev *cdev, enum led_brightness br)
+{
+       return _thingm_write(cdev, br, 1);
+}
+
+static const struct hidled_config hidled_config_thingm_v1 = {
+       .name = "ThingM blink(1) v1",
+       .short_name = "thingm",
+       .max_brightness = 255,
+       .num_leds = 1,
+       .report_size = 9,
+       .report_type = RAW_REQUEST,
+       .write = thingm_write_v1,
+};
+
+static int thingm_init(struct hidled_device *ldev)
+{
+       __u8 buf[MAX_REPORT_SIZE] = { 1, 'v' };
+       int ret;
+
+       ret = hidled_recv(ldev, buf);
+       if (ret)
+               return ret;
+
+       /* Check for firmware major version 1 */
+       if (buf[3] == '1')
+               ldev->config = &hidled_config_thingm_v1;
+
+       return 0;
+}
+
+static inline int delcom_get_lednum(const struct hidled_led *led)
+{
+       if (led == &led->rgb->red)
+               return DELCOM_RED_LED;
+       else if (led == &led->rgb->green)
+               return DELCOM_GREEN_LED;
+       else
+               return DELCOM_BLUE_LED;
+}
+
+static int delcom_enable_led(struct hidled_led *led)
+{
+       union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 12 };
+
+       dp.tx.data_lsb = 1 << delcom_get_lednum(led);
+       dp.tx.data_msb = 0;
+
+       return hidled_send(led->rgb->ldev, dp.data);
+}
+
+static int delcom_set_pwm(struct hidled_led *led)
+{
+       union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 34 };
+
+       dp.tx.data_lsb = delcom_get_lednum(led);
+       dp.tx.data_msb = led->cdev.brightness;
+
+       return hidled_send(led->rgb->ldev, dp.data);
+}
+
+static int delcom_write(struct led_classdev *cdev, enum led_brightness br)
+{
+       struct hidled_led *led = to_hidled_led(cdev);
+       int ret;
+
+       /*
+        * enable LED
+        * We can't do this in the init function already because the device
+        * is internally reset later.
+        */
+       ret = delcom_enable_led(led);
+       if (ret)
+               return ret;
+
+       return delcom_set_pwm(led);
+}
+
+static int delcom_init(struct hidled_device *ldev)
+{
+       union delcom_packet dp = { .rx.cmd = 104 };
+       int ret;
+
+       ret = hidled_recv(ldev, dp.data);
+       if (ret)
+               return ret;
+       /*
+        * Several Delcom devices share the same USB VID/PID
+        * Check for family id 2 for Visual Signal Indicator
+        */
+       return le16_to_cpu(dp.fw.family_code) == 2 ? 0 : -ENODEV;
+}
+
+static int luxafor_write(struct led_classdev *cdev, enum led_brightness br)
+{
+       struct hidled_led *led = to_hidled_led(cdev);
+       __u8 buf[MAX_REPORT_SIZE] = { [1] = 1 };
+
+       buf[2] = led->rgb->num + 1;
+       buf[3] = led->rgb->red.cdev.brightness;
+       buf[4] = led->rgb->green.cdev.brightness;
+       buf[5] = led->rgb->blue.cdev.brightness;
+
+       return hidled_send(led->rgb->ldev, buf);
+}
+
+static const struct hidled_config hidled_configs[] = {
+       {
+               .type = RISO_KAGAKU,
+               .name = "Riso Kagaku Webmail Notifier",
+               .short_name = "riso_kagaku",
+               .max_brightness = 1,
+               .num_leds = 1,
+               .report_size = 6,
+               .report_type = OUTPUT_REPORT,
+               .write = riso_kagaku_write,
+       },
+       {
+               .type = DREAM_CHEEKY,
+               .name = "Dream Cheeky Webmail Notifier",
+               .short_name = "dream_cheeky",
+               .max_brightness = 31,
+               .num_leds = 1,
+               .report_size = 9,
+               .report_type = RAW_REQUEST,
+               .init = dream_cheeky_init,
+               .write = dream_cheeky_write,
+       },
+       {
+               .type = THINGM,
+               .name = "ThingM blink(1)",
+               .short_name = "thingm",
+               .max_brightness = 255,
+               .num_leds = 2,
+               .report_size = 9,
+               .report_type = RAW_REQUEST,
+               .init = thingm_init,
+               .write = thingm_write,
+       },
+       {
+               .type = DELCOM,
+               .name = "Delcom Visual Signal Indicator G2",
+               .short_name = "delcom",
+               .max_brightness = 100,
+               .num_leds = 1,
+               .report_size = 8,
+               .report_type = RAW_REQUEST,
+               .init = delcom_init,
+               .write = delcom_write,
+       },
+       {
+               .type = LUXAFOR,
+               .name = "Greynut Luxafor",
+               .short_name = "luxafor",
+               .max_brightness = 255,
+               .num_leds = 6,
+               .report_size = 9,
+               .report_type = OUTPUT_REPORT,
+               .write = luxafor_write,
+       },
+};
+
+static int hidled_init_led(struct hidled_led *led, const char *color_name,
+                          struct hidled_rgb *rgb, unsigned int minor)
+{
+       const struct hidled_config *config = rgb->ldev->config;
+
+       if (config->num_leds > 1)
+               snprintf(led->name, sizeof(led->name), "%s%u:%s:led%u",
+                        config->short_name, minor, color_name, rgb->num);
+       else
+               snprintf(led->name, sizeof(led->name), "%s%u:%s",
+                        config->short_name, minor, color_name);
+       led->cdev.name = led->name;
+       led->cdev.max_brightness = config->max_brightness;
+       led->cdev.brightness_set_blocking = config->write;
+       led->cdev.flags = LED_HW_PLUGGABLE;
+       led->rgb = rgb;
+
+       return devm_led_classdev_register(&rgb->ldev->hdev->dev, &led->cdev);
+}
+
+static int hidled_init_rgb(struct hidled_rgb *rgb, unsigned int minor)
+{
+       int ret;
+
+       /* Register the red diode */
+       ret = hidled_init_led(&rgb->red, "red", rgb, minor);
+       if (ret)
+               return ret;
+
+       /* Register the green diode */
+       ret = hidled_init_led(&rgb->green, "green", rgb, minor);
+       if (ret)
+               return ret;
+
+       /* Register the blue diode */
+       return hidled_init_led(&rgb->blue, "blue", rgb, minor);
+}
+
+static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct hidled_device *ldev;
+       unsigned int minor;
+       int ret, i;
+
+       ldev = devm_kzalloc(&hdev->dev, sizeof(*ldev), GFP_KERNEL);
+       if (!ldev)
+               return -ENOMEM;
+
+       ret = hid_parse(hdev);
+       if (ret)
+               return ret;
+
+       ldev->hdev = hdev;
+       mutex_init(&ldev->lock);
+
+       for (i = 0; !ldev->config && i < ARRAY_SIZE(hidled_configs); i++)
+               if (hidled_configs[i].type == id->driver_data)
+                       ldev->config = &hidled_configs[i];
+
+       if (!ldev->config)
+               return -EINVAL;
+
+       if (ldev->config->init) {
+               ret = ldev->config->init(ldev);
+               if (ret)
+                       return ret;
+       }
+
+       ldev->rgb = devm_kcalloc(&hdev->dev, ldev->config->num_leds,
+                                sizeof(struct hidled_rgb), GFP_KERNEL);
+       if (!ldev->rgb)
+               return -ENOMEM;
+
+       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (ret)
+               return ret;
+
+       minor = ((struct hidraw *) hdev->hidraw)->minor;
+
+       for (i = 0; i < ldev->config->num_leds; i++) {
+               ldev->rgb[i].ldev = ldev;
+               ldev->rgb[i].num = i;
+               ret = hidled_init_rgb(&ldev->rgb[i], minor);
+               if (ret) {
+                       hid_hw_stop(hdev);
+                       return ret;
+               }
+       }
+
+       hid_info(hdev, "%s initialized\n", ldev->config->name);
+
+       return 0;
+}
+
+static const struct hid_device_id hidled_table[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU,
+         USB_DEVICE_ID_RI_KA_WEBMAIL), .driver_data = RISO_KAGAKU },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
+         USB_DEVICE_ID_DREAM_CHEEKY_WN), .driver_data = DREAM_CHEEKY },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
+         USB_DEVICE_ID_DREAM_CHEEKY_FA), .driver_data = DREAM_CHEEKY },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THINGM,
+         USB_DEVICE_ID_BLINK1), .driver_data = THINGM },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM,
+         USB_DEVICE_ID_DELCOM_VISUAL_IND), .driver_data = DELCOM },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP,
+         USB_DEVICE_ID_LUXAFOR), .driver_data = LUXAFOR },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, hidled_table);
+
+static struct hid_driver hidled_driver = {
+       .name = "hid-led",
+       .probe = hidled_probe,
+       .id_table = hidled_table,
+};
+
+module_hid_driver(hidled_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Heiner Kallweit <hkallweit1@gmail.com>");
+MODULE_DESCRIPTION("Simple USB RGB LED driver");
diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c
deleted file mode 100644 (file)
index 9b4fec4..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * ThingM blink(1) USB RGB LED driver
- *
- * Copyright 2013-2014 Savoir-faire Linux Inc.
- *     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- */
-
-#include <linux/hid.h>
-#include <linux/hidraw.h>
-#include <linux/leds.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-
-#include "hid-ids.h"
-
-#define REPORT_ID      1
-#define REPORT_SIZE    9
-
-/* Firmware major number of supported devices */
-#define THINGM_MAJOR_MK1       '1'
-#define THINGM_MAJOR_MK2       '2'
-
-struct thingm_fwinfo {
-       char major;
-       unsigned numrgb;
-       unsigned first;
-};
-
-static const struct thingm_fwinfo thingm_fwinfo[] = {
-       {
-               .major = THINGM_MAJOR_MK1,
-               .numrgb = 1,
-               .first = 0,
-       }, {
-               .major = THINGM_MAJOR_MK2,
-               .numrgb = 2,
-               .first = 1,
-       }
-};
-
-/* A red, green or blue channel, part of an RGB chip */
-struct thingm_led {
-       struct thingm_rgb *rgb;
-       struct led_classdev ldev;
-       char name[32];
-};
-
-/* Basically a WS2812 5050 RGB LED chip */
-struct thingm_rgb {
-       struct thingm_device *tdev;
-       struct thingm_led red;
-       struct thingm_led green;
-       struct thingm_led blue;
-       u8 num;
-};
-
-struct thingm_device {
-       struct hid_device *hdev;
-       struct {
-               char major;
-               char minor;
-       } version;
-       const struct thingm_fwinfo *fwinfo;
-       struct mutex lock;
-       struct thingm_rgb *rgb;
-};
-
-static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
-{
-       int ret;
-
-       hid_dbg(tdev->hdev, "-> %d %c %7ph\n", buf[0], buf[1], &buf[2]);
-
-       mutex_lock(&tdev->lock);
-
-       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
-                       HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-
-       mutex_unlock(&tdev->lock);
-
-       return ret < 0 ? ret : 0;
-}
-
-static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
-{
-       int ret;
-
-       /*
-        * A read consists of two operations: sending the read command
-        * and the actual read from the device. Use the mutex to protect
-        * the full sequence of both operations.
-        */
-       mutex_lock(&tdev->lock);
-
-       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
-                       HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-       if (ret < 0)
-               goto err;
-
-       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
-                       HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
-       if (ret < 0)
-               goto err;
-
-       ret = 0;
-
-       hid_dbg(tdev->hdev, "<- %d %c %7ph\n", buf[0], buf[1], &buf[2]);
-err:
-       mutex_unlock(&tdev->lock);
-       return ret;
-}
-
-static int thingm_version(struct thingm_device *tdev)
-{
-       u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
-       int err;
-
-       err = thingm_recv(tdev, buf);
-       if (err)
-               return err;
-
-       tdev->version.major = buf[3];
-       tdev->version.minor = buf[4];
-
-       return 0;
-}
-
-static int thingm_write_color(struct thingm_rgb *rgb)
-{
-       u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 };
-
-       buf[2] = rgb->red.ldev.brightness;
-       buf[3] = rgb->green.ldev.brightness;
-       buf[4] = rgb->blue.ldev.brightness;
-
-       return thingm_send(rgb->tdev, buf);
-}
-
-static int thingm_led_set(struct led_classdev *ldev,
-                         enum led_brightness brightness)
-{
-       struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
-
-       return thingm_write_color(led->rgb);
-}
-
-static int thingm_init_led(struct thingm_led *led, const char *color_name,
-                          struct thingm_rgb *rgb, int minor)
-{
-       snprintf(led->name, sizeof(led->name), "thingm%d:%s:led%d",
-                minor, color_name, rgb->num);
-       led->ldev.name = led->name;
-       led->ldev.max_brightness = 255;
-       led->ldev.brightness_set_blocking = thingm_led_set;
-       led->ldev.flags = LED_HW_PLUGGABLE;
-       led->rgb = rgb;
-       return devm_led_classdev_register(&rgb->tdev->hdev->dev, &led->ldev);
-}
-
-static int thingm_init_rgb(struct thingm_rgb *rgb)
-{
-       const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor;
-       int err;
-
-       /* Register the red diode */
-       err = thingm_init_led(&rgb->red, "red", rgb, minor);
-       if (err)
-               return err;
-
-       /* Register the green diode */
-       err = thingm_init_led(&rgb->green, "green", rgb, minor);
-       if (err)
-               return err;
-
-       /* Register the blue diode */
-       return thingm_init_led(&rgb->blue, "blue", rgb, minor);
-}
-
-static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
-{
-       struct thingm_device *tdev;
-       int i, err;
-
-       tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device),
-                       GFP_KERNEL);
-       if (!tdev)
-               return -ENOMEM;
-
-       tdev->hdev = hdev;
-       hid_set_drvdata(hdev, tdev);
-
-       err = hid_parse(hdev);
-       if (err)
-               return err;
-
-       mutex_init(&tdev->lock);
-
-       err = thingm_version(tdev);
-       if (err)
-               return err;
-
-       hid_dbg(hdev, "firmware version: %c.%c\n",
-                       tdev->version.major, tdev->version.minor);
-
-       for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i)
-               if (thingm_fwinfo[i].major == tdev->version.major)
-                       tdev->fwinfo = &thingm_fwinfo[i];
-
-       if (!tdev->fwinfo) {
-               hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
-               return -ENODEV;
-       }
-
-       tdev->rgb = devm_kzalloc(&hdev->dev,
-                       sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
-                       GFP_KERNEL);
-       if (!tdev->rgb)
-               return -ENOMEM;
-
-       err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-       if (err)
-               return err;
-
-       for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
-               struct thingm_rgb *rgb = tdev->rgb + i;
-
-               rgb->tdev = tdev;
-               rgb->num = tdev->fwinfo->first + i;
-               err = thingm_init_rgb(rgb);
-               if (err) {
-                       hid_hw_stop(hdev);
-                       return err;
-               }
-       }
-
-       return 0;
-}
-
-static const struct hid_device_id thingm_table[] = {
-       { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
-       { }
-};
-MODULE_DEVICE_TABLE(hid, thingm_table);
-
-static struct hid_driver thingm_driver = {
-       .name = "thingm",
-       .probe = thingm_probe,
-       .id_table = thingm_table,
-};
-
-module_hid_driver(thingm_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Vivien Didelot <vivien.didelot@savoirfairelinux.com>");
-MODULE_DESCRIPTION("ThingM blink(1) USB RGB LED driver");
index e9e5ae5..9e5c835 100644 (file)
@@ -79,15 +79,6 @@ config USB_LCD
          To compile this driver as a module, choose M here: the
          module will be called usblcd.
 
-config USB_LED
-       tristate "USB LED driver support"
-       help
-         Say Y here if you want to connect an USBLED device to your 
-         computer's USB port.
-
-         To compile this driver as a module, choose M here: the
-         module will be called usbled.
-
 config USB_CYPRESS_CY7C63
        tristate "Cypress CY7C63xxx USB driver support"
        help
index 2769cf6..3d79faa 100644 (file)
@@ -15,7 +15,6 @@ obj-$(CONFIG_USB_IOWARRIOR)           += iowarrior.o
 obj-$(CONFIG_USB_ISIGHTFW)             += isight_firmware.o
 obj-$(CONFIG_USB_LCD)                  += usblcd.o
 obj-$(CONFIG_USB_LD)                   += ldusb.o
-obj-$(CONFIG_USB_LED)                  += usbled.o
 obj-$(CONFIG_USB_LEGOTOWER)            += legousbtower.o
 obj-$(CONFIG_USB_RIO500)               += rio500.o
 obj-$(CONFIG_USB_TEST)                 += usbtest.o
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c
deleted file mode 100644 (file)
index bdef0d6..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * USB LED driver
- *
- * Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation, version 2.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/usb.h>
-
-
-#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
-#define DRIVER_DESC "USB LED Driver"
-
-enum led_type {
-       DELCOM_VISUAL_SIGNAL_INDICATOR,
-       DREAM_CHEEKY_WEBMAIL_NOTIFIER,
-       RISO_KAGAKU_LED
-};
-
-/* the Webmail LED made by RISO KAGAKU CORP. decodes a color index
-   internally, we want to keep the red+green+blue sysfs api, so we decode
-   from 1-bit RGB to the riso kagaku color index according to this table... */
-
-static unsigned const char riso_kagaku_tbl[] = {
-/* R+2G+4B -> riso kagaku color index */
-       [0] = 0, /* black   */
-       [1] = 2, /* red     */
-       [2] = 1, /* green   */
-       [3] = 5, /* yellow  */
-       [4] = 3, /* blue    */
-       [5] = 6, /* magenta */
-       [6] = 4, /* cyan    */
-       [7] = 7  /* white   */
-};
-
-#define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
-
-/* table of devices that work with this driver */
-static const struct usb_device_id id_table[] = {
-       { USB_DEVICE(0x0fc5, 0x1223),
-                       .driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
-       { USB_DEVICE(0x1d34, 0x0004),
-                       .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
-       { USB_DEVICE(0x1d34, 0x000a),
-                       .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
-       { USB_DEVICE(0x1294, 0x1320),
-                       .driver_info = RISO_KAGAKU_LED },
-       { },
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-struct usb_led {
-       struct usb_device       *udev;
-       unsigned char           blue;
-       unsigned char           red;
-       unsigned char           green;
-       enum led_type           type;
-};
-
-static void change_color(struct usb_led *led)
-{
-       int retval = 0;
-       unsigned char *buffer;
-       int actlength;
-
-       buffer = kmalloc(8, GFP_KERNEL);
-       if (!buffer) {
-               dev_err(&led->udev->dev, "out of memory\n");
-               return;
-       }
-
-       switch (led->type) {
-       case DELCOM_VISUAL_SIGNAL_INDICATOR: {
-               unsigned char color = 0x07;
-
-               if (led->blue)
-                       color &= ~0x04;
-               if (led->red)
-                       color &= ~0x02;
-               if (led->green)
-                       color &= ~0x01;
-               dev_dbg(&led->udev->dev,
-                       "blue = %d, red = %d, green = %d, color = %.2x\n",
-                       led->blue, led->red, led->green, color);
-
-               retval = usb_control_msg(led->udev,
-                                       usb_sndctrlpipe(led->udev, 0),
-                                       0x12,
-                                       0xc8,
-                                       (0x02 * 0x100) + 0x0a,
-                                       (0x00 * 0x100) + color,
-                                       buffer,
-                                       8,
-                                       2000);
-               break;
-       }
-
-       case DREAM_CHEEKY_WEBMAIL_NOTIFIER:
-               dev_dbg(&led->udev->dev,
-                       "red = %d, green = %d, blue = %d\n",
-                       led->red, led->green, led->blue);
-
-               buffer[0] = led->red;
-               buffer[1] = led->green;
-               buffer[2] = led->blue;
-               buffer[3] = buffer[4] = buffer[5] = 0;
-               buffer[6] = 0x1a;
-               buffer[7] = 0x05;
-
-               retval = usb_control_msg(led->udev,
-                                       usb_sndctrlpipe(led->udev, 0),
-                                       0x09,
-                                       0x21,
-                                       0x200,
-                                       0,
-                                       buffer,
-                                       8,
-                                       2000);
-               break;
-
-       case RISO_KAGAKU_LED:
-               buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue);
-               buffer[1] = 0;
-               buffer[2] = 0;
-               buffer[3] = 0;
-               buffer[4] = 0;
-
-               retval = usb_interrupt_msg(led->udev,
-                       usb_sndctrlpipe(led->udev, 2),
-                       buffer, 5, &actlength, 1000 /*ms timeout*/);
-               break;
-
-       default:
-               dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
-       }
-
-       if (retval)
-               dev_dbg(&led->udev->dev, "retval = %d\n", retval);
-       kfree(buffer);
-}
-
-#define show_set(value)        \
-static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\
-                           char *buf)                                  \
-{                                                                      \
-       struct usb_interface *intf = to_usb_interface(dev);             \
-       struct usb_led *led = usb_get_intfdata(intf);                   \
-                                                                       \
-       return sprintf(buf, "%d\n", led->value);                        \
-}                                                                      \
-static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\
-                          const char *buf, size_t count)               \
-{                                                                      \
-       struct usb_interface *intf = to_usb_interface(dev);             \
-       struct usb_led *led = usb_get_intfdata(intf);                   \
-       int temp = simple_strtoul(buf, NULL, 10);                       \
-                                                                       \
-       led->value = temp;                                              \
-       change_color(led);                                              \
-       return count;                                                   \
-}                                                                      \
-static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value);
-show_set(blue);
-show_set(red);
-show_set(green);
-
-static int led_probe(struct usb_interface *interface,
-                    const struct usb_device_id *id)
-{
-       struct usb_device *udev = interface_to_usbdev(interface);
-       struct usb_led *dev = NULL;
-       int retval = -ENOMEM;
-
-       dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
-       if (dev == NULL) {
-               dev_err(&interface->dev, "out of memory\n");
-               goto error_mem;
-       }
-
-       dev->udev = usb_get_dev(udev);
-       dev->type = id->driver_info;
-
-       usb_set_intfdata(interface, dev);
-
-       retval = device_create_file(&interface->dev, &dev_attr_blue);
-       if (retval)
-               goto error;
-       retval = device_create_file(&interface->dev, &dev_attr_red);
-       if (retval)
-               goto error;
-       retval = device_create_file(&interface->dev, &dev_attr_green);
-       if (retval)
-               goto error;
-
-       if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
-               unsigned char *enable;
-
-               enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
-               if (!enable) {
-                       dev_err(&interface->dev, "out of memory\n");
-                       retval = -ENOMEM;
-                       goto error;
-               }
-
-               retval = usb_control_msg(udev,
-                                       usb_sndctrlpipe(udev, 0),
-                                       0x09,
-                                       0x21,
-                                       0x200,
-                                       0,
-                                       enable,
-                                       8,
-                                       2000);
-
-               kfree(enable);
-               if (retval != 8)
-                       goto error;
-       }
-
-       dev_info(&interface->dev, "USB LED device now attached\n");
-       return 0;
-
-error:
-       device_remove_file(&interface->dev, &dev_attr_blue);
-       device_remove_file(&interface->dev, &dev_attr_red);
-       device_remove_file(&interface->dev, &dev_attr_green);
-       usb_set_intfdata(interface, NULL);
-       usb_put_dev(dev->udev);
-       kfree(dev);
-error_mem:
-       return retval;
-}
-
-static void led_disconnect(struct usb_interface *interface)
-{
-       struct usb_led *dev;
-
-       dev = usb_get_intfdata(interface);
-
-       device_remove_file(&interface->dev, &dev_attr_blue);
-       device_remove_file(&interface->dev, &dev_attr_red);
-       device_remove_file(&interface->dev, &dev_attr_green);
-
-       /* first remove the files, then set the pointer to NULL */
-       usb_set_intfdata(interface, NULL);
-
-       usb_put_dev(dev->udev);
-
-       kfree(dev);
-
-       dev_info(&interface->dev, "USB LED now disconnected\n");
-}
-
-static struct usb_driver led_driver = {
-       .name =         "usbled",
-       .probe =        led_probe,
-       .disconnect =   led_disconnect,
-       .id_table =     id_table,
-};
-
-module_usb_driver(led_driver);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");