// SPDX-License-Identifier: GPL-2.0+ /* * HID driver for Google Hammer device. * * Copyright (c) 2017 Google Inc. * Author: Wei-Ning Huang <wnhuang@google.com>
*/
/* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version.
*/
/* * C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting * state of the "Whiskers" base - attached or detached. Whiskers USB device also * reports position of the keyboard - folded or not. Combining base state and * position allows us to generate proper "Tablet mode" events.
*/ struct cbas_ec { struct device *dev; /* The platform device (EC) */ struct input_dev *input; bool base_present; bool base_folded; struct notifier_block notifier;
};
if (device_may_wakeup(cbas_ec.dev) ||
!queued_during_suspend) {
pm_wakeup_event(cbas_ec.dev, 0);
spin_lock_irqsave(&cbas_ec_lock, flags);
/* * While input layer dedupes the events, we do not want * to disrupt the state reported by the base by * overriding it with state reported by the LID. Only * report changes, as we assume that on attach the base * is not folded.
*/ if (base_present != cbas_ec.base_present) {
input_report_switch(cbas_ec.input,
SW_TABLET_MODE,
!base_present);
input_sync(cbas_ec.input);
cbas_ec.base_present = base_present;
}
spin_unlock_irqrestore(&cbas_ec_lock, flags);
}
}
return NOTIFY_OK;
}
static __maybe_unused int cbas_ec_resume(struct device *dev)
{ struct cros_ec_device *ec = dev_get_drvdata(dev->parent); bool base_present; int error;
error = cbas_ec_query_base(ec, true, &base_present); if (error) {
dev_warn(dev, "failed to fetch base state on resume: %d\n",
error);
} else {
spin_lock_irq(&cbas_ec_lock);
cbas_ec.base_present = base_present;
/* * Only report if base is disconnected. If base is connected, * it will resend its state on resume, and we'll update it * in hammer_event().
*/ if (!cbas_ec.base_present) {
input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1);
input_sync(cbas_ec.input);
}
staticvoid cbas_ec_set_input(struct input_dev *input)
{ /* Take the lock so hammer_event() does not race with us here */
spin_lock_irq(&cbas_ec_lock);
cbas_ec.input = input;
spin_unlock_irq(&cbas_ec_lock);
}
/* * Request USB HID device to be in Full On mode, so that sending * hardware output report and hardware raw request won't fail.
*/
ret = hid_hw_power(led->hdev, PM_HINT_FULLON); if (ret < 0) {
hid_err(led->hdev, "failed: device not resumed %d\n", ret); return ret;
}
ret = hid_hw_output_report(led->hdev, led->buf, sizeof(led->buf)); if (ret == -ENOSYS)
ret = hid_hw_raw_request(led->hdev, 0, led->buf, sizeof(led->buf),
HID_OUTPUT_REPORT,
HID_REQ_SET_REPORT); if (ret < 0)
hid_err(led->hdev, "failed to set keyboard backlight: %d\n",
ret);
/* Request USB HID device back to Normal Mode. */
hid_hw_power(led->hdev, PM_HINT_NORMAL);
staticint hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsignedlong **bit, int *max)
{ 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 * separate input device for tablet switch mode.
*/ return -1;
}
/* * If we are getting events from Whiskers that means that it * is attached to the lid.
*/
cbas_ec.base_present = true;
cbas_ec.base_folded = folded;
hid_dbg(hdev, "%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, folded);
input_sync(cbas_ec.input);
}
spin_unlock_irqrestore(&cbas_ec_lock, flags);
}
staticint hammer_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
{ if (usage->hid == HID_USAGE_KBD_FOLDED) {
hammer_folded_event(hid, value); return 1; /* We handled this event */
}
vdata = devm_kzalloc(&hdev->dev, sizeof(*vdata), GFP_KERNEL); if (!vdata) return -ENOMEM;
hid_set_drvdata(hdev, vdata);
error = hid_parse(hdev); if (error) return error;
error = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (error) return error;
error = devm_add_action(&hdev->dev, hammer_stop, 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;
hammer_get_folded_state(hdev);
}
if (hammer_has_backlight_control(hdev)) {
error = hammer_register_leds(hdev); if (error)
hid_warn(hdev, "Failed to register keyboard backlight: %d\n",
error);
}
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);
}
/* Unregistering LEDs and stopping the hardware is done via devm */
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.