Skip to content

Commit d8d0470

Browse files
author
Jiri Kosina
committed
Merge branch 'for-5.5/whiskers' into for-linus
- robustification of tablet mode support in google-whiskers driver (Dmitry Torokhov)
2 parents a820e45 + 20c55f2 commit d8d0470

File tree

1 file changed

+103
-43
lines changed

1 file changed

+103
-43
lines changed

drivers/hid/hid-google-hammer.c

Lines changed: 103 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct cbas_ec {
3535
struct device *dev; /* The platform device (EC) */
3636
struct input_dev *input;
3737
bool base_present;
38+
bool base_folded;
3839
struct notifier_block notifier;
3940
};
4041

@@ -208,7 +209,14 @@ static int __cbas_ec_probe(struct platform_device *pdev)
208209
return error;
209210
}
210211

211-
input_report_switch(input, SW_TABLET_MODE, !cbas_ec.base_present);
212+
if (!cbas_ec.base_present)
213+
cbas_ec.base_folded = false;
214+
215+
dev_dbg(&pdev->dev, "%s: base: %d, folded: %d\n", __func__,
216+
cbas_ec.base_present, cbas_ec.base_folded);
217+
218+
input_report_switch(input, SW_TABLET_MODE,
219+
!cbas_ec.base_present || cbas_ec.base_folded);
212220

213221
cbas_ec_set_input(input);
214222

@@ -322,10 +330,9 @@ static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
322330
static int hammer_register_leds(struct hid_device *hdev)
323331
{
324332
struct hammer_kbd_leds *kbd_backlight;
333+
int error;
325334

326-
kbd_backlight = devm_kzalloc(&hdev->dev,
327-
sizeof(*kbd_backlight),
328-
GFP_KERNEL);
335+
kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL);
329336
if (!kbd_backlight)
330337
return -ENOMEM;
331338

@@ -339,12 +346,31 @@ static int hammer_register_leds(struct hid_device *hdev)
339346
/* Set backlight to 0% initially. */
340347
hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);
341348

342-
return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
349+
error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
350+
if (error)
351+
goto err_free_mem;
352+
353+
hid_set_drvdata(hdev, kbd_backlight);
354+
return 0;
355+
356+
err_free_mem:
357+
kfree(kbd_backlight);
358+
return error;
359+
}
360+
361+
static void hammer_unregister_leds(struct hid_device *hdev)
362+
{
363+
struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev);
364+
365+
if (kbd_backlight) {
366+
led_classdev_unregister(&kbd_backlight->cdev);
367+
kfree(kbd_backlight);
368+
}
343369
}
344370

345371
#define HID_UP_GOOGLEVENDOR 0xffd10000
346372
#define HID_VD_KBD_FOLDED 0x00000019
347-
#define WHISKERS_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED)
373+
#define HID_USAGE_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED)
348374

