diff options
author | Jiri Kosina <jkosina@suse.cz> | 2017-05-02 11:02:41 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2017-05-02 11:02:41 +0200 |
commit | 4d6ca227c768b50b05cf183974b40abe444e9d0c (patch) | |
tree | bf953d8e895281053548b9967a2c4b58d641df00 /drivers/hid | |
parent | Merge branch 'for-4.12/sony' into for-linus (diff) | |
parent | HID: asus: support backlight on USB keyboards (diff) | |
download | linux-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/Kconfig | 7 | ||||
-rw-r--r-- | drivers/hid/hid-asus.c | 248 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 6 | ||||
-rw-r--r-- | drivers/hid/hid-debug.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-kye.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-roccat.c | 2 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 2 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 1 |
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> |