Boost logo

Boost Users :

Subject: Re: [Boost-users] [Asio] Need help making a custom service
From: Kyle Edwards (kedwards_at_[hidden])
Date: 2015-03-10 17:14:54


On Tue, 2015-03-10 at 20:35 +0100, svante karlsson wrote:
> If you got it working by wrapping the file descriptor - post it and
> I'll give it a shot on tidying it up.

Here's what I've got. It works pretty well so far.

/////////////////////////
// gpio_interrupt.hpp: //
/////////////////////////

#ifndef GPIO_INTERRUPT_HPP
#define GPIO_INTERRUPT_HPP

#include <boost/asio/basic_io_object.hpp>
#include <boost/asio/ip/udp.hpp>

class gpio_interrupt_service : public boost::asio::io_service::service {
private:
  typedef boost::asio::ip::udp::socket socket_type;
  typedef socket_type::service_type socket_service_type;
  typedef socket_type::implementation_type socket_implementation_type;

public:
  class implementation_type {
  private:
    friend class gpio_interrupt_service;

    socket_implementation_type underlying_impl_;
  };

  static boost::asio::io_service::id id;

  gpio_interrupt_service(boost::asio::io_service &io_service);

  void construct(implementation_type &impl);

  void destroy(implementation_type &impl);

  boost::system::error_code open(implementation_type &impl, unsigned int
number, boost::system::error_code &ec);

  boost::system::error_code close(implementation_type &impl,
boost::system::error_code &ec);

  template<typename Handler>
  void async_wait(implementation_type &impl, Handler handler)
  {
    bound_handler<Handler> bound(*this, impl, handler);
    socket_service_.async_receive(impl.underlying_impl_,
boost::asio::null_buffers(), socket_type::message_out_of_band, bound);
  }

  boost::system::error_code cancel(implementation_type &impl,
boost::system::error_code &ec);

private:
  template<typename Handler>
  class bound_handler {
  public:
    bound_handler(gpio_interrupt_service &service, implementation_type
&impl, Handler handler) :
      service_(service),
      impl_(impl),
      handler_(std::move(handler)) {}

    bound_handler(const bound_handler &other) :
      service_(other.service_),
      impl_(other.impl_),
      handler_(other.handler_) {}

    bound_handler(bound_handler &&other) :
      service_(other.service_),
      impl_(other.impl_),
      handler_(std::move(other.handler_)) {}

    void operator()(const boost::system::error_code &ec, std::size_t
bytes_transferred)
    {
      service_.reset_descriptor(impl_, ec);
      handler_(ec);
    }

  private:
    gpio_interrupt_service &service_;
    implementation_type &impl_;
    Handler handler_;
  };

  socket_service_type &socket_service_;

  virtual void shutdown_service();

  void reset_descriptor(implementation_type &impl, const
boost::system::error_code &ec);
};

class gpio_interrupt : public
boost::asio::basic_io_object<gpio_interrupt_service> {
public:
  gpio_interrupt(boost::asio::io_service &io_service);

  void open(unsigned int number);

  boost::system::error_code open(unsigned int number,
boost::system::error_code &ec);

  void close();

  boost::system::error_code close(boost::system::error_code &ec);

  template<typename Handler>
  void async_wait(Handler &&handler)
  {
    this->get_service().async_wait(this->get_implementation(),
std::forward<Handler>(handler));
  }

  void cancel();

  boost::system::error_code cancel(boost::system::error_code &ec);
};

#endif // GPIO_INTERRUPT_HPP

/////////////////////////
// gpio_interrupt.cpp: //
/////////////////////////

#include "gpio_interrupt.hpp"

#include <boost/asio/detail/throw_error.hpp>
#include <boost/asio/error.hpp>

#include <fcntl.h>

boost::asio::io_service::id gpio_interrupt_service::id;

gpio_interrupt_service::gpio_interrupt_service(boost::asio::io_service
&io_service) :
  service(io_service),

socket_service_(boost::asio::use_service<socket_service_type>(io_service))
{
}

void gpio_interrupt_service::construct(implementation_type &impl)
{
  socket_service_.construct(impl.underlying_impl_);
}

void gpio_interrupt_service::destroy(implementation_type &impl)
{
  socket_service_.destroy(impl.underlying_impl_);
}

boost::system::error_code
gpio_interrupt_service::open(implementation_type &impl, unsigned int
number, boost::system::error_code &ec)
{
  std::ostringstream filename;
  filename << "/sys/class/gpio/gpio" << number << "/value";

  int fd = ::open(filename.str().c_str(), O_RDONLY | O_NONBLOCK);
  if (fd < 0) {
    ec = boost::system::error_code(errno,
boost::asio::error::get_system_category());
    return ec;
  }

  reset_descriptor(impl, ec);
  return socket_service_.assign(impl.underlying_impl_,
boost::asio::ip::udp::v4(), fd, ec);
}

boost::system::error_code
gpio_interrupt_service::close(implementation_type &impl,
boost::system::error_code &ec)
{
  return socket_service_.close(impl.underlying_impl_, ec);
}

boost::system::error_code
gpio_interrupt_service::cancel(implementation_type &impl,
boost::system::error_code &ec)
{
  return socket_service_.cancel(impl.underlying_impl_, ec);
}

void gpio_interrupt_service::shutdown_service()
{
}

void gpio_interrupt_service::reset_descriptor(implementation_type &impl,
const boost::system::error_code &ec)
{
  if (ec)
    return;

  int fd = socket_service_.native_handle(impl.underlying_impl_);
  lseek(fd, 0, SEEK_SET);
  char buf[2];
  read(fd, buf, 2);
}

gpio_interrupt::gpio_interrupt(boost::asio::io_service &io_service) :
basic_io_object(io_service)
{
}

void gpio_interrupt::open(unsigned int number)
{
  boost::system::error_code ec;
  this->get_service().open(this->get_implementation(), number, ec);
  boost::asio::detail::throw_error(ec, "open");
}

boost::system::error_code gpio_interrupt::open(unsigned int number,
boost::system::error_code &ec)
{
  return this->get_service().open(this->get_implementation(), number,
ec);
}

void gpio_interrupt::close()
{
  boost::system::error_code ec;
  this->get_service().close(this->get_implementation(), ec);
  boost::asio::detail::throw_error(ec, "close");
}

boost::system::error_code
gpio_interrupt::close(boost::system::error_code &ec)
{
  return this->get_service().close(this->get_implementation(), ec);
}

void gpio_interrupt::cancel()
{
  boost::system::error_code ec;
  this->get_service().cancel(this->get_implementation(), ec);
  boost::asio::detail::throw_error(ec, "cancel");
}

boost::system::error_code
gpio_interrupt::cancel(boost::system::error_code &ec)
{
  return this->get_service().cancel(this->get_implementation(), ec);
}

/////////////////////////////////

OT: Bjorn, I took a look at your Aware project, and it actually looks
like something I might be able to use. Thank you!


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net