I have a single-threaded Linux application using boost::asio for asynchronous input/output. Now I need to extend this application to read in GPIO inputs on /sys/class/gpio/gpioXX/value.
It is possible to do that with boost::asio::posix::stream_descriptor on edge-triggered GPIO inputs?
I configured the GPIO input like follows:
echo XX >/sys/class/gpio/export
echo in >/sys/class/gpio/gpioXX/direction
echo both >/sys/class/gpio/gpioXX/edge
I managed to write a epoll based test application that blocks on the GPIO file descriptor until the GPIO signal changes but boost::asio does not seem to be able to block properly. A call to boost::asio::async_read always immediately invokes the handler (of course only within io_service.run()) with either EOF or - in case the file pointer was set back - 2 bytes data.
I'm not an expert in boost::asio internals but could the reason be that the boost::asio epoll reactor is level triggered instead of edge triggered in case of posix::stream_descriptor?
Here is my code:
#include <fcntl.h>
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <boost/asio.hpp>
boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor sd(io_service);
boost::asio::streambuf streambuf;
void read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
    if (error.value() == boost::asio::error::eof) {
        // If we don't reset the file pointer we only get EOFs
        lseek(sd.native_handle(), 0, SEEK_SET);
    } else if (error)
        throw std::runtime_error(std::string("Error ") + std::to_string(error.value()) + " occurred (" + error.message() + ")");
    std::copy_n(std::istreambuf_iterator<char>(&streambuf), bytes_transferred, std::ostreambuf_iterator<char>(std::cout));
    streambuf.consume(bytes_transferred);
    boost::asio::async_read(sd, streambuf, &read_handler);
}
int main(int argc, char *argv[])
{
    if (argc != 2)
        return 1;
    int fd = open(argv[1], O_RDONLY);
    if (fd < 1)
        return 1;
    try {
        sd.assign(fd);
        boost::asio::async_read(sd, streambuf, &read_handler);
        io_service.run();
    } catch (...) {
        close(fd);
        return 1;
    }
    close(fd);
    return 0;
}