Loading drivers/hid/hid-google-hammer.c +103 −43 Original line number Diff line number Diff line Loading @@ -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; }; Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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 Loading @@ -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 Loading @@ -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); Loading @@ -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) { 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) 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_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; } } Loading @@ -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; Loading @@ -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) Loading @@ -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, Loading @@ -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, }; Loading Loading
drivers/hid/hid-google-hammer.c +103 −43 Original line number Diff line number Diff line Loading @@ -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; }; Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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 Loading @@ -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 Loading @@ -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); Loading @@ -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) { 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) 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_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; } } Loading @@ -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; Loading @@ -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) Loading @@ -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, Loading @@ -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, }; Loading