diff options
| author | Vivien Didelot <vivien.didelot@savoirfairelinux.com> | 2014-04-14 16:50:19 -0400 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2014-04-15 08:42:51 -0400 |
| commit | f70ed8a6f7cd9c55cc16287c584cb26efb53cbd7 (patch) | |
| tree | 1a63df88938c20c499f97c5e7bb3bab7fe32fa42 | |
| parent | aee114fd3c94f1be0f95af84d6ed25cd47702c41 (diff) | |
HID: thingm: refactor blink(1) support
This patch refactors the way the thingm driver registers a blink(1) LED.
In order to make the driver simpler and more standard, drop the "rgb"
sysfs attribute and create one instance of LED class per RGB channel.
Actually, the name of the LED class instance registered for a blink(1)
device is "blink1::ABCD", where ABCD is the last 4 chars of the serial
number. The driver now registers 3 instances per RGB chip, named
"thingmX:{red,green,blue}:ledY" where X is the hidraw minor number and Y
is the RGB chip number (as seen by the firmware).
This patch also uses work queues to defer calls with the device, which
now allows triggers to work as expected with this LED device.
Also remove the brightness structure field and the brightness_get
backend, as it is already handled by the LED class, and changes the
prefix of functions and structures to thingm_ to match the driver name.
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | Documentation/ABI/testing/sysfs-driver-hid-thingm | 8 | ||||
| -rw-r--r-- | drivers/hid/hid-thingm.c | 304 | ||||
| -rw-r--r-- | drivers/leds/Kconfig | 2 |
3 files changed, 209 insertions, 105 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-thingm b/Documentation/ABI/testing/sysfs-driver-hid-thingm deleted file mode 100644 index 735c5cb0bb07..000000000000 --- a/Documentation/ABI/testing/sysfs-driver-hid-thingm +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | What: /sys/class/leds/blink1::<serial>/rgb | ||
| 2 | Date: January 2013 | ||
| 3 | Contact: Vivien Didelot <vivien.didelot@savoirfairelinux.com> | ||
| 4 | Description: The ThingM blink1 is an USB RGB LED. The color notation is | ||
| 5 | 3-byte hexadecimal. Read this attribute to get the last set | ||
| 6 | color. Write the 24-bit hexadecimal color to change the current | ||
| 7 | LED color. The default color is full white (0xFFFFFF). | ||
| 8 | For instance, set the color to green with: echo 00FF00 > rgb | ||
diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index e3b6647e00ce..0af0eb446636 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * ThingM blink(1) USB RGB LED driver | 2 | * ThingM blink(1) USB RGB LED driver |
| 3 | * | 3 | * |
| 4 | * Copyright 2013 Savoir-faire Linux Inc. | 4 | * Copyright 2013-2014 Savoir-faire Linux Inc. |
| 5 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> | 5 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> |
| 6 | * | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
| @@ -10,170 +10,280 @@ | |||
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include <linux/hid.h> | 12 | #include <linux/hid.h> |
| 13 | #include <linux/hidraw.h> | ||
| 13 | #include <linux/leds.h> | 14 | #include <linux/leds.h> |
| 14 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/mutex.h> | ||
| 17 | #include <linux/workqueue.h> | ||
| 15 | 18 | ||
| 16 | #include "hid-ids.h" | 19 | #include "hid-ids.h" |
| 17 | 20 | ||
| 18 | #define BLINK1_CMD_SIZE 9 | 21 | #define REPORT_ID 1 |
| 22 | #define REPORT_SIZE 9 | ||
| 19 | 23 | ||
| 20 | #define blink1_rgb_to_r(rgb) ((rgb & 0xFF0000) >> 16) | 24 | /* Firmware major number of supported devices */ |
| 21 | #define blink1_rgb_to_g(rgb) ((rgb & 0x00FF00) >> 8) | 25 | #define THINGM_MAJOR_MK1 '1' |
| 22 | #define blink1_rgb_to_b(rgb) ((rgb & 0x0000FF) >> 0) | ||
| 23 | 26 | ||
| 24 | /** | 27 | struct thingm_fwinfo { |
| 25 | * struct blink1_data - blink(1) device specific data | 28 | char major; |
| 26 | * @hdev: HID device. | 29 | unsigned numrgb; |
| 27 | * @led_cdev: LED class instance. | 30 | unsigned first; |
| 28 | * @rgb: 8-bit per channel RGB notation. | 31 | }; |
| 29 | * @brightness: brightness coefficient. | 32 | |
| 30 | */ | 33 | const struct thingm_fwinfo thingm_fwinfo[] = { |
| 31 | struct blink1_data { | 34 | { |
| 35 | .major = THINGM_MAJOR_MK1, | ||
| 36 | .numrgb = 1, | ||
| 37 | .first = 0, | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | |||
| 41 | /* A red, green or blue channel, part of an RGB chip */ | ||
| 42 | struct thingm_led { | ||
| 43 | struct thingm_rgb *rgb; | ||
| 44 | struct led_classdev ldev; | ||
| 45 | char name[32]; | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* Basically a WS2812 5050 RGB LED chip */ | ||
| 49 | struct thingm_rgb { | ||
| 50 | struct thingm_device *tdev; | ||
| 51 | struct thingm_led red; | ||
| 52 | struct thingm_led green; | ||
| 53 | struct thingm_led blue; | ||
| 54 | struct work_struct work; | ||
| 55 | u8 num; | ||
| 56 | }; | ||
| 57 | |||
| 58 | struct thingm_device { | ||
| 32 | struct hid_device *hdev; | 59 | struct hid_device *hdev; |
| 33 | struct led_classdev led_cdev; | 60 | struct { |
| 34 | u32 rgb; | 61 | char major; |
| 35 | u8 brightness; | 62 | char minor; |
| 63 | } version; | ||
| 64 | const struct thingm_fwinfo *fwinfo; | ||
| 65 | struct mutex lock; | ||
| 66 | struct thingm_rgb *rgb; | ||
| 36 | }; | 67 | }; |
| 37 | 68 | ||
| 38 | static int blink1_send_command(struct blink1_data *data, | 69 | static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) |
| 39 | u8 buf[BLINK1_CMD_SIZE]) | ||
| 40 | { | 70 | { |
| 41 | int ret; | 71 | int ret; |
| 42 | 72 | ||
| 43 | hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n", | 73 | hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", |
| 44 | buf[0], buf[1], buf[2], buf[3], buf[4], | 74 | buf[0], buf[1], buf[2], buf[3], buf[4], |
| 45 | buf[5], buf[6], buf[7], buf[8]); | 75 | buf[5], buf[6], buf[7], buf[8]); |
| 46 | 76 | ||
| 47 | ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE, | 77 | ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, |
| 48 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | 78 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); |
| 49 | 79 | ||
| 50 | return ret < 0 ? ret : 0; | 80 | return ret < 0 ? ret : 0; |
| 51 | } | 81 | } |
| 52 | 82 | ||
| 53 | static int blink1_update_color(struct blink1_data *data) | 83 | static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) |
| 54 | { | 84 | { |
| 55 | u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 }; | 85 | int ret; |
| 56 | 86 | ||
| 57 | if (data->brightness) { | 87 | ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, |
| 58 | unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness); | 88 | HID_FEATURE_REPORT, HID_REQ_GET_REPORT); |
| 89 | if (ret < 0) | ||
| 90 | return ret; | ||
| 59 | 91 | ||
| 60 | buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef); | 92 | hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", |
| 61 | buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef); | 93 | buf[0], buf[1], buf[2], buf[3], buf[4], |
| 62 | buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef); | 94 | buf[5], buf[6], buf[7], buf[8]); |
| 63 | } | ||
| 64 | 95 | ||
| 65 | return blink1_send_command(data, buf); | 96 | return 0; |
| 66 | } | 97 | } |
| 67 | 98 | ||
| 68 | static void blink1_led_set(struct led_classdev *led_cdev, | 99 | static int thingm_version(struct thingm_device *tdev) |
| 69 | enum led_brightness brightness) | ||
| 70 | { | 100 | { |
| 71 | struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); | 101 | u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 }; |
| 102 | int err; | ||
| 103 | |||
| 104 | err = thingm_send(tdev, buf); | ||
| 105 | if (err) | ||
| 106 | return err; | ||
| 107 | |||
| 108 | err = thingm_recv(tdev, buf); | ||
| 109 | if (err) | ||
| 110 | return err; | ||
| 72 | 111 | ||
| 73 | data->brightness = brightness; | 112 | tdev->version.major = buf[3]; |
| 74 | if (blink1_update_color(data)) | 113 | tdev->version.minor = buf[4]; |
| 75 | hid_err(data->hdev, "failed to update color\n"); | 114 | |
| 115 | return 0; | ||
| 76 | } | 116 | } |
| 77 | 117 | ||
| 78 | static enum led_brightness blink1_led_get(struct led_classdev *led_cdev) | 118 | static int thingm_write_color(struct thingm_rgb *rgb) |
| 79 | { | 119 | { |
| 80 | struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); | 120 | u8 buf[REPORT_SIZE] = { REPORT_ID, 'n', 0, 0, 0, 0, 0, 0, 0 }; |
| 121 | |||
| 122 | buf[2] = rgb->red.ldev.brightness; | ||
| 123 | buf[3] = rgb->green.ldev.brightness; | ||
| 124 | buf[4] = rgb->blue.ldev.brightness; | ||
| 81 | 125 | ||
| 82 | return data->brightness; | 126 | return thingm_send(rgb->tdev, buf); |
| 83 | } | 127 | } |
| 84 | 128 | ||
| 85 | static ssize_t blink1_show_rgb(struct device *dev, | 129 | static void thingm_work(struct work_struct *work) |
| 86 | struct device_attribute *attr, char *buf) | ||
| 87 | { | 130 | { |
| 88 | struct blink1_data *data = dev_get_drvdata(dev->parent); | 131 | struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work); |
| 89 | 132 | ||
| 90 | return sprintf(buf, "%.6X\n", data->rgb); | 133 | mutex_lock(&rgb->tdev->lock); |
| 134 | |||
| 135 | if (thingm_write_color(rgb)) | ||
