struct input_dev *idev;
struct chromeos_ec_device *ec;
struct notifier_block notifier;
+ struct notifier_block wake_notifier;
};
static int mkbp_open(struct input_dev *dev)
{
struct mkbp_device *mkbp_dev = input_get_drvdata(dev);
+ int ret;
- return blocking_notifier_chain_register(&mkbp_dev->ec->event_notifier,
+ ret = blocking_notifier_chain_register(&mkbp_dev->ec->event_notifier,
&mkbp_dev->notifier);
+ if (ret)
+ return ret;
+ ret = blocking_notifier_chain_register(&mkbp_dev->ec->wake_notifier,
+ &mkbp_dev->wake_notifier);
+ if (ret) {
+ blocking_notifier_chain_unregister(
+ &mkbp_dev->ec->event_notifier, &mkbp_dev->notifier);
+ return ret;
+ }
+
+ return 0;
}
static void mkbp_close(struct input_dev *dev)
blocking_notifier_chain_unregister(&mkbp_dev->ec->event_notifier,
&mkbp_dev->notifier);
+ blocking_notifier_chain_unregister(&mkbp_dev->ec->wake_notifier,
+ &mkbp_dev->wake_notifier);
+}
+
+static int mkbp_get_state(struct mkbp_device *mkbp_dev, uint8_t *kb_state)
+{
+ return mkbp_dev->ec->command_recv(mkbp_dev->ec, EC_CMD_MKBP_STATE,
+ kb_state, MKBP_NUM_COLS);
}
static int mkbp_work(struct notifier_block *nb,
notifier);
uint8_t kb_state[MKBP_NUM_COLS];
- ret = mkbp_dev->ec->command_recv(mkbp_dev->ec, EC_CMD_MKBP_STATE,
- kb_state, MKBP_NUM_COLS);
+ ret = mkbp_get_state(mkbp_dev, kb_state);
if (ret >= 0)
mkbp_process(mkbp_dev, kb_state, ret);
return NOTIFY_DONE;
}
+/* On resume, clear any keys in the buffer, crosbug.com/p/14523 */
+static int mkbp_clear_keyboard(struct notifier_block *nb,
+ unsigned long state, void *_notify)
+{
+ struct mkbp_device *mkbp_dev = container_of(nb, struct mkbp_device,
+ wake_notifier);
+ uint8_t old_state[MKBP_NUM_COLS];
+ uint8_t new_state[MKBP_NUM_COLS];
+ unsigned long duration;
+ int i, ret;
+
+ /*
+ * Keep reading until we see that the scan state does not change.
+ * That indicates that we are done.
+ *
+ * Assume that the EC keyscan buffer is at most 32 deep.
+ *
+ * TODO(sjg@chromium.org): Add EC command to clear keyscan FIFO.
+ */
+ duration = jiffies;
+ ret = mkbp_get_state(mkbp_dev, new_state);
+ for (i = 1; !ret && i < 32; i++) {
+ memcpy(old_state, new_state, sizeof(old_state));
+ ret = mkbp_get_state(mkbp_dev, new_state);
+ if (0 == memcmp(old_state, new_state, sizeof(old_state)))
+ break;
+ }
+ duration = jiffies - duration;
+ dev_info(mkbp_dev->dev, "Discarded %d keyscan(s) in %dus\n", i,
+ jiffies_to_usecs(duration));
+
+ return 0;
+}
+
static int __devinit mkbp_probe(struct platform_device *pdev)
{
struct chromeos_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
mkbp_dev->ec = ec;
mkbp_dev->notifier.notifier_call = mkbp_work;
+ mkbp_dev->wake_notifier.notifier_call = mkbp_clear_keyboard;
mkbp_dev->dev = dev;
idev->name = ec->get_name(ec);
int err = 0;
BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
+ BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->wake_notifier);
ec_dev->command_send = cros_ec_command_send;
ec_dev->command_recv = cros_ec_command_recv;
int cros_ec_resume(struct chromeos_ec_device *ec_dev)
{
+ /*
+ * When the EC is not a wake source, then it could not have caused the
+ * resume, so we should do the resume processing. This may clear the
+ * EC's key scan buffer, for example. If the EC is a wake source (e.g.
+ * the lid is open and the user might press a key to wake) then we
+ * don't want to do resume processing (key scan buffer should be
+ * preserved).
+ */
+ if (!ec_dev->wake_enabled)
+ blocking_notifier_call_chain(&ec_dev->wake_notifier, 1, ec_dev);
enable_irq(ec_dev->irq);
if (ec_dev->wake_enabled) {