Features Download
From: Daniel Drake <dsd <at> laptop.org>
Subject: Problem statement: Opportunistic suspend and i8042 wakeups
Newsgroups: gmane.linux.kernel.input
Date: Monday 10th October 2011 19:24:08 UTC (over 6 years ago)

OLPC is still keen on getting upstream support for seamless i8042
wakeup. Since this has stagnated a bit I'm hoping that writing a
detailed overview of the situation and requirements will be useful in
finding a solution.

We're working with the already-upstream opportunistic wakeup model
described in http://lwn.net/images/pdf/suspend_blockers.pdf
- this is
a shared problem space with other parties wishing to implement
opportunistic suspend with i8042-based input devices, and it is a
crucial part of the opportunistic suspend user experience.


OLPC laptops are engineered to suspend and resume very quickly.

OLPC laptops are also engineered to be somewhat operational during
suspend. That is, the screen can be left on, the wireless card is left
on during suspend, and incoming frames will wake the system, and we
also wake up on the lid switch, power button, and low battery or AC
plug/unplug event. All those wakeup events are taken care of in the
upstream kernel. The missing part of the design is that the user can
seamlessly wake the system with the keyboard and mouse too, and thats
what we need to tackle here. The overall design is that the system
suspends opportunistically to the point where the user is unaware that
the system is suspending and resuming at high frequency in the

The OLPC XO-1 and XO-1.5 keyboard and mouse(touchpad) is standard
atkbd and psmouse on a serio bus connected to a i8042 chip. This
hardware is guaranteed to be left powered during suspend.

All keyboard keys behave 'regularly' in that when you press and hold a
key, the make code interrupt for that key will be repeated by the
hardware until you release it, at which point it generates a break
code interrupt. All keys behave identically.

The keyboard does not have any LEDs.

By default the system will not wake up on i8042 activity. The wakeup
is enabled with olpc_ec_wakeup_set(), all this infrastructure is
already upstream and used by the wifi, some switches, the battery/AC
driver, etc.

When enabled, the EC will wake the system up on key press events,
mouse movements and mouse clicks, but not key release events. (the key
release event behaviour could be changed if desired/necessary)

On resume, the operating system must 'simulate' an i8042 interrupt. By
this I mean it should pretend an interrupt arrived, and therefore read
the status register, see if data is present, if so read the data
register, etc. (Conveniently, the i8042 resume code already has this
behaviour). If the system does not do this, no i8042 interrupts will
be generated in resume to the wakeup event or any later input actions
from the user.

We have a userspace agent 'powerd' which keeps track of system
activity, suspends opportunistically when the system is idle, etc.

We currently have some hacky patches that we use to enable this
functionality which are working well. I've sent cleaned up versions
upstream but these have not gone beyond the discussion stages yet.

When the system resumes due to a key press or mouse movement it is
obviously of crucial importance that the wakeup event (key press,
mouse click, etc) is not lost. If the system is suspended and I press
'k' then the system must resume and that 'k' press must go through the
input layer and reach the on-screen userspace application as usual.
Otherwise, as a user unaware of the suspend, I'd be pretty confused as
to why I pressed a key but nothing appeared to happen.

This mail is written primarily in the context of the x86 XO-1 and
XO-1.5 laptops. However, we are also working on a new ARM-based laptop
known as the XO-1.75. This laptop is also following the opportunistic
suspend model, and while it does not have i8042, it does use serio (as
a child of a custom embedded controller driver at [1] that we will
submit later), atkbd and psmouse, so the majority of what is written
here also applies to the XO-1.75. But for the purpose of discussion,
lets focus on XO-1 and XO-1.5 first, where all the required bits are
already upstream.


In addition to providing an on/off switch for this functionality,
several parts of the i8042/serio/input code behaviour need to be
changed to support this correctly and efficiently. The main problems
are that the hardware gets reset on 3 layers during suspend/resume,
and the wakeup event is discarded in the process.

1. On suspend, i8042.c calls i8042_controller_reset() which resets
various things and attempts to disable the interfaces. On resume,
i8042_controller_resume() tries to turn things on again, and then
simulates an interrupt. Apart from the simulated interrupt, we do not
want any of that behaviour on a setup where i8042 devices are to be
used as an opportunistic suspend wakeup source, since it would cause
an interruption to the user's movement of the mouse, throw away the
wakeup event, etc.

