aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJanne Kanniainen <janne.kanniainen@gmail.com>2014-06-18 12:05:02 -0400
committerBryan Wu <cooloney@gmail.com>2014-07-01 11:45:56 -0400
commitf471d9480275796dea2ac7ec249b050e70a2888d (patch)
tree51f5ac39050dcf045bc5a45a67af67c300ae0512
parent44a1255b03aeedb56cbe7cf27461458bd4513049 (diff)
HID: add support for MSI GT683R led panels
This driver adds support for USB controlled led panels that exists in MSI GT683R laptop Signed-off-by: Janne Kanniainen <janne.kanniainen@gmail.com> Reviewed-by: Johan Hovold <johan@kernel.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz> Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r--Documentation/ABI/testing/sysfs-class-hid-driver-gt683r14
-rw-r--r--drivers/hid/Kconfig14
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-gt683r.c309
-rw-r--r--drivers/hid/hid-ids.h2
-rw-r--r--drivers/hid/usbhid/hid-quirks.c2
7 files changed, 341 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 000000000000..317e9d5170f3
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
1What: /sys/class/hidraw/<hidraw>/device/leds_mode
2Date: Jun 2014
3KernelVersion: 3.17
4Contact: Janne Kanniainen <janne.kanniainen@gmail.com>
5Description:
6 Set the mode of LEDs
7
8 0 - normal
9 1 - audio
10 2 - breathing
11
12 Normal: LEDs are fully on when enabled
13 Audio: LEDs brightness depends on sound level
14 Breathing: LEDs brightness varies at human breathing rate \ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 800c8b60f7a2..bc7ecbdd03da 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,20 @@ config HOLTEK_FF
261 Say Y here if you have a Holtek On Line Grip based game controller 261 Say Y here if you have a Holtek On Line Grip based game controller
262 and want to have force feedback support for it. 262 and want to have force feedback support for it.
263 263
264config HID_GT683R
265 tristate "MSI GT68xR LED support"
266 depends on LEDS_CLASS && USB_HID
267 ---help---
268 Say Y here if you want to enable support for the three MSI GT68xR LEDs
269
270 This driver support following modes:
271 - Normal: LEDs are fully on when enabled
272 - Audio: LEDs brightness depends on sound level
273 - Breathing: LEDs brightness varies at human breathing rate
274
275 Currently the following devices are know to be supported:
276 - MSI GT683R
277
264config HID_HUION 278config HID_HUION
265 tristate "Huion tablets" 279 tristate "Huion tablets"
266 depends on USB_HID 280 depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index a6fa6baf368e..058b5f0249e1 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
48obj-$(CONFIG_HID_ELECOM) += hid-elecom.o 48obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
49obj-$(CONFIG_HID_ELO) += hid-elo.o 49obj-$(CONFIG_HID_ELO) += hid-elo.o
50obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o 50obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
51obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
51obj-$(CONFIG_HID_GYRATION) += hid-gyration.o 52obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
52obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o 53obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
53obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o 54obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 8ed66fd1ea87..2ce3f7a88002 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1845,6 +1845,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
1845 { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, 1845 { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
1846 { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, 1846 { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
1847 { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, 1847 { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
1848 { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
1848 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, 1849 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
1849 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) }, 1850 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
1850 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) }, 1851 { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 000000000000..077f7a19c9d5
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,309 @@
1/*
2 * MSI GT683R led driver
3 *
4 * Copyright (c) 2014 Janne Kanniainen <janne.kanniainen@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/device.h>
19#include <linux/hid.h>
20#include <linux/kernel.h>
21#include <linux/leds.h>
22#include <linux/module.h>
23
24#include "hid-ids.h"
25
26#define GT683R_BUFFER_SIZE 8
27
28/*
29 * GT683R_LED_OFF: all LEDs are off
30 * GT683R_LED_AUDIO: LEDs brightness depends on sound level
31 * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
32 * GT683R_LED_NORMAL: LEDs are fully on when enabled
33 */
34enum gt683r_led_mode {
35 GT683R_LED_OFF = 0,
36 GT683R_LED_AUDIO = 2,
37 GT683R_LED_BREATHING = 3,
38 GT683R_LED_NORMAL = 5
39};
40
41enum gt683r_panels {
42 GT683R_LED_BACK = 0,
43 GT683R_LED_SIDE = 1,
44 GT683R_LED_FRONT = 2,
45 GT683R_LED_COUNT,
46};
47
48static const char * const gt683r_panel_names[] = {
49 "back",
50 "side",
51 "front",
52};
53
54struct gt683r_led {
55 struct hid_device *hdev;
56 struct led_classdev led_devs[GT683R_LED_COUNT];
57 struct mutex lock;
58 struct work_struct work;
59 enum led_brightness brightnesses[GT683R_LED_COUNT];
60 enum gt683r_led_mode mode;
61};
62
63static const struct hid_device_id gt683r_led_id[] = {
64 { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
65 { }
66};
67
68static void gt683r_brightness_set(struct led_classdev *led_cdev,
69 enum led_brightness brightness)
70{
71 int i;
72 struct device *dev = led_cdev->dev->parent;
73 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
74 struct gt683r_led *led = hid_get_drvdata(hdev);
75
76 for (i = 0; i < GT683R_LED_COUNT; i++) {
77 if (led_cdev == &led->led_devs[i])
78 break;
79 }
80
81 if (i < GT683R_LED_COUNT) {
82 led->brightnesses[i] = brightness;
83 schedule_work(&led->work);
84 }
85}
86
87static ssize_t leds_mode_show(struct device *dev,
88 struct device_attribute *attr,
89 char *buf)
90{
91 u8 sysfs_mode;
92 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
93 struct gt683r_led *led = hid_get_drvdata(hdev);
94
95 if (led->mode == GT683R_LED_NORMAL)
96 sysfs_mode = 0;
97 else if (led->mode == GT683R_LED_AUDIO)
98 sysfs_mode = 1;
99 else
100 sysfs_mode = 2;
101
102 return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
103}
104
105static ssize_t leds_mode_store(struct device *dev,
106 struct device_attribute *attr,
107 const char *buf, size_t count)
108{
109 u8 sysfs_mode;
110 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
111 struct gt683r_led *led = hid_get_drvdata(hdev);
112
113
114 if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
115 return -EINVAL;
116
117 mutex_lock(&led->lock);
118
119 if (sysfs_mode == 0)
120 led->mode = GT683R_LED_NORMAL;
121 else if (sysfs_mode == 1)
122 led->mode = GT683R_LED_AUDIO;
123 else
124 led->mode = GT683R_LED_BREATHING;
125
126 mutex_unlock(&led->lock);
127 schedule_work(&led->work);
128
129 return count;
130}
131
132static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
133{
134 int ret;
135
136 ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
137 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
138 if (ret != GT683R_BUFFER_SIZE) {
139 hid_err(led->hdev,
140 "failed to send set report request: %i\n", ret);
141 if (ret < 0)
142 return ret;
143 return -EIO;
144 }
145
146 return 0;
147}
148
149static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
150{
151 int ret;
152 u8 *buffer;
153
154 buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
155 if (!buffer)
156 return -ENOMEM;
157
158 buffer[0] = 0x01;
159 buffer[1] = 0x02;
160 buffer[2] = 0x30;
161 buffer[3] = leds;
162 ret = gt683r_led_snd_msg(led, buffer);
163
164 kfree(buffer);
165 return ret;
166}
167
168static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
169{
170 int ret;
171 u8 *buffer;
172
173 buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
174 if (!buffer)
175 return -ENOMEM;
176
177 buffer[0] = 0x01;
178 buffer[1] = 0x02;
179 buffer[2] = 0x20;
180 buffer[3] = mode;
181 buffer[4] = 0x01;
182 ret = gt683r_led_snd_msg(led, buffer);
183
184 kfree(buffer);
185 return ret;
186}
187
188static void gt683r_led_work(struct work_struct *work)
189{
190 int i;
191 u8 leds = 0;
192 u8 mode;
193 struct gt683r_led *led = container_of(work, struct gt683r_led, work);
194
195 mutex_lock(&led->lock);
196
197 for (i = 0; i < GT683R_LED_COUNT; i++) {
198 if (led->brightnesses[i])
199 leds |= BIT(i);
200 }
201
202 if (gt683r_leds_set(led, leds))
203 goto fail;
204
205 if (leds)
206 mode = led->mode;
207 else
208 mode = GT683R_LED_OFF;
209
210 gt683r_mode_set(led, mode);
211fail:
212 mutex_unlock(&led->lock);
213}
214
215static DEVICE_ATTR_RW(leds_mode);
216
217static int gt683r_led_probe(struct hid_device *hdev,
218 const struct hid_device_id *id)
219{
220 int i;
221 int ret;
222 int name_sz;
223 char *name;
224 struct gt683r_led *led;
225
226 led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
227 if (!led)
228 return -ENOMEM;
229
230 led->mode = GT683R_LED_NORMAL;
231 led->hdev = hdev;
232 hid_set_drvdata(hdev, led);
233
234 ret = hid_parse(hdev);
235 if (ret) {
236 hid_err(hdev, "hid parsing failed\n");
237 return ret;
238 }
239
240 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
241 if (ret) {
242 hid_err(hdev, "hw start failed\n");
243 return ret;
244 }
245
246 for (i = 0; i < GT683R_LED_COUNT; i++) {
247 name_sz = strlen(dev_name(&hdev->dev)) +
248 strlen(gt683r_panel_names[i]) + 3;
249
250 name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
251 if (!name) {
252 ret = -ENOMEM;
253 goto fail;
254 }
255
256 snprintf(name, name_sz, "%s::%s",
257 dev_name(&hdev->dev), gt683r_panel_names[i]);
258 led->led_devs[i].name = name;
259 led->led_devs[i].max_brightness = 1;
260 led->led_devs[i].brightness_set = gt683r_brightness_set;
261 ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
262 if (ret) {
263 hid_err(hdev, "could not register led device\n");
264 goto fail;
265 }
266 }
267
268 ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
269 if (ret) {
270 hid_err(hdev, "could not make mode attribute file\n");
271 goto fail;
272 }
273
274 mutex_init(&led->lock);
275 INIT_WORK(&led->work, gt683r_led_work);
276
277 return 0;
278
279fail:
280 for (i = i - 1; i >= 0; i--)
281 led_classdev_unregister(&led->led_devs[i]);
282 hid_hw_stop(hdev);
283 return ret;
284}
285
286static void gt683r_led_remove(struct hid_device *hdev)
287{
288 int i;
289 struct gt683r_led *led = hid_get_drvdata(hdev);
290
291 device_remove_file(&hdev->dev, &dev_attr_leds_mode);
292 for (i = 0; i < GT683R_LED_COUNT; i++)
293 led_classdev_unregister(&led->led_devs[i]);
294 flush_work(&led->work);
295 hid_hw_stop(hdev);
296}
297
298static struct hid_driver gt683r_led_driver = {
299 .probe = gt683r_led_probe,
300 .remove = gt683r_led_remove,
301 .name = "gt683r_led",
302 .id_table = gt683r_led_id,
303};
304
305module_hid_driver(gt683r_led_driver);
306
307MODULE_AUTHOR("Janne Kanniainen");
308MODULE_DESCRIPTION("MSI GT683R led driver");
309MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 6d00bb9366fa..7370ae301975 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -645,7 +645,7 @@
645#define USB_DEVICE_ID_GENIUS_KB29E 0x3004 645#define USB_DEVICE_ID_GENIUS_KB29E 0x3004
646 646
647#define USB_VENDOR_ID_MSI 0x1770 647#define USB_VENDOR_ID_MSI 0x1770
648#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00 648#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
649 649
650#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 650#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
651#define USB_DEVICE_ID_N_S_HARMONY 0xc359 651#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 59badc10a08c..13ca19262e0c 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
73 { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, 73 { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
74 { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, 74 { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
75 { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, 75 { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
76 { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, 76 { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
77 { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, 77 { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
78 { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, 78 { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
79 { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, 79 { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },