Re: Generic battery interface

Shem Multinymous
2006-07-30T18:37:25Z

Hi all,

Here's an updated rough proto-spec for the userspace side of the
continuous-parameter polling interface, intended for hwmon, batteries
and their ilk, and maybe even the input infrastructure.

Compared to the parent post, this version adds a second parameter to
the ioctl (see discussion elsewhere in this thread), and corrects a
few inaccuracies.

With an event-based data source, this interface allows polling with no
time interrupts on tickless kernels. With a polling-based data source
and a bit of luck, the frequency of polling = frequency of induced
timer interrupts will be the minimum that satisfies the greediest
client (even if there are many). In general, complicated data sources
can be handled optimally by custom driver+infrastructure code. All of
this is completely transparent to userspace, which just states its
needs and has its every desire fulfilled.

Hardware readouts are obtained from a dedicated file - a sysfs
attribute (as in hwmon and tp_smapi) or a device file (as in the input
infrastructure). The file has the following properties:

1. A new ioctl DELAYED_UPDATE, with parameters min_wait and
min_fresh, meaning: "I want an a fresh readout. If I poll() this FD
with POLLIN then send an input-ready event at time is T+min_wait, or
when you have a readout that was received from the hardware at time
T+min_fresh, whichever is *later*. Likewise if I select()".
Here T is the time of the ioctl call and min_wait>=min_fresh.
2. When the file is opened in O_NONBLOCKing mode, read() always return
the latest cached readout rather than querying the hardware
(unless the latter has negligible cost).
3. When the file is opened in normal (blocking) mode, read() blocks
until a fresh readout is available and returns this readout; see
below.

To illustrate, here's an example of a proper polling loop (sans error
checking). This app wants to refresh its display when the data has
changed, but not more often than once per second. It wants the
readouts to be reasonly spaced: they should be obtained at least 700ms
apart. And it needs to update its GUI every 3 seconds regardless of
readouts.

In some linux/foo.h:
--------------------------
struct delayed_update_req {
int min_wait;
int min_fresh;
};
--------------------------

Application code:
--------------------------
/* Open attribute file with O_NONBLOCK so that all reads will
* return cached values instead of blocking:
*/
int fd = open("/whatever/voltage", O_NONBLOCK|O_RDONLY);

/* Read and process latest cached attribute value: */
read(fd, ...);
...

while (1) {
const struct delayed_update_req dureq =
{ .min_wait=1000, min_fresh=700 };
struct pollfd ufds = { .fd=fd, events=POLLIN };

/* Tell the driver we want a fresh readout, but no sooner than
* 1sec from now, and we want the readout to reflect reality no
* sooner than 700ms now:
*/
ioctl(fd, DELAYED_UPDATE, &dureq);

/* Wait for an update for at most 3 seconds. Nominally this will
* block for at least 1 second, because of the above ioct. If we
* were reading multiple attributes, we could poll them all
* simultaneously.
*/
poll(&ufds, 1, 3000);

/* Read latest cached attribute value: */
read(fd, ...);

... handle readout, update GUI ...
}
--------------------------

Legacy and lightweight applications (e.g., cat from shell) will open
the file in blocking mode. In this case, a "read(fd, ...)" has
semantics equivalent to the following O_NONBLOCK-mode code:

--------------------------
const struct delayed_update_req dureq =
{ .min_wait=0, min_fresh=0 };

/* Tell driver we want an immediate update: */
ioctl(fd, DELAYED_UPDATE, &dureq);

/* Wait indefinitely for an update: */
poll(&ufds, 1, -1);

/* Read the latest cached attribute value: */
read(fd, ...);
--------------------------

Similarly, select() and poll() on non-blocking files acts like you did
"ioctl(fd, DELAYED_UPDATE, &dureq)" with dureq={0,0} and then waited
for a refresh or timeout.

If we want to penalize code which doesn't use nonblocking mode and
delayed updates, we can increase the implicit dureq.min_wait above
from 0 to a sufficiently nasty (driver-dependent?) delay.

Comments?

(I still can't implement this myself. Just trying to elucidate my suggestion.)

Shem