How to use boost :: asio with GPIO Linux

I have a single threaded Linux application using boost :: asio for asynchronous I / O. Now I need to extend this reader application in the GPIO inputs to /sys/class/gpio/gpioXX/value .

Can this be done using boost :: asio :: posix :: stream_descriptor on GPIO inputs with multiple edges?

I configured the GPIO input as follows:

 echo XX >/sys/class/gpio/export echo in >/sys/class/gpio/gpioXX/direction echo both >/sys/class/gpio/gpioXX/edge 

I was able to write a test application based on epoll that locks the GPIO file descriptor until the GPIO signal changes, but boost::asio does not seem to be able to block correctly. The boost::asio::async_read always immediately calls the handler (of course, only inside io_service.run() ) with EOF or - if the file pointer was set back - 2 bytes of data.

I am not an expert in boost::asio internal, but maybe the reason is that the boost::asio epoll reactor starts at the level instead of starting in the 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; } 
+7
c ++ boost-asio epoll gpio
source share
1 answer

As far as I know, it is impossible to achieve this specific behavior using Boost.Asio. Although the core flags of some files on procfs and sysfs are infected, they do not provide the streaming behavior expected from boost::asio::posix::stream_descriptor and its activities.

The Boost.Asio epoll reactor has an edge effect (see Boost.Asio 1.43 change history notes ). Under certain conditions, 1 Boost.Asio will attempt to perform an I / O operation in the context of the initiating function (for example, async_read() ). If the I / O operation completes (success or failure), then the completion handler is sent to io_service as-if via io_service.post() . Otherwise, the file descriptor will be added to the event demultiplexer for monitoring. The documentation refers to this behavior:

Regardless of whether the asynchronous operation completes immediately or not, the handler will not be called from this function. The handler call will be executed in a way equivalent to using boost::asio::io_service::post() .

For compound operations, such as async_read() , EOF is considered an error because it indicates a violation in the operation contract (i.e., the termination condition will never be fulfilled because more data will not be available). In this particular case, the I / O system call will be executed in the async_read() start function, starting from the beginning of the file (offset 0) to the end of the file, as a result of which the operation will complete with boost::asio::error::eof . Upon completion of the operation, it is never added to the event demultiplexer for edge-delayed monitoring:

 boost::asio::io_service io_service; boost::asio::posix::stream_descriptor stream_descriptor(io_service); void read_handler(const boost::system::error_code& error, ...) { if (error.value() == boost::asio::error::eof) { // Reset to start of file. lseek(sd.native_handle(), 0, SEEK_SET); } // Same as below. ::readv() will occur within this context, reading // from the start of file to end-of-file, causing the operation to // complete with failure. boost::asio::async_read(stream_descriptor, ..., &read_handler); } int main() { int fd = open( /* sysfs file */, O_RDONLY); // This would throw an exception for normal files, as they are not // poll-able. However, the kernel flags some files on procfs and // sysfs as pollable. stream_descriptor.assign(fd); // The underlying ::readv() system call will occur within the // following function (not deferred until edge-triggered notification // by the reactor). The operation will read from start of file to // end-of-file, causing the operation to complete with failure. boost::asio::async_read(stream_descriptor, ..., &read_handler); // Run will invoke the ready-to-run completion handler from the above // operation. io_service.run(); } 

<sub> 1. Internally, Boost.Asio refers to this behavior as speculative operation. This is an implementation detail, but an I / O operation will be undertaken as part of the initiating function if the operation may not require an event notification (for example, it may immediately try to make a non-blocking I / O call) and there are no pending operations of the same type or pending out-of-band operations on the input-output object. There are no configuration adapters to prevent this behavior.

+4
source share

All Articles