aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/Kconfig1
-rw-r--r--drivers/hid/hid-asus.c183
2 files changed, 183 insertions, 1 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index de4ed3752a4e..502444fff869 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -136,6 +136,7 @@ config HID_APPLEIR
136 136
137config HID_ASUS 137config HID_ASUS
138 tristate "Asus" 138 tristate "Asus"
139 depends on LEDS_CLASS
139 ---help--- 140 ---help---
140 Support for Asus notebook built-in keyboard and touchpad via i2c, and 141 Support for Asus notebook built-in keyboard and touchpad via i2c, and
141 the Asus Republic of Gamers laptop keyboard special keys. 142 the Asus Republic of Gamers laptop keyboard special keys.
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index bacba97668bf..16df6cc90235 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -40,8 +40,12 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
40 40
41#define FEATURE_REPORT_ID 0x0d 41#define FEATURE_REPORT_ID 0x0d
42#define INPUT_REPORT_ID 0x5d 42#define INPUT_REPORT_ID 0x5d
43#define FEATURE_KBD_REPORT_ID 0x5a
43 44
44#define INPUT_REPORT_SIZE 28 45#define INPUT_REPORT_SIZE 28
46#define FEATURE_KBD_REPORT_SIZE 16
47
48#define SUPPORT_KBD_BACKLIGHT BIT(0)
45 49
46#define MAX_CONTACTS 5 50#define MAX_CONTACTS 5
47 51
@@ -64,6 +68,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
64#define QUIRK_SKIP_INPUT_MAPPING BIT(2) 68#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
65#define QUIRK_IS_MULTITOUCH BIT(3) 69#define QUIRK_IS_MULTITOUCH BIT(3)
66#define QUIRK_NO_CONSUMER_USAGES BIT(4) 70#define QUIRK_NO_CONSUMER_USAGES BIT(4)
71#define QUIRK_USE_KBD_BACKLIGHT BIT(5)
67 72
68#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ 73#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
69 QUIRK_NO_INIT_REPORTS | \ 74 QUIRK_NO_INIT_REPORTS | \
@@ -74,9 +79,19 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
74 79
75#define TRKID_SGN ((TRKID_MAX + 1) >> 1) 80#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
76 81
82struct asus_kbd_leds {
83 struct led_classdev cdev;
84 struct hid_device *hdev;
85 struct work_struct work;
86 unsigned int brightness;
87 bool removed;
88};
89
77struct asus_drvdata { 90struct asus_drvdata {
78 unsigned long quirks; 91 unsigned long quirks;
79 struct input_dev *input; 92 struct input_dev *input;
93 struct asus_kbd_leds *kbd_backlight;
94 bool enable_backlight;
80}; 95};
81 96
82static void asus_report_contact_down(struct input_dev *input, 97static void asus_report_contact_down(struct input_dev *input,
@@ -171,6 +186,148 @@ static int asus_raw_event(struct hid_device *hdev,
171 return 0; 186 return 0;
172} 187}
173 188
189static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size)
190{
191 unsigned char *dmabuf;
192 int ret;
193
194 dmabuf = kmemdup(buf, buf_size, GFP_KERNEL);
195 if (!dmabuf)
196 return -ENOMEM;
197
198 ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
199 buf_size, HID_FEATURE_REPORT,
200 HID_REQ_SET_REPORT);
201 kfree(dmabuf);
202
203 return ret;
204}
205
206static int asus_kbd_init(struct hid_device *hdev)
207{
208 u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
209 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
210 int ret;
211
212 ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
213 if (ret < 0)
214 hid_err(hdev, "Asus failed to send init command: %d\n", ret);
215
216 return ret;
217}
218
219static int asus_kbd_get_functions(struct hid_device *hdev,
220 unsigned char *kbd_func)
221{
222 u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 };
223 u8 *readbuf;
224 int ret;
225
226 ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
227 if (ret < 0) {
228 hid_err(hdev, "Asus failed to send configuration command: %d\n", ret);
229 return ret;
230 }
231
232 readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
233 if (!readbuf)
234 return -ENOMEM;
235
236 ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, readbuf,
237 FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
238 HID_REQ_GET_REPORT);
239 if (ret < 0) {
240 hid_err(hdev, "Asus failed to request functions: %d\n", ret);
241 kfree(readbuf);
242 return ret;
243 }
244
245 *kbd_func = readbuf[6];
246
247 kfree(readbuf);
248 return ret;
249}
250
251static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
252 enum led_brightness brightness)
253{
254 struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
255 cdev);
256 if (led->brightness == brightness)
257 return;
258
259 led->brightness = brightness;
260 schedule_work(&led->work);
261}
262
263static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
264{
265 struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
266 cdev);
267
268 return led->brightness;
269}
270
271static void asus_kbd_backlight_work(struct work_struct *work)
272{
273 struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
274 u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
275 int ret;
276
277 if (led->removed)
278 return;
279
280 buf[4] = led->brightness;
281
282 ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf));
283 if (ret < 0)
284 hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
285}
286
287static int asus_kbd_register_leds(struct hid_device *hdev)
288{
289 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
290 unsigned char kbd_func;
291 int ret;
292
293 /* Initialize keyboard */
294 ret = asus_kbd_init(hdev);
295 if (ret < 0)
296 return ret;
297
298 /* Get keyboard functions */
299 ret = asus_kbd_get_functions(hdev, &kbd_func);
300 if (ret < 0)
301 return ret;
302
303 /* Check for backlight support */
304 if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
305 return -ENODEV;
306
307 drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
308 sizeof(struct asus_kbd_leds),
309 GFP_KERNEL);
310 if (!drvdata->kbd_backlight)
311 return -ENOMEM;
312
313 drvdata->kbd_backlight->removed = false;
314 drvdata->kbd_backlight->brightness = 0;
315 drvdata->kbd_backlight->hdev = hdev;
316 drvdata->kbd_backlight->cdev.name = "asus::kbd_backlight";
317 drvdata->kbd_backlight->cdev.max_brightness = 3;
318 drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set;
319 drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get;
320 INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
321
322 ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev);
323 if (ret < 0) {
324 /* No need to have this still around */
325 devm_kfree(&hdev->dev, drvdata->kbd_backlight);
326 }
327
328 return ret;
329}
330
174static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi) 331static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
175{ 332{
176 struct input_dev *input = hi->input; 333 struct input_dev *input = hi->input;
@@ -198,6 +355,9 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
198 355
199 drvdata->input = input; 356 drvdata->input = input;
200 357
358 if (drvdata->enable_backlight && asus_kbd_register_leds(hdev))
359 hid_warn(hdev, "Failed to initialize backlight.\n");
360
201 return 0; 361 return 0;
202} 362}
203 363
@@ -248,6 +408,16 @@ static int asus_input_mapping(struct hid_device *hdev,
248 * as some make the keyboard appear as a pointer device. */ 408 * as some make the keyboard appear as a pointer device. */
249 return -1; 409 return -1;
250 } 410 }
411
412 /*
413 * Check and enable backlight only on devices with UsagePage ==
414 * 0xff31 to avoid initializing the keyboard firmware multiple
415 * times on devices with multiple HID descriptors but same
416 * PID/VID.
417 */
418 if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT)
419 drvdata->enable_backlight = true;
420
251 return 1; 421 return 1;
252 } 422 }
253 423
@@ -358,6 +528,16 @@ err_stop_hw:
358 return ret; 528 return ret;
359} 529}
360 530
531static void asus_remove(struct hid_device *hdev)
532{
533 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
534
535 if (drvdata->kbd_backlight) {
536 drvdata->kbd_backlight->removed = true;
537 cancel_work_sync(&drvdata->kbd_backlight->work);
538 }
539}
540
361static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, 541static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
362 unsigned int *rsize) 542 unsigned int *rsize)
363{ 543{
@@ -379,7 +559,7 @@ static const struct hid_device_id asus_devices[] = {
379 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, 559 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
380 USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1) }, 560 USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1) },
381 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, 561 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
382 USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2) }, 562 USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2), QUIRK_USE_KBD_BACKLIGHT },
383 { } 563 { }
384}; 564};
385MODULE_DEVICE_TABLE(hid, asus_devices); 565MODULE_DEVICE_TABLE(hid, asus_devices);
@@ -389,6 +569,7 @@ static struct hid_driver asus_driver = {
389 .id_table = asus_devices, 569 .id_table = asus_devices,
390 .report_fixup = asus_report_fixup, 570 .report_fixup = asus_report_fixup,
391 .probe = asus_probe, 571 .probe = asus_probe,
572 .remove = asus_remove,
392 .input_mapping = asus_input_mapping, 573 .input_mapping = asus_input_mapping,
393 .input_configured = asus_input_configured, 574 .input_configured = asus_input_configured,
394#ifdef CONFIG_PM 575#ifdef CONFIG_PM