aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorVivien Didelot <vivien.didelot@savoirfairelinux.com>2013-01-22 12:01:21 -0500
committerJiri Kosina <jkosina@suse.cz>2013-02-19 05:31:46 -0500
commit30ba2fbde1840db440915491cdde235b72a11384 (patch)
treed83440cb8b97c8e063fc1be2e581def0f641ac6d /drivers/hid
parent89bdd0c6f38ccf0de43d5a36ede384a730f3394e (diff)
HID: add ThingM blink(1) USB RGB LED support
The ThingM blink(1) is an open source hardware USB RGB LED. It contains an internal EEPROM, allowing to configure up to 12 light patterns. A light pattern is a RGB color plus a fade time. This driver registers a LED class instance with additional sysfs attributes to support basic functions such as setting RGB colors, fade and playing. Other functions are still accessible through the hidraw interface. At this time, the only documentation for the device is the firmware source code from ThingM, plus a few schematics. They are available at: https://github.com/todbot/blink1 This patch is version 3. It updates the name of the source file, the driver and the led sysfs entry, according to comments from Jiri Kosina and Simon Wood. Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig10
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h3
-rw-r--r--drivers/hid/hid-thingm.c272
5 files changed, 287 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index bf88eca3e868..3d5294531ba5 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -655,6 +655,16 @@ config HID_TOPSEED
655 Say Y if you have a TopSeed Cyberlink or BTC Emprex or Conceptronic 655 Say Y if you have a TopSeed Cyberlink or BTC Emprex or Conceptronic
656 CLLRCMCE remote control. 656 CLLRCMCE remote control.
657 657
658config HID_THINGM
659 tristate "ThingM blink(1) USB RGB LED"
660 depends on USB_HID
661 depends on LEDS_CLASS
662 ---help---
663 Support for the ThingM blink(1) USB RGB LED. This driver registers a
664 Linux LED class instance, plus additional sysfs attributes to control
665 RGB colors, fade time and playing. The device is exposed through hidraw
666 to access other functions.
667
658config HID_THRUSTMASTER 668config HID_THRUSTMASTER
659 tristate "ThrustMaster devices support" 669 tristate "ThrustMaster devices support"
660 depends on USB_HID 670 depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index b62215716b2f..93017043f6f8 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -103,6 +103,7 @@ obj-$(CONFIG_HID_SONY) += hid-sony.o
103obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o 103obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
104obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o 104obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
105obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o 105obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
106obj-$(CONFIG_HID_THINGM) += hid-thingm.o
106obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o 107obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
107obj-$(CONFIG_HID_TIVO) += hid-tivo.o 108obj-$(CONFIG_HID_TIVO) += hid-tivo.o
108obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o 109obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index bf44af35439b..5f7115eb9412 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1699,6 +1699,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
1699 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, 1699 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
1700 { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, 1700 { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
1701 { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, 1701 { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
1702 { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
1702 { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, 1703 { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
1703 { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, 1704 { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
1704 { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) }, 1705 { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 104105208596..eebbbf6f0a9a 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -747,6 +747,9 @@
747#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 747#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010
748#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 748#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013
749 749
750#define USB_VENDOR_ID_THINGM 0x27b8
751#define USB_DEVICE_ID_BLINK1 0x01ed
752
750#define USB_VENDOR_ID_THRUSTMASTER 0x044f 753#define USB_VENDOR_ID_THRUSTMASTER 0x044f
751 754
752#define USB_VENDOR_ID_TIVO 0x150a 755#define USB_VENDOR_ID_TIVO 0x150a
diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c
new file mode 100644
index 000000000000..2055a52e9a20
--- /dev/null
+++ b/drivers/hid/hid-thingm.c
@@ -0,0 +1,272 @@
1/*
2 * ThingM blink(1) USB RGB LED driver
3 *
4 * Copyright 2013 Savoir-faire Linux Inc.
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, version 2.
10 */
11
12#include <linux/hid.h>
13#include <linux/leds.h>
14#include <linux/module.h>
15#include <linux/usb.h>
16
17#include "hid-ids.h"
18
19#define BLINK1_CMD_SIZE 9
20
21#define blink1_rgb_to_r(rgb) ((rgb & 0xFF0000) >> 16)
22#define blink1_rgb_to_g(rgb) ((rgb & 0x00FF00) >> 8)
23#define blink1_rgb_to_b(rgb) ((rgb & 0x0000FF) >> 0)
24
25/**
26 * struct blink1_data - blink(1) device specific data
27 * @hdev: HID device.
28 * @led_cdev: LED class instance.
29 * @rgb: 8-bit per channel RGB notation.
30 * @fade: fade time in hundredths of a second.
31 * @brightness: brightness coefficient.
32 * @play: play/pause in-memory patterns.
33 */
34struct blink1_data {
35 struct hid_device *hdev;
36 struct led_classdev led_cdev;
37 u32 rgb;
38 u16 fade;
39 u8 brightness;
40 bool play;
41};
42
43static int blink1_send_command(struct blink1_data *data,
44 u8 buf[BLINK1_CMD_SIZE])
45{
46 int ret;
47
48 hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n",
49 buf[0], buf[1], buf[2], buf[3], buf[4],
50 buf[5], buf[6], buf[7], buf[8]);
51
52 ret = data->hdev->hid_output_raw_report(data->hdev, buf,
53 BLINK1_CMD_SIZE, HID_FEATURE_REPORT);
54
55 return ret < 0 ? ret : 0;
56}
57
58static int blink1_update_color(struct blink1_data *data)
59{
60 u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 };
61
62 if (data->brightness) {
63 unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness);
64
65 buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef);
66 buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef);
67 buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef);
68 }
69
70 if (data->fade) {
71 buf[1] = 'c';
72 buf[5] = (data->fade & 0xFF00) >> 8;
73 buf[6] = (data->fade & 0x00FF);
74 }
75
76 return blink1_send_command(data, buf);
77}
78
79static void blink1_led_set(struct led_classdev *led_cdev,
80 enum led_brightness brightness)
81{
82 struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
83
84 data->brightness = brightness;
85 if (blink1_update_color(data))
86 hid_err(data->hdev, "failed to update color\n");
87}
88
89static enum led_brightness blink1_led_get(struct led_classdev *led_cdev)
90{
91 struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
92
93 return data->brightness;
94}
95
96static ssize_t blink1_show_rgb(struct device *dev,
97 struct device_attribute *attr, char *buf)
98{
99 struct blink1_data *data = dev_get_drvdata(dev->parent);
100
101 return sprintf(buf, "%.6X\n", data->rgb);
102}
103
104static ssize_t blink1_store_rgb(struct device *dev,
105 struct device_attribute *attr, const char *buf, size_t count)
106{
107 struct blink1_data *data = dev_get_drvdata(dev->parent);
108 long unsigned int rgb;
109 int ret;
110
111 ret = kstrtoul(buf, 16, &rgb);
112 if (ret)
113 return ret;
114
115 /* RGB triplet notation is 24-bit hexadecimal */
116 if (rgb > 0xFFFFFF)
117 return -EINVAL;
118
119 data->rgb = rgb;
120 ret = blink1_update_color(data);
121
122 return ret ? ret : count;
123}
124
125static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb);
126
127static ssize_t blink1_show_fade(struct device *dev,
128 struct device_attribute *attr, char *buf)
129{
130 struct blink1_data *data = dev_get_drvdata(dev->parent);
131
132 return sprintf(buf, "%d\n", data->fade * 10);
133}
134
135static ssize_t blink1_store_fade(struct device *dev,
136 struct device_attribute *attr, const char *buf, size_t count)
137{
138 struct blink1_data *data = dev_get_drvdata(dev->parent);
139 long unsigned int fade;
140 int ret;
141
142 ret = kstrtoul(buf, 10, &fade);
143 if (ret)
144 return ret;
145
146 /* blink(1) accepts 16-bit fade time, number of 10ms ticks */
147 fade = DIV_ROUND_CLOSEST(fade, 10);
148 if (fade > 65535)
149 return -EINVAL;
150
151 data->fade = fade;
152
153 return count;
154}
155
156static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR,
157 blink1_show_fade, blink1_store_fade);
158
159static ssize_t blink1_show_play(struct device *dev,
160 struct device_attribute *attr, char *buf)
161{
162 struct blink1_data *data = dev_get_drvdata(dev->parent);
163
164 return sprintf(buf, "%d\n", data->play);
165}
166
167static ssize_t blink1_store_play(struct device *dev,
168 struct device_attribute *attr, const char *buf, size_t count)
169{
170 struct blink1_data *data = dev_get_drvdata(dev->parent);
171 u8 cmd[BLINK1_CMD_SIZE] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 };
172 long unsigned int play;
173 int ret;
174
175 ret = kstrtoul(buf, 10, &play);
176 if (ret)
177 return ret;
178
179 data->play = !!play;
180 cmd[2] = data->play;
181 ret = blink1_send_command(data, cmd);
182
183 return ret ? ret : count;
184}
185
186static DEVICE_ATTR(play, S_IRUGO | S_IWUSR,
187 blink1_show_play, blink1_store_play);
188
189static const struct attribute_group blink1_sysfs_group = {
190 .attrs = (struct attribute *[]) {
191 &dev_attr_rgb.attr,
192 &dev_attr_fade.attr,
193 &dev_attr_play.attr,
194 NULL
195 },
196};
197
198static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
199{
200 struct blink1_data *data;
201 struct led_classdev *led;
202 char led_name[13];
203 int ret;
204
205 data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL);
206 if (!data)
207 return -ENOMEM;
208
209 hid_set_drvdata(hdev, data);
210 data->hdev = hdev;
211 data->rgb = 0xFFFFFF; /* set a default white color */
212
213 ret = hid_parse(hdev);
214 if (ret)
215 goto error;
216
217 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
218 if (ret)
219 goto error;
220
221 /* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */
222 led = &data->led_cdev;
223 snprintf(led_name, sizeof(led_name), "blink1::%s", hdev->uniq + 4);
224 led->name = led_name;
225 led->brightness_set = blink1_led_set;
226 led->brightness_get = blink1_led_get;
227 ret = led_classdev_register(&hdev->dev, led);
228 if (ret)
229 goto stop;
230
231 ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group);
232 if (ret)
233 goto remove_led;
234
235 return 0;
236
237remove_led:
238 led_classdev_unregister(led);
239stop:
240 hid_hw_stop(hdev);
241error:
242 return ret;
243}
244
245static void thingm_remove(struct hid_device *hdev)
246{
247 struct blink1_data *data = hid_get_drvdata(hdev);
248 struct led_classdev *led = &data->led_cdev;
249
250 sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group);
251 led_classdev_unregister(led);
252 hid_hw_stop(hdev);
253}
254
255static const struct hid_device_id thingm_table[] = {
256 { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
257 { }
258};
259MODULE_DEVICE_TABLE(hid, thingm_table);
260
261static struct hid_driver thingm_driver = {
262 .name = "thingm",
263 .probe = thingm_probe,
264 .remove = thingm_remove,
265 .id_table = thingm_table,
266};
267
268module_hid_driver(thingm_driver);
269
270MODULE_LICENSE("GPL");
271MODULE_AUTHOR("Vivien Didelot <vivien.didelot@savoirfairelinux.com>");
272MODULE_DESCRIPTION("ThingM blink(1) USB RGB LED driver");