aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiner Kallweit <hkallweit1@gmail.com>2016-06-17 15:20:46 -0400
committerJiri Kosina <jkosina@suse.cz>2016-06-17 16:28:32 -0400
commit6c7ad07e9e05a659496e26243643280610c13d3a (patch)
tree557f8983f185bf7638b37be58fb6e3bc640fee45
parentdcc4c2f61cdc7e0ab61b25b8d28205302497a8c4 (diff)
HID: migrate USB LED driver from usb misc to hid
This patch migrates the USB LED driver to the HID subsystem. Supported are Dream Cheeky Webmail Notifier / Friends Alert and Riso Kagaku Webmail Notifier. Benefits: - Avoid using USB low-level calls and use the HID subsystem instead (as this device provides a USB HID interface) - Use standard LED subsystem instead of proprietary sysfs entries, this allows e.g. to use the device with features like triggers Successfully tested with a Dream Cheeky Webmail Notifier and a Riso Kagaku Webmail Notifier compatible device. Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/Kconfig12
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c6
-rw-r--r--drivers/hid/hid-ids.h2
-rw-r--r--drivers/hid/hid-led.c288
5 files changed, 306 insertions, 3 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 5646ca4b95de..04bd2039b23b 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -388,6 +388,18 @@ config HID_LCPOWER
388 ---help--- 388 ---help---
389 Support for LC-Power RC1000MCE RF remote control. 389 Support for LC-Power RC1000MCE RF remote control.
390 390
391config HID_LED
392 tristate "Simple USB RGB LED support"
393 depends on HID
394 depends on LEDS_CLASS
395 ---help---
396 Support for simple USB RGB LED devices. Currently supported are the
397 Riso Kagaku Webmail Notifier and the Dream Cheeky Webmail Notifier
398 and Friends Alert.
399
400 To compile this driver as a module, choose M here: the
401 module will be called hid-led.
402
391config HID_LENOVO 403config HID_LENOVO
392 tristate "Lenovo / Thinkpad devices" 404 tristate "Lenovo / Thinkpad devices"
393 depends on HID 405 depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index a2fb562de748..86d71f60a967 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_HID_TIVO) += hid-tivo.o
96obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o 96obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
97obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o 97obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
98obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o 98obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
99obj-$(CONFIG_HID_LED) += hid-led.o
99obj-$(CONFIG_HID_XINMO) += hid-xinmo.o 100obj-$(CONFIG_HID_XINMO) += hid-xinmo.o
100obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o 101obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
101obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o 102obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 8ea3a26360e9..eb674ce75fab 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1879,6 +1879,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
1879 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, 1879 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
1880 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, 1880 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
1881 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, 1881 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
1882 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) },
1883 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) },
1882 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, 1884 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
1883 { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, 1885 { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) },
1884 { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) }, 1886 { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) },
@@ -2008,6 +2010,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
2008 { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, 2010 { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
2009 { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, 2011 { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
2010 { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, 2012 { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
2013 { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
2011#if IS_ENABLED(CONFIG_HID_ROCCAT) 2014#if IS_ENABLED(CONFIG_HID_ROCCAT)
2012 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, 2015 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
2013 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, 2016 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
@@ -2348,8 +2351,6 @@ static const struct hid_device_id hid_ignore_list[] = {
2348 { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) }, 2351 { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
2349 { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, 2352 { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
2350 { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, 2353 { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
2351 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
2352 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
2353 { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) }, 2354 { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) },
2354 { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) }, 2355 { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) },
2355 { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, 2356 { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
@@ -2486,7 +2487,6 @@ static const struct hid_device_id hid_ignore_list[] = {
2486 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) }, 2487 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
2487#endif 2488#endif
2488 { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, 2489 { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
2489 { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
2490 { } 2490 { }
2491}; 2491};
2492 2492
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 3eec09a134cb..e104abae0dad 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -334,6 +334,8 @@
334#define USB_DEVICE_ID_ELECOM_BM084 0x0061 334#define USB_DEVICE_ID_ELECOM_BM084 0x0061
335 335
336#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 336#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34
337#define USB_DEVICE_ID_DREAM_CHEEKY_WN 0x0004
338#define USB_DEVICE_ID_DREAM_CHEEKY_FA 0x000a
337 339
338#define USB_VENDOR_ID_ELITEGROUP 0x03fc 340#define USB_VENDOR_ID_ELITEGROUP 0x03fc
339#define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8 341#define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8
diff --git a/drivers/hid/hid-led.c b/drivers/hid/hid-led.c
new file mode 100644
index 000000000000..43fb089a8cd5
--- /dev/null
+++ b/drivers/hid/hid-led.c
@@ -0,0 +1,288 @@
1/*
2 * Simple USB RGB LED driver
3 *
4 * Copyright 2016 Heiner Kallweit <hkallweit1@gmail.com>
5 * Based on drivers/hid/hid-thingm.c and
6 * drivers/usb/misc/usbled.c
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation, version 2.
11 */
12
13#include <linux/hid.h>
14#include <linux/hidraw.h>
15#include <linux/leds.h>
16#include <linux/module.h>
17#include <linux/mutex.h>
18
19#include "hid-ids.h"
20
21enum hidled_report_type {
22 RAW_REQUEST,
23 OUTPUT_REPORT
24};
25
26enum hidled_type {
27 RISO_KAGAKU,
28 DREAM_CHEEKY,
29};
30
31static unsigned const char riso_kagaku_tbl[] = {
32/* R+2G+4B -> riso kagaku color index */
33 [0] = 0, /* black */
34 [1] = 2, /* red */
35 [2] = 1, /* green */
36 [3] = 5, /* yellow */
37 [4] = 3, /* blue */
38 [5] = 6, /* magenta */
39 [6] = 4, /* cyan */
40 [7] = 7 /* white */
41};
42
43#define RISO_KAGAKU_IX(r, g, b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
44
45struct hidled_device;
46
47struct hidled_config {
48 enum hidled_type type;
49 const char *name;
50 const char *short_name;
51 enum led_brightness max_brightness;
52 size_t report_size;
53 enum hidled_report_type report_type;
54 u8 report_id;
55 int (*init)(struct hidled_device *ldev);
56 int (*write)(struct led_classdev *cdev, enum led_brightness br);
57};
58
59struct hidled_led {
60 struct led_classdev cdev;
61 struct hidled_device *ldev;
62 char name[32];
63};
64
65struct hidled_device {
66 const struct hidled_config *config;
67 struct hidled_led red;
68 struct hidled_led green;
69 struct hidled_led blue;
70 struct hid_device *hdev;
71 struct mutex lock;
72};
73
74#define MAX_REPORT_SIZE 16
75
76#define to_hidled_led(arg) container_of(arg, struct hidled_led, cdev)
77
78static bool riso_kagaku_switch_green_blue;
79module_param(riso_kagaku_switch_green_blue, bool, S_IRUGO | S_IWUSR);
80MODULE_PARM_DESC(riso_kagaku_switch_green_blue,
81 "switch green and blue RGB component for Riso Kagaku devices");
82
83static int hidled_send(struct hidled_device *ldev, __u8 *buf)
84{
85 int ret;
86
87 buf[0] = ldev->config->report_id;
88
89 mutex_lock(&ldev->lock);
90
91 if (ldev->config->report_type == RAW_REQUEST)
92 ret = hid_hw_raw_request(ldev->hdev, buf[0], buf,
93 ldev->config->report_size,
94 HID_FEATURE_REPORT,
95 HID_REQ_SET_REPORT);
96 else if (ldev->config->report_type == OUTPUT_REPORT)
97 ret = hid_hw_output_report(ldev->hdev, buf,
98 ldev->config->report_size);
99 else
100 ret = -EINVAL;
101
102 mutex_unlock(&ldev->lock);
103
104 if (ret < 0)
105 return ret;
106
107 return ret == ldev->config->report_size ? 0 : -EMSGSIZE;
108}
109
110static u8 riso_kagaku_index(struct hidled_device *ldev)
111{
112 enum led_brightness r, g, b;
113
114 r = ldev->red.cdev.brightness;
115 g = ldev->green.cdev.brightness;
116 b = ldev->blue.cdev.brightness;
117
118 if (riso_kagaku_switch_green_blue)
119 return RISO_KAGAKU_IX(r, b, g);
120 else
121 return RISO_KAGAKU_IX(r, g, b);
122}
123
124static int riso_kagaku_write(struct led_classdev *cdev, enum led_brightness br)
125{
126 struct hidled_led *led = to_hidled_led(cdev);
127 struct hidled_device *ldev = led->ldev;
128 __u8 buf[MAX_REPORT_SIZE] = {};
129
130 buf[1] = riso_kagaku_index(ldev);
131
132 return hidled_send(ldev, buf);
133}
134
135static int dream_cheeky_write(struct led_classdev *cdev, enum led_brightness br)
136{
137 struct hidled_led *led = to_hidled_led(cdev);
138 struct hidled_device *ldev = led->ldev;
139 __u8 buf[MAX_REPORT_SIZE] = {};
140
141 buf[1] = ldev->red.cdev.brightness;
142 buf[2] = ldev->green.cdev.brightness;
143 buf[3] = ldev->blue.cdev.brightness;
144 buf[7] = 0x1a;
145 buf[8] = 0x05;
146
147 return hidled_send(ldev, buf);
148}
149
150static int dream_cheeky_init(struct hidled_device *ldev)
151{
152 __u8 buf[MAX_REPORT_SIZE] = {};
153
154 /* Dream Cheeky magic */
155 buf[1] = 0x1f;
156 buf[2] = 0x02;
157 buf[4] = 0x5f;
158 buf[7] = 0x1a;
159 buf[8] = 0x03;
160
161 return hidled_send(ldev, buf);
162}
163
164static const struct hidled_config hidled_configs[] = {
165 {
166 .type = RISO_KAGAKU,
167 .name = "Riso Kagaku Webmail Notifier",
168 .short_name = "riso_kagaku",
169 .max_brightness = 1,
170 .report_size = 6,
171 .report_type = OUTPUT_REPORT,
172 .report_id = 0,
173 .write = riso_kagaku_write,
174 },
175 {
176 .type = DREAM_CHEEKY,
177 .name = "Dream Cheeky Webmail Notifier",
178 .short_name = "dream_cheeky",
179 .max_brightness = 31,
180 .report_size = 9,
181 .report_type = RAW_REQUEST,
182 .report_id = 0,
183 .init = dream_cheeky_init,
184 .write = dream_cheeky_write,
185 },
186};
187
188static int hidled_init_led(struct hidled_led *led, const char *color_name,
189 struct hidled_device *ldev, unsigned int minor)
190{
191 snprintf(led->name, sizeof(led->name), "%s%u:%s",
192 ldev->config->short_name, minor, color_name);
193 led->cdev.name = led->name;
194 led->cdev.max_brightness = ldev->config->max_brightness;
195 led->cdev.brightness_set_blocking = ldev->config->write;
196 led->cdev.flags = LED_HW_PLUGGABLE;
197 led->ldev = ldev;
198
199 return devm_led_classdev_register(&ldev->hdev->dev, &led->cdev);
200}
201
202static int hidled_init_rgb(struct hidled_device *ldev, unsigned int minor)
203{
204 int ret;
205
206 /* Register the red diode */
207 ret = hidled_init_led(&ldev->red, "red", ldev, minor);
208 if (ret)
209 return ret;
210
211 /* Register the green diode */
212 ret = hidled_init_led(&ldev->green, "green", ldev, minor);
213 if (ret)
214 return ret;
215
216 /* Register the blue diode */
217 return hidled_init_led(&ldev->blue, "blue", ldev, minor);
218}
219
220static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id)
221{
222 struct hidled_device *ldev;
223 unsigned int minor;
224 int ret, i;
225
226 ldev = devm_kzalloc(&hdev->dev, sizeof(*ldev), GFP_KERNEL);
227 if (!ldev)
228 return -ENOMEM;
229
230 ret = hid_parse(hdev);
231 if (ret)
232 return ret;
233
234 ldev->hdev = hdev;
235 mutex_init(&ldev->lock);
236
237 for (i = 0; !ldev->config && i < ARRAY_SIZE(hidled_configs); i++)
238 if (hidled_configs[i].type == id->driver_data)
239 ldev->config = &hidled_configs[i];
240
241 if (!ldev->config)
242 return -EINVAL;
243
244 if (ldev->config->init) {
245 ret = ldev->config->init(ldev);
246 if (ret)
247 return ret;
248 }
249
250 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
251 if (ret)
252 return ret;
253
254 minor = ((struct hidraw *) hdev->hidraw)->minor;
255
256 ret = hidled_init_rgb(ldev, minor);
257 if (ret) {
258 hid_hw_stop(hdev);
259 return ret;
260 }
261
262 hid_info(hdev, "%s initialized\n", ldev->config->name);
263
264 return 0;
265}
266
267static const struct hid_device_id hidled_table[] = {
268 { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU,
269 USB_DEVICE_ID_RI_KA_WEBMAIL), .driver_data = RISO_KAGAKU },
270 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
271 USB_DEVICE_ID_DREAM_CHEEKY_WN), .driver_data = DREAM_CHEEKY },
272 { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY,
273 USB_DEVICE_ID_DREAM_CHEEKY_FA), .driver_data = DREAM_CHEEKY },
274 { }
275};
276MODULE_DEVICE_TABLE(hid, hidled_table);
277
278static struct hid_driver hidled_driver = {
279 .name = "hid-led",
280 .probe = hidled_probe,
281 .id_table = hidled_table,
282};
283
284module_hid_driver(hidled_driver);
285
286MODULE_LICENSE("GPL");
287MODULE_AUTHOR("Heiner Kallweit <hkallweit1@gmail.com>");
288MODULE_DESCRIPTION("Simple USB RGB LED driver");