Re: Generic battery interface

From: Shem Multinymous
Date: Sat Jul 29 2006 - 17:04:11 EST


On 7/29/06, Shem Multinymous <multinymous@xxxxxxxxx> wrote:

There's a pattern here. Maybe what we need is a generic scheme for
publishing continuous readouts, that will work for both hdaps and
batteries (despite a X100 difference in typical time magnitudes).
Doubtless there are other uses for such a scheme. We want it to
support clients with different desired rates, data sources with
different update frequencies, use minimum CPU and avoid unnecesary
timer interrupts on tickless kernels.

Here's one approach: use a syscall (e.g., ioctl) saying "block until
there's new data on this fd, or N milliseconds have passed, whichever
is *later*". This way each client declares the update rate it wants
and can change it on the fly. The driver sees all the requests and can
perform the minimum hardware quering -- for example, it won't query
the hardware at all if no client has submitted a request with
parameter N more than N milliseconds go. And there's no excessive work
or interrupts. Some (simple) kernel code infrastructure is needed to
help drivers manage the pending requests.

Here's a rough sketch for the userspace side of a continuous function
sampling interface. It handles the blocking a bit better than the
above proposal, in that it lets you easily handle multiple readouts.
It's agnostic about /dev vs. /sys.

As always, we have a file handing out readouts obtained from the
hardware - either a device file or a sysfs attribute. The file has the
following properties:

1. A new ioctl, SYSFS_DELAYED_UPDATE, meaning: "I want an a fresh
readout in N milliseconds, or whenver it's ready, whichever is later.
If I poll this FD with POLLIN, wait until the new readout is ready.
Likewise for select."
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 illustrte, here's an example of a proper polling loop (sans error
checking). This app wants updates at most every second, but obviously
not more frequently than the hardware has to offer. If no readout ca
be obtained for 20 seconds, it warns the user that the data is stale.

--------------------------
/* 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) {
struct pollfd ufds = { .fd=fd, events=POLLIN };

/* Tell driver we want a fresh readout but no sooner than 1sec
* from now:
*/
ioctl(fd, SYSFS_DELAYED_UPDATE, 1000);

/* Wait for an update for at most 5 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, 20000);
... warn user if timed out ...

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

Note that the above does not ensure minimal separation between
readouts: you're guaranteed a fresh readout at every iteration, but
it might have been taken at the beginning of the >1sec poll period. If
you need to ensure that all readouts are at least (700ms apart, add a
sleep(700) at the end of the loop and decrease 1000 to 300 in the
ioctl().I think most apps won't need this.

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:

--------------------------
/* Tell driver we want an immediate update: */
ioctl(fd, ATTR_DELAY_UPDATE, 0);

/* 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, ATTR_DELAY_UPDATE, 0);" and then wait for a refresh or timeout.

If we want to penalize people not using nonblocking mode and delayed
updates, we can replace two "0" above with a sufficiently nasty
(driver-dependent?) constant.

How does this look?

I'm not getting into the kernel side for now; it's doable, and with
proper infrastructure (e.g., at the sysfs level) can be elegant and
efficient.

Shem
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/