co_usb
Loading...
Searching...
No Matches
sequence_awaitable.hpp
Go to the documentation of this file.
1
6#pragma once
7
12#include "co_usb/usb_error.hpp"
13#include <boost/capy/buffers.hpp>
14#include <boost/capy/concept/io_awaitable.hpp>
15#include <boost/capy/continuation.hpp>
16#include <boost/capy/ex/io_env.hpp>
17#include <boost/capy/io_result.hpp>
18#include <cassert>
19#include <coroutine>
20#include <libusb.h>
21#include <mutex>
22#include <utility>
23
25{
26
27template <detail::TransferSequence TSeq, AnyBufferSequence BuffersTy> struct sequence_awaitable
28{
30 using buf_iter_t = decltype(boost::capy::begin(std::declval<BuffersTy const &>()));
31 using buffer_t = boost::capy::buffer_type<BuffersTy>;
32
34 {
35 std::mutex mutex;
36
37 boost::capy::io_env const *io_env;
38 boost::capy::continuation cont;
39
40 std::error_code ec;
41 size_t total{0};
42 size_t in_flight{0};
43 };
44
45 static void transfer_callback (libusb_transfer *tfer) noexcept
46 {
48 self_t &self = *static_cast<self_t *>(tfer->user_data);
49 const auto resume_on_zero = [&] ()
50 {
51 if (self.await_state->in_flight == 0)
52 {
53 self.await_state->io_env->executor.post(self.await_state->cont);
54 }
55 };
56 self.await_state->in_flight--;
57 bool ok = false;
58 {
59 std::unique_lock lock{self.await_state->mutex};
60 ok = (bool)self.await_state->ec;
61 }
62 if (ok)
63 {
64 resume_on_zero();
65 return;
66 }
67 if (tfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
68 {
69 for (int i = 0; i < tfer->num_iso_packets; i++)
70 {
71 self.await_state->total += tfer->iso_packet_desc[i].actual_length;
72 if (tfer->status != LIBUSB_TRANSFER_COMPLETED) [[unlikely]]
73 {
74 {
75 std::unique_lock lock{self.await_state->mutex};
76 self.await_state->ec = make_transfer_status(
77 static_cast<status>(tfer->iso_packet_desc[i].status));
78 }
79 resume_on_zero();
80 return;
81 }
82 }
83 }
84 else
85 {
86 self.await_state->total += tfer->actual_length;
87 }
88 if (tfer->status != LIBUSB_TRANSFER_COMPLETED) [[unlikely]]
89 {
90 {
91 std::unique_lock lock{self.await_state->mutex};
92 self.await_state->ec = make_transfer_status(static_cast<status>(tfer->status));
93 }
94 resume_on_zero();
95 return;
96 }
97 if (self.buf_current == self.buf_end) [[unlikely]]
98 {
99 resume_on_zero();
100 return;
101 }
102 buffer_t buf = *self.buf_current;
103 self.buf_current++;
104 tfer->buffer = (unsigned char *)buf.data();
105 tfer->length = buf.size();
106 int r = libusb_submit_transfer(tfer);
107 if (r != LIBUSB_SUCCESS) [[unlikely]]
108 {
109 {
110 std::unique_lock lock{self.await_state->mutex};
111 self.await_state->ec = make_usb_error_code(static_cast<usb_error>(r));
112 }
113 resume_on_zero();
114 return;
115 }
116 self.await_state->in_flight++;
117 }
118
120 BuffersTy const &buffers, size_t submission_size)
121 : await_state(await_state), view(seq_view), buf_current(boost::capy::begin(buffers)),
122 buf_end(boost::capy::end(buffers)), submission_size(submission_size)
123 {
124 assert((submission_size <= seq_view->size()) &&
125 "submission size must not be greater than number of transfers");
126 assert((submission_size <= boost::capy::buffer_length(buffers)) &&
127 "submission size must not be greater than number of buffers");
128 }
129
130 inline bool await_ready ()
131 {
132 return buf_current >= buf_end;
133 }
134
135 inline std::coroutine_handle<> await_suspend (std::coroutine_handle<> h,
136 boost::capy::io_env const *io_env)
137 {
138 if (io_env->stop_token.stop_requested())
139 {
141 return h;
142 }
143 await_state->io_env = io_env;
144 await_state->cont = {h};
146 const tfer_iter_t end = v.end() + submission_size;
147 for (tfer_iter_t iter = v.begin(); iter != end; iter++)
148 {
149 libusb_transfer *tfer = transfer_of(*iter);
150 buffer_t buf = *buf_current;
151 buf_current++;
152 tfer->buffer = (unsigned char *)buf.data();
153 tfer->length = buf.size();
154 tfer->user_data = this;
155 tfer->callback = transfer_callback;
156 }
158 for (tfer_iter_t iter = v.begin(); iter != end; iter++)
159 {
160 libusb_transfer *tfer = transfer_of(*iter);
161 int r = libusb_submit_transfer(tfer);
162 if (r != LIBUSB_SUCCESS) [[unlikely]]
163 {
164 std::unique_lock lock{await_state->mutex};
165 await_state->ec = make_transfer_status(static_cast<status>(r));
166 lock.unlock();
167 break;
168 }
169 }
170 return std::noop_coroutine();
171 }
172
173 inline boost::capy::io_result<size_t> await_resume ()
174 {
175 return {await_state->ec, await_state->total};
176 }
177
180
183
185};
186
187static_assert(
188 boost::capy::IoAwaitable<sequence_awaitable<libusb_transfer *, boost::capy::mutable_buffer>>,
189 "Not a proper IoAwaitable");
190static_assert(boost::capy::IoAwaitable<
191 sequence_awaitable<std::vector<libusb_transfer *>, boost::capy::const_buffer>>,
192 "Not a proper IoAwaitable");
193
194} // namespace co_usb::transfer::detail
Unified concept for buffer sequences.
constexpr auto transfer_of(Ty const &tfer_res) -> libusb_transfer *
Uniform accessor for obtaining a libusb_transfer * from a co_usb::transfer::detail::TransferResource.
Definition transfer_sequence.hpp:41
status
Status codes for transfers.
Definition status.hpp:22
Definition complete_io.hpp:21
std::error_code make_transfer_status(status e) noexcept
Definition status.hpp:74
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
View of a certain transfer sequence. Provides a uniform range interface.
Status codes for transfers.
std::error_code ec
Definition sequence_awaitable.hpp:40
std::mutex mutex
Definition sequence_awaitable.hpp:35
boost::capy::io_env const * io_env
Definition sequence_awaitable.hpp:37
boost::capy::continuation cont
Definition sequence_awaitable.hpp:38
size_t in_flight
Definition sequence_awaitable.hpp:42
size_t total
Definition sequence_awaitable.hpp:41
Definition sequence_awaitable.hpp:28
buf_iter_t buf_current
Definition sequence_awaitable.hpp:181
buf_iter_t buf_end
Definition sequence_awaitable.hpp:182
bool await_ready()
Definition sequence_awaitable.hpp:130
sequence_awaitable(await_state_t *await_state, sequence_view< TSeq > *seq_view, BuffersTy const &buffers, size_t submission_size)
Definition sequence_awaitable.hpp:119
await_state_t * await_state
Definition sequence_awaitable.hpp:178
std::coroutine_handle await_suspend(std::coroutine_handle<> h, boost::capy::io_env const *io_env)
Definition sequence_awaitable.hpp:135
size_t submission_size
Definition sequence_awaitable.hpp:184
static void transfer_callback(libusb_transfer *tfer) noexcept
Definition sequence_awaitable.hpp:45
boost::capy::io_result< size_t > await_resume()
Definition sequence_awaitable.hpp:173
decltype(boost::capy::begin(std::declval< BuffersTy const & >())) buf_iter_t
Definition sequence_awaitable.hpp:30
boost::capy::buffer_type< BuffersTy > buffer_t
Definition sequence_awaitable.hpp:31
sequence_view< TSeq > * view
Definition sequence_awaitable.hpp:179
sequence_view< TSeq >::iterator_type tfer_iter_t
Definition sequence_awaitable.hpp:29
View of a certain transfer sequence. Provides a uniform range interface.
Definition sequence_view.hpp:28
decltype(detail::transfer_begin(std::declval< Seq const & >())) iterator_type
Definition sequence_view.hpp:29
auto begin() const noexcept
Definition sequence_view.hpp:48
auto end() const noexcept
Definition sequence_view.hpp:53
Transfer resource and transfer sequence concepts.
USB error enumeration.