Merge tag 'pnp-extra-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[deliverable/linux.git] / drivers / hid / hid-led.c
index 4075f095c2a22820e38372d3c57a1d98c7325315..d8d55f37b4f5604e2d10672513267c483726f05e 100644 (file)
@@ -26,6 +26,9 @@ enum hidled_report_type {
 enum hidled_type {
        RISO_KAGAKU,
        DREAM_CHEEKY,
+       THINGM,
+       DELCOM,
+       LUXAFOR,
 };
 
 static unsigned const char riso_kagaku_tbl[] = {
@@ -42,6 +45,28 @@ static unsigned const char riso_kagaku_tbl[] = {
 
 #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;
 
@@ -53,7 +78,6 @@ struct hidled_config {
        int                     num_leds;
        size_t                  report_size;
        enum hidled_report_type report_type;
-       u8                      report_id;
        int (*init)(struct hidled_device *ldev);
        int (*write)(struct led_classdev *cdev, enum led_brightness br);
 };
@@ -92,8 +116,6 @@ static int hidled_send(struct hidled_device *ldev, __u8 *buf)
 {
        int ret;
 
-       buf[0] = ldev->config->report_id;
-
        mutex_lock(&ldev->lock);
 
        if (ldev->config->report_type == RAW_REQUEST)
@@ -123,8 +145,6 @@ static int hidled_recv(struct hidled_device *ldev, __u8 *buf)
        if (ldev->config->report_type != RAW_REQUEST)
                return -EINVAL;
 
-       buf[0] = ldev->config->report_id;
-
        mutex_lock(&ldev->lock);
 
        ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
@@ -198,6 +218,131 @@ static int dream_cheeky_init(struct hidled_device *ldev)
        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,
@@ -207,7 +352,6 @@ static const struct hidled_config hidled_configs[] = {
                .num_leds = 1,
                .report_size = 6,
                .report_type = OUTPUT_REPORT,
-               .report_id = 0,
                .write = riso_kagaku_write,
        },
        {
@@ -218,10 +362,41 @@ static const struct hidled_config hidled_configs[] = {
                .num_leds = 1,
                .report_size = 9,
                .report_type = RAW_REQUEST,
-               .report_id = 0,
                .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,
@@ -325,6 +500,12 @@ static const struct hid_device_id hidled_table[] = {
          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);
This page took 0.041716 seconds and 5 git commands to generate.