简体   繁体   中英

Listening for sysfs battery events

I'm writing a daemon which checks the battery capacity. This is for a solar powered embedded device running Linux. I've read that it's a bad idea to use sleep() in daemons, thus I'm trying to use events. So I wrote some PoCs, but I'm not getting any events, My first implementation, as they recommended to me, uses libudev and poll() :

#include <fcntl.h>
#include <libudev.h>
#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void)
{
    struct udev *udev;
    struct udev_monitor *mon;
    struct pollfd fds[1];
    int fd;
    udev = udev_new();
    if (udev == NULL)
        return 1;
    mon = udev_monitor_new_from_netlink(udev, "udev");
    udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL);
    udev_monitor_enable_receiving(mon);
    fd = udev_monitor_get_fd(mon);
    fds[0].fd = fd;
    fds[0].events = POLLIN;
    fds[0].revents = 0;
    if (poll(fds, 1, -1) > 0) {
        /* Never gets here! */
        struct udev_device *const dev = udev_monitor_receive_device(mon);
        if (dev != NULL) {
            puts(udev_device_get_sysname(dev));
            udev_device_unref(dev);
        }
        else
            fputs("udev_monitor_receive_device() failed\n", stderr);
    }
    udev_unref(udev);
    return 0;
}

They only event I get, is when I plug/unplug the charger! Then I thought that the status bar I use in my laptop's installation does show the battery capacity. I looked at the source and they're using inotify to monitor the battery's uevent . But I've read everywhere that I shouldn't use inotify for sysfs : I tried nonetheless:

#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>

#define BAT_PATH "/sys/class/power_supply/BAT0"

int main(void)
{
    struct inotify_event ev = {0};
    int wd, ret = 1;
    ssize_t len;
    const int fd = inotify_init1(IN_CLOEXEC);
    if (fd < 0) {
        perror("inotify_init() failed");
        return ret;
    }
    /* else */
    wd = inotify_add_watch(fd, BAT_PATH "/uevent", IN_ACCESS);
    if (wd < 0)
        goto end;
    /* else */
    len = read(fd, &ev, sizeof(ev));
    /* Again... never gets here. */
    if (len > 0 && (ev.mask & IN_ACCESS))
        puts("It worked!");
    inotify_rm_watch(fd, wd);
    ret = 0;
end:
    close(fd);
    return ret;
}

Turns out that doesn't work either? How can it work for my status bar but not work when I try it? Am I doing something horribly wrong. Thank you.

Regarding your first implementation (would comment but not enough rep. as i know nothing about libudev): the guide i followed to successfully use sysfs to poll() a GPIO for interrupt suggests to look for a POLLPRI event, instead of POLLIN as you show in the first implementation (see man poll for event types).

More importantly, you say you get a single event when you connect/disconnect charger, do you mean a single event per software execution? If this is the case, it might be due to the fact that you don't clear the interrupt flag: after poll() hits, in sysfs one needs to int len = read(fds[0].fd, *buf, SIZE); to mark the interrupt as served, and also lseek(fds[0].fd, 0, 0); in order for the next read() to be succesful (see my other answer here for a code example).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM