diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-12-02 05:20:54 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-12-02 05:20:54 +0300 |
commit | d004701d1cc5a036b1f2dec34dd5973064c72eab (patch) | |
tree | 09b221ecd60e43a9b77edb42b8c7e4b6b47bd02a /drivers/hid/hid-google-hammer.c | |
parent | 4a08fe5792583d81bf237a75ebc803f756204e46 (diff) | |
parent | d8d0470875aad437053b6743ef24eb9bd72d9789 (diff) | |
download | linux-d004701d1cc5a036b1f2dec34dd5973064c72eab.tar.xz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina:
- Support for Logitech G15 (Hans de Goede)
- HID parser improvements, improving support for some devices; e.g.
Windows Precision Touchpad, products from Primax, etc. (Blaž
Hrastnik, Candle Sun)
- robustification of tablet mode support in google-whiskers driver
(Dmitry Torokhov)
- assorted small fixes, device-specific quirks and device ID additions
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (23 commits)
HID: rmi: Check that the RMI_STARTED bit is set before unregistering the RMI transport device
HID: quirks: remove hid-led devices from hid_have_special_driver
HID: Improve Windows Precision Touchpad detection.
HID: i2c-hid: Reset ALPS touchpads on resume
HID: i2c-hid: fix no irq after reset on raydium 3118
HID: logitech-hidpp: Silence intermittent get_battery_capacity errors
HID: i2c-hid: remove orphaned member sleep_delay
HID: quirks: Add quirk for HP MSU1465 PIXART OEM mouse
HID: core: check whether Usage Page item is after Usage ID items
HID: intel-ish-hid: Spelling s/diconnect/disconnect/
HID: google: Detect base folded usage instead of hard-coding whiskers
HID: logitech: Add depends on LEDS_CLASS to Logitech Kconfig entry
HID: lg-g15: Add support for the G510's M1-M3 and MR LEDs
HID: lg-g15: Add support for controlling the G510's RGB backlight
HID: lg-g15: Add support for the G510 keyboards' gaming keys
HID: lg-g15: Add support for the M1-M3 and MR LEDs
HID: lg-g15: Add keyboard and LCD backlight control
HID: Add driver for Logitech gaming keyboards (G15, G15 v2)
Input: Add event-codes for macro keys found on various keyboards
HID: hidraw: replace printk() with corresponding pr_xx() variant
...
Diffstat (limited to 'drivers/hid/hid-google-hammer.c')
-rw-r--r-- | drivers/hid/hid-google-hammer.c | 146 |
1 files changed, 103 insertions, 43 deletions
diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index d86a9189e88f..2aa4ed157aec 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -35,6 +35,7 @@ struct cbas_ec { struct device *dev; /* The platform device (EC) */ struct input_dev *input; bool base_present; + bool base_folded; struct notifier_block notifier; }; @@ -208,7 +209,14 @@ static int __cbas_ec_probe(struct platform_device *pdev) return error; } - input_report_switch(input, SW_TABLET_MODE, !cbas_ec.base_present); + if (!cbas_ec.base_present) + cbas_ec.base_folded = false; + + dev_dbg(&pdev->dev, "%s: base: %d, folded: %d\n", __func__, + cbas_ec.base_present, cbas_ec.base_folded); + + input_report_switch(input, SW_TABLET_MODE, + !cbas_ec.base_present || cbas_ec.base_folded); cbas_ec_set_input(input); @@ -322,10 +330,9 @@ static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev, static int hammer_register_leds(struct hid_device *hdev) { struct hammer_kbd_leds *kbd_backlight; + int error; - kbd_backlight = devm_kzalloc(&hdev->dev, - sizeof(*kbd_backlight), - GFP_KERNEL); + kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL); if (!kbd_backlight) return -ENOMEM; @@ -339,12 +346,31 @@ static int hammer_register_leds(struct hid_device *hdev) /* Set backlight to 0% initially. */ hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0); - return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev); + error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev); + if (error) + goto err_free_mem; + + hid_set_drvdata(hdev, kbd_backlight); + return 0; + +err_free_mem: + kfree(kbd_backlight); + return error; +} + +static void hammer_unregister_leds(struct hid_device *hdev) +{ + struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev); + + if (kbd_backlight) { + led_classdev_unregister(&kbd_backlight->cdev); + kfree(kbd_backlight); + } } #define HID_UP_GOOGLEVENDOR 0xffd10000 #define HID_VD_KBD_FOLDED 0x00000019 -#define WHISKERS_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED) +#define HID_USAGE_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED) /* HID usage for keyboard backlight (Alphanumeric display brightness) */ #define HID_AD_BRIGHTNESS 0x00140046 @@ -354,8 +380,7 @@ static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_usage *usage, unsigned long **bit, int *max) { - if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS && - usage->hid == WHISKERS_KBD_FOLDED) { + if (usage->hid == HID_USAGE_KBD_FOLDED) { /* * We do not want to have this usage mapped as it will get * mixed in with "base attached" signal and delivered over @@ -372,19 +397,19 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field, { unsigned long flags; - if (hid->product == USB_DEVICE_ID_GOOGLE_WHISKERS && - usage->hid == WHISKERS_KBD_FOLDED) { + if (usage->hid == HID_USAGE_KBD_FOLDED) { spin_lock_irqsave(&cbas_ec_lock, flags); - hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__, - cbas_ec.base_present, value); - /* - * We should not get event if base is detached, but in case - * we happen to service HID and EC notifications out of order - * let's still check the "base present" flag. + * If we are getting events from Whiskers that means that it + * is attached to the lid. */ - if (cbas_ec.input && cbas_ec.base_present) { + cbas_ec.base_present = true; + cbas_ec.base_folded = value; + hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__, + cbas_ec.base_present, cbas_ec.base_folded); + + if (cbas_ec.input) { input_report_switch(cbas_ec.input, SW_TABLET_MODE, value); input_sync(cbas_ec.input); @@ -397,33 +422,22 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field, return 0; } -static bool hammer_is_keyboard_interface(struct hid_device *hdev) +static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type, + unsigned application, unsigned usage) { - struct hid_report_enum *re = &hdev->report_enum[HID_INPUT_REPORT]; - struct hid_report *report; - - list_for_each_entry(report, &re->report_list, list) - if (report->application == HID_GD_KEYBOARD) - return true; - - return false; -} - -static bool hammer_has_backlight_control(struct hid_device *hdev) -{ - struct hid_report_enum *re = &hdev->report_enum[HID_OUTPUT_REPORT]; + struct hid_report_enum *re = &hdev->report_enum[report_type]; struct hid_report *report; int i, j; list_for_each_entry(report, &re->report_list, list) { - if (report->application != HID_GD_KEYBOARD) + if (report->application != application) continue; for (i = 0; i < report->maxfield; i++) { struct hid_field *field = report->field[i]; for (j = 0; j < field->maxusage; j++) - if (field->usage[j].hid == HID_AD_BRIGHTNESS) + if (field->usage[j].hid == usage) return true; } } @@ -431,21 +445,23 @@ static bool hammer_has_backlight_control(struct hid_device *hdev) return false; } +static bool hammer_has_folded_event(struct hid_device *hdev) +{ + return hammer_has_usage(hdev, HID_INPUT_REPORT, + HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED); +} + +static bool hammer_has_backlight_control(struct hid_device *hdev) +{ + return hammer_has_usage(hdev, HID_OUTPUT_REPORT, + HID_GD_KEYBOARD, HID_AD_BRIGHTNESS); +} + static int hammer_probe(struct hid_device *hdev, const struct hid_device_id *id) { int error; - /* - * We always want to poll for, and handle tablet mode events from - * Whiskers, even when nobody has opened the input device. This also - * prevents the hid core from dropping early tablet mode events from - * the device. - */ - if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS && - hammer_is_keyboard_interface(hdev)) - hdev->quirks |= HID_QUIRK_ALWAYS_POLL; - error = hid_parse(hdev); if (error) return error; @@ -454,6 +470,19 @@ static int hammer_probe(struct hid_device *hdev, if (error) return error; + /* + * We always want to poll for, and handle tablet mode events from + * devices that have folded usage, even when nobody has opened the input + * device. This also prevents the hid core from dropping early tablet + * mode events from the device. + */ + if (hammer_has_folded_event(hdev)) { + hdev->quirks |= HID_QUIRK_ALWAYS_POLL; + error = hid_hw_open(hdev); + if (error) + return error; + } + if (hammer_has_backlight_control(hdev)) { error = hammer_register_leds(hdev); if (error) @@ -465,6 +494,36 @@ static int hammer_probe(struct hid_device *hdev, return 0; } +static void hammer_remove(struct hid_device *hdev) +{ + unsigned long flags; + + if (hammer_has_folded_event(hdev)) { + hid_hw_close(hdev); + + /* + * If we are disconnecting then most likely Whiskers is + * being removed. Even if it is not removed, without proper + * keyboard we should not stay in clamshell mode. + * + * The reason for doing it here and not waiting for signal + * from EC, is that on some devices there are high leakage + * on Whiskers pins and we do not detect disconnect reliably, + * resulting in devices being stuck in clamshell mode. + */ + spin_lock_irqsave(&cbas_ec_lock, flags); + if (cbas_ec.input && cbas_ec.base_present) { + input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1); + input_sync(cbas_ec.input); + } + cbas_ec.base_present = false; + spin_unlock_irqrestore(&cbas_ec_lock, flags); + } + + hammer_unregister_leds(hdev); + + hid_hw_stop(hdev); +} static const struct hid_device_id hammer_devices[] = { { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, @@ -487,6 +546,7 @@ static struct hid_driver hammer_driver = { .name = "hammer", .id_table = hammer_devices, .probe = hammer_probe, + .remove = hammer_remove, .input_mapping = hammer_input_mapping, .event = hammer_event, }; |