summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2017-05-02 11:02:41 +0200
committerJiri Kosina <jkosina@suse.cz>2017-05-02 11:02:41 +0200
commit4d6ca227c768b50b05cf183974b40abe444e9d0c (patch)
treebf953d8e895281053548b9967a2c4b58d641df00 /drivers/hid
parentMerge branch 'for-4.12/sony' into for-linus (diff)
parentHID: asus: support backlight on USB keyboards (diff)
downloadlinux-4d6ca227c768b50b05cf183974b40abe444e9d0c.tar.xz
linux-4d6ca227c768b50b05cf183974b40abe444e9d0c.zip
Merge branch 'for-4.12/asus' into for-linus
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig7
-rw-r--r--drivers/hid/hid-asus.c248
-rw-r--r--drivers/hid/hid-core.c6
-rw-r--r--drivers/hid/hid-debug.c2
-rw-r--r--drivers/hid/hid-ids.h6
-rw-r--r--drivers/hid/hid-kye.c2
-rw-r--r--drivers/hid/hid-roccat.c2
-rw-r--r--drivers/hid/hidraw.c2
-rw-r--r--drivers/hid/usbhid/hiddev.c1
9 files changed, 261 insertions, 15 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 816679150b35..fe40e5e499dd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -148,13 +148,16 @@ config HID_APPLEIR
config HID_ASUS
tristate "Asus"
- depends on I2C_HID
+ depends on LEDS_CLASS
---help---
- Support for Asus notebook built-in keyboard and touchpad via i2c.
+ Support for Asus notebook built-in keyboard and touchpad via i2c, and
+ the Asus Republic of Gamers laptop keyboard special keys.
Supported devices:
- EeeBook X205TA
- VivoBook E200HA
+ - GL553V series
+ - GL753V series
config HID_AUREAL
tristate "Aureal"
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 70b12f89a193..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");
#define FEATURE_REPORT_ID 0x0d
#define INPUT_REPORT_ID 0x5d
+#define FEATURE_KBD_REPORT_ID 0x5a
#define INPUT_REPORT_SIZE 28
+#define FEATURE_KBD_REPORT_SIZE 16
+
+#define SUPPORT_KBD_BACKLIGHT BIT(0)
#define MAX_CONTACTS 5
@@ -63,18 +67,31 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_NO_INIT_REPORTS BIT(1)
#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
#define QUIRK_IS_MULTITOUCH BIT(3)
+#define QUIRK_NO_CONSUMER_USAGES BIT(4)
+#define QUIRK_USE_KBD_BACKLIGHT BIT(5)
-#define KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
- QUIRK_NO_INIT_REPORTS)
-#define TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
+#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
+ QUIRK_NO_INIT_REPORTS | \
+ QUIRK_NO_CONSUMER_USAGES)
+#define I2C_TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
QUIRK_SKIP_INPUT_MAPPING | \
QUIRK_IS_MULTITOUCH)
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
+struct asus_kbd_leds {
+ struct led_classdev cdev;
+ struct hid_device *hdev;
+ struct work_struct work;
+ unsigned int brightness;
+ bool removed;
+};
+
struct asus_drvdata {
unsigned long quirks;
struct input_dev *input;
+ struct asus_kbd_leds *kbd_backlight;
+ bool enable_backlight;
};
static void asus_report_contact_down(struct input_dev *input,
@@ -169,6 +186,148 @@ static int asus_raw_event(struct hid_device *hdev,
return 0;
}
+static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size)
+{
+ unsigned char *dmabuf;
+ int ret;
+
+ dmabuf = kmemdup(buf, buf_size, GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
+ buf_size, HID_FEATURE_REPORT,
+ HID_REQ_SET_REPORT);
+ kfree(dmabuf);
+
+ return ret;
+}
+
+static int asus_kbd_init(struct hid_device *hdev)
+{
+ u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
+ 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
+ int ret;
+
+ ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
+ if (ret < 0)
+ hid_err(hdev, "Asus failed to send init command: %d\n", ret);
+
+ return ret;
+}
+
+static int asus_kbd_get_functions(struct hid_device *hdev,
+ unsigned char *kbd_func)
+{
+ u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 };
+ u8 *readbuf;
+ int ret;
+
+ ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
+ if (ret < 0) {
+ hid_err(hdev, "Asus failed to send configuration command: %d\n", ret);
+ return ret;
+ }
+
+ readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
+ if (!readbuf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, readbuf,
+ FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
+ if (ret < 0) {
+ hid_err(hdev, "Asus failed to request functions: %d\n", ret);
+ kfree(readbuf);
+ return ret;
+ }
+
+ *kbd_func = readbuf[6];
+
+ kfree(readbuf);
+ return ret;
+}
+
+static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
+ cdev);
+ if (led->brightness == brightness)
+ return;
+
+ led->brightness = brightness;
+ schedule_work(&led->work);
+}
+
+static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
+{
+ struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
+ cdev);
+
+ return led->brightness;
+}
+
+static void asus_kbd_backlight_work(struct work_struct *work)
+{
+ struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
+ u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
+ int ret;
+
+ if (led->removed)
+ return;
+
+ buf[4] = led->brightness;
+
+ ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf));
+ if (ret < 0)
+ hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
+}
+
+static int asus_kbd_register_leds(struct hid_device *hdev)
+{
+ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ unsigned char kbd_func;
+ int ret;
+
+ /* Initialize keyboard */
+ ret = asus_kbd_init(hdev);
+ if (ret < 0)
+ return ret;
+
+ /* Get keyboard functions */
+ ret = asus_kbd_get_functions(hdev, &kbd_func);
+ if (ret < 0)
+ return ret;
+
+ /* Check for backlight support */
+ if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
+ return -ENODEV;
+
+ drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
+ sizeof(struct asus_kbd_leds),
+ GFP_KERNEL);
+ if (!drvdata->kbd_backlight)
+ return -ENOMEM;
+
+ drvdata->kbd_backlight->removed = false;
+ drvdata->kbd_backlight->brightness = 0;
+ drvdata->kbd_backlight->hdev = hdev;
+ drvdata->kbd_backlight->cdev.name = "asus::kbd_backlight";
+ drvdata->kbd_backlight->cdev.max_brightness = 3;
+ drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set;
+ drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get;
+ INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
+
+ ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev);
+ if (ret < 0) {
+ /* No need to have this still around */
+ devm_kfree(&hdev->dev, drvdata->kbd_backlight);
+ }
+
+ return ret;
+}
+
static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct input_dev *input = hi->input;
@@ -196,9 +355,14 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
drvdata->input = input;
+ if (drvdata->enable_backlight && asus_kbd_register_leds(hdev))
+ hid_warn(hdev, "Failed to initialize backlight.\n");
+
return 0;
}
+#define asus_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, \
+ max, EV_KEY, (c))
static int asus_input_mapping(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit,
@@ -213,6 +377,65 @@ static int asus_input_mapping(struct hid_device *hdev,
return -1;
}
+ /* ASUS-specific keyboard hotkeys */
+ if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
+ set_bit(EV_REP, hi->input->evbit);
+ switch (usage->hid & HID_USAGE) {
+ case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
+ case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
+ case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
+ case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
+ case 0x82: asus_map_key_clear(KEY_CAMERA); break;
+ case 0x88: asus_map_key_clear(KEY_RFKILL); break;
+ case 0xb5: asus_map_key_clear(KEY_CALC); break;
+ case 0xc4: asus_map_key_clear(KEY_KBDILLUMUP); break;
+ case 0xc5: asus_map_key_clear(KEY_KBDILLUMDOWN); break;
+
+ /* ASUS touchpad toggle */
+ case 0x6b: asus_map_key_clear(KEY_F21); break;
+
+ /* ROG key */
+ case 0x38: asus_map_key_clear(KEY_PROG1); break;
+
+ /* Fn+C ASUS Splendid */
+ case 0xba: asus_map_key_clear(KEY_PROG2); break;
+
+ /* Fn+Space Power4Gear Hybrid */
+ case 0x5c: asus_map_key_clear(KEY_PROG3); break;
+
+ default:
+ /* ASUS lazily declares 256 usages, ignore the rest,
+ * as some make the keyboard appear as a pointer device. */
+ return -1;
+ }
+
+ /*
+ * Check and enable backlight only on devices with UsagePage ==
+ * 0xff31 to avoid initializing the keyboard firmware multiple
+ * times on devices with multiple HID descriptors but same
+ * PID/VID.
+ */
+ if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT)
+ drvdata->enable_backlight = true;
+
+ return 1;
+ }
+
+ if (drvdata->quirks & QUIRK_NO_CONSUMER_USAGES &&
+ (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+ switch (usage->hid & HID_USAGE) {
+ case 0xe2: /* Mute */
+ case 0xe9: /* Volume up */
+ case 0xea: /* Volume down */
+ return 0;
+ default:
+ /* Ignore dummy Consumer usages which make the
+ * keyboard incorrectly appear as a pointer device.
+ */
+ return -1;
+ }
+ }
+
return 0;
}
@@ -305,6 +528,16 @@ err_stop_hw:
return ret;
}
+static void asus_remove(struct hid_device *hdev)
+{
+ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+
+ if (drvdata->kbd_backlight) {
+ drvdata->kbd_backlight->removed = true;
+ cancel_work_sync(&drvdata->kbd_backlight->work);
+ }
+}
+
static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -320,9 +553,13 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static const struct hid_device_id asus_devices[] = {
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
- USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD), KEYBOARD_QUIRKS},
+ USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD), I2C_KEYBOARD_QUIRKS},
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
- USB_DEVICE_ID_ASUSTEK_TOUCHPAD), TOUCHPAD_QUIRKS },
+ USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD), I2C_TOUCHPAD_QUIRKS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
+ USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
+ USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2), QUIRK_USE_KBD_BACKLIGHT },
{ }
};
MODULE_DEVICE_TABLE(hid, asus_devices);
@@ -332,6 +569,7 @@ static struct hid_driver asus_driver = {
.id_table = asus_devices,
.report_fixup = asus_report_fixup,
.probe = asus_probe,
+ .remove = asus_remove,
.input_mapping = asus_input_mapping,
.input_configured = asus_input_configured,
#ifdef CONFIG_PM
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index cd6eba051b97..37084b645785 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1851,8 +1851,10 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
- { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
- { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_TOUCHPAD) },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD) },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) },
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 5a0061c0ee87..5271db593478 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -30,7 +30,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 4b07a467d7a3..643390ba749d 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -173,8 +173,10 @@
#define USB_VENDOR_ID_ASUSTEK 0x0b05
#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
-#define USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD 0x8585
-#define USB_DEVICE_ID_ASUSTEK_TOUCHPAD 0x0101
+#define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD 0x8585
+#define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD 0x0101
+#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
+#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
#define USB_VENDOR_ID_ATEN 0x0557
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c
index 0dd1167b2c9b..9c113f62472d 100644
--- a/drivers/hid/hid-kye.c
+++ b/drivers/hid/hid-kye.c
@@ -487,7 +487,7 @@ static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize, int offset, const char *device_name) {
/*
* the fixup that need to be done:
- * - change Usage Maximum in the Comsumer Control
+ * - change Usage Maximum in the Consumer Control
* (report ID 3) to a reasonable value
*/
if (*rsize >= offset + 31 &&
diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
index 76d06cf87b2a..fb77dec720a4 100644
--- a/drivers/hid/hid-roccat.c
+++ b/drivers/hid/hid-roccat.c
@@ -25,7 +25,7 @@
#include <linux/cdev.h>
#include <linux/poll.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/hid-roccat.h>
#include <linux/module.h>
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index f0e2757cb909..ec530454e6f6 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -33,7 +33,7 @@
#include <linux/slab.h>
#include <linux/hid.h>
#include <linux/mutex.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/string.h>
#include <linux/hidraw.h>
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index a8baaf60e28a..0e06368d1fbb 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -27,6 +27,7 @@
#include <linux/poll.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>