349375
/* HID usage for keyboard backlight (Alphanumeric display brightness) */
350376
#define HID_AD_BRIGHTNESS 0x00140046
@@ -354,8 +380,7 @@ static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi,
354380
struct hid_usage *usage,
355381
unsigned long **bit, int *max)
356382
{
357-
if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
358-
usage->hid == WHISKERS_KBD_FOLDED) {
383+
if (usage->hid == HID_USAGE_KBD_FOLDED) {
359384
/*
360385
* We do not want to have this usage mapped as it will get
361386
* 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,
372397
{
373398
unsigned long flags;
374399

375-
if (hid->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
376-
usage->hid == WHISKERS_KBD_FOLDED) {
400+
if (usage->hid == HID_USAGE_KBD_FOLDED) {
377401
spin_lock_irqsave(&cbas_ec_lock, flags);
378402

379-
hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
380-
cbas_ec.base_present, value);
381-
382403
/*
383-
* We should not get event if base is detached, but in case
384-
* we happen to service HID and EC notifications out of order
385-
* let's still check the "base present" flag.
404+
* If we are getting events from Whiskers that means that it
405+
* is attached to the lid.
386406
*/
387-
if (cbas_ec.input && cbas_ec.base_present) {
407+
cbas_ec.base_present = true;
408+
cbas_ec.base_folded = value;
409+
hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
410+
cbas_ec.base_present, cbas_ec.base_folded);
411+
412+
if (cbas_ec.input) {
388413
input_report_switch(cbas_ec.input,
389414
SW_TABLET_MODE, value);
390415
input_sync(cbas_ec.input);
@@ -397,55 +422,46 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field,
397422
return 0;
398423
}
399424

400-
static bool hammer_is_keyboard_interface(struct hid_device *hdev)
425+
static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type,
426+
unsigned application, unsigned usage)
401427
{
402-
struct hid_report_enum *re = &hdev->report_enum[HID_INPUT_REPORT];
403-
struct hid_report *report;
404-
405-
list_for_each_entry(report, &re->report_list, list)
406-
if (report->application == HID_GD_KEYBOARD)
407-
return true;
408-
409-
return false;
410-
}
411-
412-
static bool hammer_has_backlight_control(struct hid_device *hdev)
413-
{
414-
struct hid_report_enum *re = &hdev->report_enum[HID_OUTPUT_REPORT];
428+
struct hid_report_enum *re = &hdev->report_enum[report_type];
415429
struct hid_report *report;
416430
int i, j;
417431

418432
list_for_each_entry(report, &re->report_list, list) {
419-
if (report->application != HID_GD_KEYBOARD)
433+
if (report->application != application)
420434
continue;
421435

422436
for (i = 0; i < report->maxfield; i++) {
423437
struct hid_field *field = report->field[i];
424438

425439
for (j = 0; j < field->maxusage; j++)
426-
if (field->usage[j].hid == HID_AD_BRIGHTNESS)
440+
if (field->usage[j].hid == usage)
427441
return true;
428442
}
429443
}
430444

431445
return false;
432446
}
433447

448+
static bool hammer_has_folded_event(struct hid_device *hdev)
449+
{
450+
return hammer_has_usage(hdev, HID_INPUT_REPORT,
451+
HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED);
452+
}
453+
454+
static bool hammer_has_backlight_control(struct hid_device *hdev)
455+
{
456+
return hammer_has_usage(hdev, HID_OUTPUT_REPORT,
457+
HID_GD_KEYBOARD, HID_AD_BRIGHTNESS);
458+
}
459+
434460
static int hammer_probe(struct hid_device *hdev,
435461
const struct hid_device_id *id)
436462
{
437463
int error;
438464

439-
/*
440-
* We always want to poll for, and handle tablet mode events from
441-
* Whiskers, even when nobody has opened the input device. This also
442-
* prevents the hid core from dropping early tablet mode events from
443-
* the device.
444-
*/
445-
if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
446-
hammer_is_keyboard_interface(hdev))
447-
hdev->quirks |= HID_QUIRK_ALWAYS_POLL;
448-
449465
error = hid_parse(hdev);
450466
if (error)
451467
return error;
@@ -454,6 +470,19 @@ static int hammer_probe(struct hid_device *hdev,
454470
if (error)
455471
return error;
456472

473+
/*
474+
* We always want to poll for, and handle tablet mode events from
475+
* devices that have folded usage, even when nobody has opened the input
476+
* device. This also prevents the hid core from dropping early tablet
477+
* mode events from the device.
478+
*/
479+
if (hammer_has_folded_event(hdev)) {
480+
hdev->quirks |= HID_QUIRK_ALWAYS_POLL;
481+
error = hid_hw_open(hdev);
482+
if (error)
483+
return error;
484+
}
485+
457486
if (hammer_has_backlight_control(hdev)) {
458487
error = hammer_register_leds(hdev);
459488
if (error)
@@ -465,6 +494,36 @@ static int hammer_probe(struct hid_device *hdev,
465494
return 0;
466495
}
467496

497+
static void hammer_remove(struct hid_device *hdev)
498+
{
499+
unsigned long flags;
500+
501+
if (hammer_has_folded_event(hdev)) {
502+
hid_hw_close(hdev);
503+
504+
/*
505+
* If we are disconnecting then most likely Whiskers is
506+
* being removed. Even if it is not removed, without proper
507+
* keyboard we should not stay in clamshell mode.
508+
*
509+
* The reason for doing it here and not waiting for signal
510+
* from EC, is that on some devices there are high leakage
511+
* on Whiskers pins and we do not detect disconnect reliably,
512+
* resulting in devices being stuck in clamshell mode.
513+
*/
514+
spin_lock_irqsave(&cbas_ec_lock, flags);
515+
if (cbas_ec.input && cbas_ec.base_present) {
516+
input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1);
517+
input_sync(cbas_ec.input);
518+
}
519+
cbas_ec.base_present = false;
520+
spin_unlock_irqrestore(&cbas_ec_lock, flags);
521+
}
522+
523+
hammer_unregister_leds(hdev);
524+
525+
hid_hw_stop(hdev);
526+
}
468527

469528
static const struct hid_device_id hammer_devices[] = {
470529
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
@@ -487,6 +546,7 @@ static struct hid_driver hammer_driver = {
487546
.name = "hammer",
488547
.id_table = hammer_devices,
489548
.probe = hammer_probe,
549+
.remove = hammer_remove,
490550
.input_mapping = hammer_input_mapping,
491551
.event = hammer_event,
492552
};

0 commit comments

Comments
 (0)