2. In addition to the above, on suspend, serio_suspend() calls the
driver's cleanup routines. For atkbd and psmouse, this attempts to
reset the keyboard/mouse state completely. On resume, serio_resume()
queues a workqueue item to reconnect the port. The effect of this is
that atkbd_reconnect() gets called at some point later, which again
disables/resets the keyboard, then enables it, and psmouse_reconnect()
does something very similar for the mouse. Again, not wanted in this
case, and likely to be one of the reasons that the wakeup reason is

3. In addition to the above, on suspend, input_dev_suspend() tries to
turn off the keyboard LEDs. We don't have any, nor do we have
num/caps/scroll lock keys, so the lights are already off and this
function has nothing to do. However, on resume, input_dev_reset() is
called. This function tries to restore the keyboard LED state, which
results in atkbd queuing a atkbd_set_leds() workqueue item in order to
tell the hardware to turn all LEDs off (even though we dont have any,
and the hardware state said they were already off). Then,
input_reset_device() releases any keys that are held. This behaviour
is particularly undesirable for us because:

 a) Our keyboard autorepeats keys at the interrupt level, but we have
keyboard autorepeat disabled in X. Anyone who has worked with first
time computer users of 6 years of age will know that children tend to
press the keys sloowly and surely at first; autorepeat is not the
behaviour we want for them. So, X looks for break codes before
processing keypresses and effectively ignores the multiple make codes
- this is how its disable-autorepeat implementation works, and this
seems like a sensible design.

 b) If I resume the system with a slightly elongated key press, the
following happens with the current behaviour of input_dev_reset()
(with the i8042/serio layers modified to not mess with device state
over suspend/resume): A make code will be generated by the hardware
since I pressed the key. This will wake the system and reach userspace
as a key press event. input_reset_device() will then fabricate a key
release event, which reaches userspace, so X then registers a
keypress/keyrelease and prints the character to the screen. Then,
because I'm still holding the key, the hardware generates another make
interrupt. Userspace receives this as a key press event. Then I
release the key, which results in a break interrupt, which gets
propagated to userspace as a key release, so X prints the character
again. So, I have just woken up the system with a key press, but
userspace has interpreted it as two key presses. This is surprisingly
easy to reproduce, and is not what we want.


This is the open question - how can we adjust the above kernel
behaviour to be suitable for opportunistic suspend?

I'd welcome input here.

In my most recent set of patches I took the following approach:

1. The 'wakeup' property is enabled on the i8042, serio and input
devices. Before going into suspend where input wakeups are desired,
userspace must enable wakeup in sysfs on: i8042, serio0, the mouse
input device, serio1, and the keyboard input device.
2. In the suspend/resume handlers, each of the three layers above
checks to see if wakeups are enabled on its own device, and if so, the
suspend/resume handlers effectively do nothing, avoiding the triple
layer resetting described above.
3. The i8042 driver wakeup property is also used to call
olpc_ec_wakeup_set() in order to tell the EC to wake up the CPU on
input events when in suspend.

Dmitry suggested a way of simplifying this: detect at the i8042 or
serio level if this behaviour is to be enabled in a pre-suspend
handler, and if so, catch and discard all the traffic that normally
happens during suspend and resume handlers. Unfortunately this doesn't
work because:
 1. Some of the communication that happens in these suspend/resume
handlers involves getting a response back from the device. If we're
just discarding the outgoing requests, we're not going to get a
response, and this makes the code unhappy.
 2. Some of the communication that happens in the resume path happens
in workqueue items that run later (at both the serio and atkbd
levels). So some of the communication we need to drop happens after
resume has finished, I'm not sure how we would catch and discard this.
 3. The issue of the input layer releasing all our keys is left
unresolved with this approach.

I acknowledge that the patches so far posted aren't the cleanest in
the world, but I'm not really sure of a better way to do it.



[1] http://dev.laptop.org/git/olpc-kernel/tree/drivers/input/serio/olpc_keyboard.c?h=arm-3.0-wip&id=e7ad082
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
CD: 3ms