blob: 0af0eb446636ba6beeaf22c90ae43da6b55b6c6e [file] [log] [blame]
Vivien Didelot30ba2fb2013-01-22 12:01:21 -05001/*
2 * ThingM blink(1) USB RGB LED driver
3 *
Vivien Didelotf70ed8a2014-04-14 16:50:19 -04004 * Copyright 2013-2014 Savoir-faire Linux Inc.
Vivien Didelot30ba2fb2013-01-22 12:01:21 -05005 * 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>
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040013#include <linux/hidraw.h>
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050014#include <linux/leds.h>
15#include <linux/module.h>
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040016#include <linux/mutex.h>
17#include <linux/workqueue.h>
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050018
19#include "hid-ids.h"
20
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040021#define REPORT_ID 1
22#define REPORT_SIZE 9
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050023
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040024/* Firmware major number of supported devices */
25#define THINGM_MAJOR_MK1 '1'
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050026
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040027struct thingm_fwinfo {
28 char major;
29 unsigned numrgb;
30 unsigned first;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050031};
32
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040033const struct thingm_fwinfo thingm_fwinfo[] = {
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 */
42struct 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 */
49struct 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
58struct thingm_device {
59 struct hid_device *hdev;
60 struct {
61 char major;
62 char minor;
63 } version;
64 const struct thingm_fwinfo *fwinfo;
65 struct mutex lock;
66 struct thingm_rgb *rgb;
67};
68
69static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050070{
71 int ret;
72
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040073 hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050074 buf[0], buf[1], buf[2], buf[3], buf[4],
75 buf[5], buf[6], buf[7], buf[8]);
76
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040077 ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
78 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050079
80 return ret < 0 ? ret : 0;
81}
82
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040083static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050084{
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050085 int ret;
86
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040087 ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
88 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
89 if (ret < 0)
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050090 return ret;
91
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040092 hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
93 buf[0], buf[1], buf[2], buf[3], buf[4],
94 buf[5], buf[6], buf[7], buf[8]);
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050095
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040096 return 0;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -050097}
98
Vivien Didelotf70ed8a2014-04-14 16:50:19 -040099static int thingm_version(struct thingm_device *tdev)
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500100{
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400101 u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
102 int err;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500103
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400104 err = thingm_send(tdev, buf);
105 if (err)
106 return err;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500107
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400108 err = thingm_recv(tdev, buf);
109 if (err)
110 return err;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500111
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400112 tdev->version.major = buf[3];
113 tdev->version.minor = buf[4];
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500114
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400115 return 0;
116}
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500117
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400118static int thingm_write_color(struct thingm_rgb *rgb)
119{
120 u8 buf[REPORT_SIZE] = { REPORT_ID, 'n', 0, 0, 0, 0, 0, 0, 0 };
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500121
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400122 buf[2] = rgb->red.ldev.brightness;
123 buf[3] = rgb->green.ldev.brightness;
124 buf[4] = rgb->blue.ldev.brightness;
125
126 return thingm_send(rgb->tdev, buf);
127}
128
129static void thingm_work(struct work_struct *work)
130{
131 struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work);
132
133 mutex_lock(&rgb->tdev->lock);
134
135 if (thingm_write_color(rgb))
136 hid_err(rgb->tdev->hdev, "failed to write color\n");
137
138 mutex_unlock(&rgb->tdev->lock);
139}
140
141static void thingm_led_set(struct led_classdev *ldev,
142 enum led_brightness brightness)
143{
144 struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
145
146 /* the ledclass has already stored the brightness value */
147 schedule_work(&led->rgb->work);
148}
149
150static int thingm_init_rgb(struct thingm_rgb *rgb)
151{
152 const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor;
153 int err;
154
155 /* Register the red diode */
156 snprintf(rgb->red.name, sizeof(rgb->red.name),
157 "thingm%d:red:led%d", minor, rgb->num);
158 rgb->red.ldev.name = rgb->red.name;
159 rgb->red.ldev.max_brightness = 255;
160 rgb->red.ldev.brightness_set = thingm_led_set;
161 rgb->red.rgb = rgb;
162
163 err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev);
164 if (err)
165 return err;
166
167 /* Register the green diode */
168 snprintf(rgb->green.name, sizeof(rgb->green.name),
169 "thingm%d:green:led%d", minor, rgb->num);
170 rgb->green.ldev.name = rgb->green.name;
171 rgb->green.ldev.max_brightness = 255;
172 rgb->green.ldev.brightness_set = thingm_led_set;
173 rgb->green.rgb = rgb;
174
175 err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev);
176 if (err)
177 goto unregister_red;
178
179 /* Register the blue diode */
180 snprintf(rgb->blue.name, sizeof(rgb->blue.name),
181 "thingm%d:blue:led%d", minor, rgb->num);
182 rgb->blue.ldev.name = rgb->blue.name;
183 rgb->blue.ldev.max_brightness = 255;
184 rgb->blue.ldev.brightness_set = thingm_led_set;
185 rgb->blue.rgb = rgb;
186
187 err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
188 if (err)
189 goto unregister_green;
190
191 INIT_WORK(&rgb->work, thingm_work);
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500192
193 return 0;
194
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400195unregister_green:
196 led_classdev_unregister(&rgb->green.ldev);
197
198unregister_red:
199 led_classdev_unregister(&rgb->red.ldev);
200
201 return err;
202}
203
204static void thingm_remove_rgb(struct thingm_rgb *rgb)
205{
206 flush_work(&rgb->work);
207 led_classdev_unregister(&rgb->red.ldev);
208 led_classdev_unregister(&rgb->green.ldev);
209 led_classdev_unregister(&rgb->blue.ldev);
210}
211
212static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
213{
214 struct thingm_device *tdev;
215 int i, err;
216
217 tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device),
218 GFP_KERNEL);
219 if (!tdev)
220 return -ENOMEM;
221
222 tdev->hdev = hdev;
223 hid_set_drvdata(hdev, tdev);
224
225 err = hid_parse(hdev);
226 if (err)
227 goto error;
228
229 err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
230 if (err)
231 goto error;
232
233 mutex_init(&tdev->lock);
234
235 err = thingm_version(tdev);
236 if (err)
237 goto stop;
238
239 hid_dbg(hdev, "firmware version: %c.%c\n",
240 tdev->version.major, tdev->version.minor);
241
242 for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i)
243 if (thingm_fwinfo[i].major == tdev->version.major)
244 tdev->fwinfo = &thingm_fwinfo[i];
245
246 if (!tdev->fwinfo) {
247 hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
248 goto stop;
249 }
250
251 tdev->rgb = devm_kzalloc(&hdev->dev,
252 sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
253 GFP_KERNEL);
254 if (!tdev->rgb) {
255 err = -ENOMEM;
256 goto stop;
257 }
258
259 for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
260 struct thingm_rgb *rgb = tdev->rgb + i;
261
262 rgb->tdev = tdev;
263 rgb->num = tdev->fwinfo->first + i;
264 err = thingm_init_rgb(rgb);
265 if (err) {
266 while (--i >= 0)
267 thingm_remove_rgb(tdev->rgb + i);
268 goto stop;
269 }
270 }
271
272 return 0;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500273stop:
274 hid_hw_stop(hdev);
275error:
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400276 return err;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500277}
278
279static void thingm_remove(struct hid_device *hdev)
280{
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400281 struct thingm_device *tdev = hid_get_drvdata(hdev);
282 int i;
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500283
Vivien Didelotf70ed8a2014-04-14 16:50:19 -0400284 for (i = 0; i < tdev->fwinfo->numrgb; ++i)
285 thingm_remove_rgb(tdev->rgb + i);
286
Vivien Didelot30ba2fb2013-01-22 12:01:21 -0500287 hid_hw_stop(hdev);
288}
289
290static const struct hid_device_id thingm_table[] = {
291 { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
292 { }
293};
294MODULE_DEVICE_TABLE(hid, thingm_table);
295
296static struct hid_driver thingm_driver = {
297 .name = "thingm",
298 .probe = thingm_probe,
299 .remove = thingm_remove,
300 .id_table = thingm_table,
301};
302
303module_hid_driver(thingm_driver);
304
305MODULE_LICENSE("GPL");
306MODULE_AUTHOR("Vivien Didelot <vivien.didelot@savoirfairelinux.com>");
307MODULE_DESCRIPTION("ThingM blink(1) USB RGB LED driver");