mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 13:11:40 +00:00
Merge branch 'for-5.5/whiskers' into for-linus
- robustification of tablet mode support in google-whiskers driver (Dmitry Torokhov)
This commit is contained in:
commit
d8d0470875
@ -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,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user