14#include <boost/capy/concept/io_awaitable.hpp>
15#include <boost/capy/continuation.hpp>
16#include <boost/capy/ex/async_mutex.hpp>
17#include <boost/capy/ex/executor_ref.hpp>
18#include <boost/capy/ex/io_env.hpp>
19#include <boost/capy/ex/run.hpp>
20#include <boost/capy/ex/run_async.hpp>
21#include <boost/capy/ex/this_coro.hpp>
22#include <boost/capy/io_result.hpp>
23#include <boost/capy/io_task.hpp>
24#include <boost/capy/task.hpp>
27#include <memory_resource>
30#include <system_error>
57 std::pmr::memory_resource *memres = std::pmr::get_default_resource())
58 : m_ev_handler_ref(::
co_usb::ev::detail::get_handler_service(exec).handler()),
59 m_usb_ctx(::
co_usb::ev::detail::get_handler_service(exec).usb_context()),
60 m_memres(memres), m_resumptions(memres), m_arrived_devices(memres), m_handle(0)
71 libusb_hotplug_callback_handle handle{0};
73 std::unique_lock<std::mutex> lock{m_mutex};
74 handle = std::exchange(m_handle, 0);
78 libusb_hotplug_deregister_callback(m_usb_ctx, handle);
80 std::unique_lock lock{m_mutex};
81 for (resumption_t *res : m_resumptions)
84 res->op_res->ec = std::make_error_code(std::errc::operation_canceled);
85 res->env->executor.post(res->cont);
88 m_resumptions.clear();
89 m_arrived_devices.clear();
102 return std::make_error_code(std::errc::operation_in_progress);
112 return std::make_error_code(std::errc::operation_in_progress);
114 libusb_hotplug_callback_handle handle;
115 auto r = libusb_hotplug_register_callback(
116 m_usb_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
117 LIBUSB_HOTPLUG_ENUMERATE, m_filter.
vid, m_filter.
pid, m_filter.
dev_class,
118 [] ([[maybe_unused]] libusb_context *ctx, libusb_device *dev, libusb_hotplug_event ev,
119 void *user_data) ->
int
121 libusb_device_descriptor dev_desc;
122 libusb_get_device_descriptor(dev, &dev_desc);
123 device_triplet const triplet = triplet_from_descriptor(dev_desc);
124 device_ref dev_ref{dev};
126 if (ev == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
128 std::unique_lock lock{self.m_mutex};
129 auto it = std::ranges::find_if(
130 self.m_arrived_devices, [&] (dev_info_t
const &info)
131 { return wildcard_triplet_comparator()(info.triplet, triplet); });
132 if (it != self.m_arrived_devices.end())
134 self.m_arrived_devices.erase(it);
139 std::unique_lock lock{self.m_mutex};
140 if (self.m_resumptions.empty())
142 self.m_arrived_devices.emplace_back(triplet,
device_ref{dev});
145 resumption_t *r = self.m_resumptions.front();
147 r->env->executor.post(r->cont);
148 self.m_resumptions.erase(self.m_resumptions.begin());
153 if (r != LIBUSB_SUCCESS)
165 return std::make_error_code(std::errc::invalid_argument);
167 libusb_hotplug_deregister_callback(m_usb_ctx, m_handle);
171 auto accept () -> boost::capy::io_task<device_ref>
173 auto exec =
co_await boost::capy::this_coro::executor;
174 auto stop =
co_await boost::capy::this_coro::stop_token;
175 auto alloc =
co_await boost::capy::this_coro::frame_allocator;
179 auto stop_fn = [
this, op_res_ptr = &op_res, res = &res] ()
mutable
181 std::unique_lock lock{m_mutex};
182 if (res->env && res->cont.h)
184 res->op_res->ec = std::make_error_code(std::errc::operation_canceled);
185 res->env->executor.post(res->cont);
187 std::find_if(m_resumptions.begin(), m_resumptions.end(),
188 [res_ptr = res] (resumption_t *res) { return res == res_ptr; });
189 if (it != m_resumptions.end())
191 m_resumptions.erase(it);
197 std::stop_callback stop_cb{stop, std::move(stop_fn)};
198 co_return co_await awaitable(
this, &op_res, &res);
210 std::error_code ec{};
215 boost::capy::io_env
const *env{
nullptr};
216 boost::capy::continuation cont{.h =
nullptr};
217 op_result *op_res{
nullptr};
222 device_triplet triplet;
228 explicit awaitable (device_acceptor *acceptor, op_result *state, resumption_t *res)
229 : acceptor_ptr(acceptor), op_res(state), res(res)
233 inline bool await_ready ()
235 std::unique_lock lock{acceptor_ptr->m_mutex};
236 if (!acceptor_ptr->m_arrived_devices.empty())
238 op_res->dev_ref = acceptor_ptr->m_arrived_devices.back().dev_ref;
239 acceptor_ptr->m_arrived_devices.pop_back();
245 inline std::coroutine_handle<> await_suspend (std::coroutine_handle<> h,
246 boost::capy::io_env
const *env)
248 if (env->stop_token.stop_requested())
250 std::unique_lock lock{acceptor_ptr->m_mutex};
251 op_res->ec = std::make_error_code(std::errc::operation_canceled);
255 std::unique_lock lock{acceptor_ptr->m_mutex};
256 if (!acceptor_ptr->m_arrived_devices.empty())
258 op_res->dev_ref = acceptor_ptr->m_arrived_devices.back().dev_ref;
259 acceptor_ptr->m_arrived_devices.pop_back();
263 *res = resumption_t{env, boost::capy::continuation{h}, op_res};
264 acceptor_ptr->m_resumptions.emplace_back(res);
265 return std::noop_coroutine();
268 inline boost::capy::io_result<device_ref> await_resume ()
270 std::unique_lock lock{acceptor_ptr->m_mutex};
273 return {op_res->ec, device_ref{}};
275 return {std::error_code{}, op_res->dev_ref};
278 device_acceptor *acceptor_ptr;
283 ev::event_handler_ref m_ev_handler_ref;
285 std::pmr::memory_resource *m_memres;
287 std::pmr::vector<resumption_t *> m_resumptions{};
288 std::pmr::vector<dev_info_t> m_arrived_devices{};
290 device_triplet m_filter{};
291 libusb_context *m_usb_ctx;
292 libusb_hotplug_callback_handle m_handle{0};
Wrapper for nullable libusb_device that increments ref count on ctor and decrements on dtor.
An aggregate struct to pass to functions requiring device information.
Type-erased reference to any event handler.
Capy execution_context service wrapping a libusb event handler.
Definition hotplug_awaitable.hpp:18
Definition flag_type.hpp:6
usb_error
USB error enumeration.
Definition usb_error.hpp:20
std::error_code make_usb_error_code(usb_error e) noexcept
Definition usb_error.hpp:85
Wrapper for nullable libusb_device that increments ref count on ctor and decrements on dtor.
Definition device_ref.hpp:25
An aggregate struct to pass to functions requiring device information.
Definition device_triplet.hpp:21
int pid
Definition device_triplet.hpp:23
int dev_class
Definition device_triplet.hpp:24
int vid
Definition device_triplet.hpp:22
Asio/Corosio-like acceptor for devices via hotplug API.
Definition device_acceptor.hpp:55
auto listen() -> std::error_code
Definition device_acceptor.hpp:108
device_acceptor & operator=(device_acceptor &&)=delete
auto shutdown() -> void
Definition device_acceptor.hpp:69
device_acceptor(device_acceptor &&)=delete
auto accept() -> boost::capy::io_task< device_ref >
Definition device_acceptor.hpp:171
~device_acceptor()
Definition device_acceptor.hpp:64
auto bind(device_triplet triplet, std::error_code &ec) -> void
Definition device_acceptor.hpp:92
device_acceptor(const device_acceptor &)=delete
device_acceptor & operator=(const device_acceptor &)=delete
auto close() -> std::error_code
Definition device_acceptor.hpp:161
device_acceptor(boost::capy::executor_ref exec, std::pmr::memory_resource *memres=std::pmr::get_default_resource())
Definition device_acceptor.hpp:56
auto bind(device_triplet triplet) -> std::error_code
Definition device_acceptor.hpp:98