source: mainline/uspace/lib/usbdev/src/pipes.c@ 6ff23ff

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6ff23ff was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * Copyright (c) 2018 Ondrej Hlavaty, Michal Staruch
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30/** @addtogroup libusbdev
31 * @{
32 */
33/** @file
34 * USB endpoint pipes functions.
35 */
36#include <usb/dev/pipes.h>
37#include <usb/dev/request.h>
38#include <usb/usb.h>
39#include <usb/dma_buffer.h>
40
41#include <assert.h>
42#include <bitops.h>
43#include <async.h>
44#include <as.h>
45#include <errno.h>
46#include <mem.h>
47
48/** Try to clear endpoint halt of default control pipe.
49 *
50 * @param pipe Pipe for control endpoint zero.
51 */
52static void clear_self_endpoint_halt(usb_pipe_t *pipe)
53{
54 assert(pipe != NULL);
55
56 if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
57 return;
58 }
59
60 /* Prevent infinite recursion. */
61 pipe->auto_reset_halt = false;
62 usb_pipe_clear_halt(pipe, pipe);
63 pipe->auto_reset_halt = true;
64}
65
66/* Helper structure to avoid passing loads of arguments through */
67typedef struct {
68 usb_pipe_t *pipe;
69 usb_direction_t dir;
70 bool is_control; // Only for checking purposes
71
72 usbhc_iface_transfer_request_t req;
73
74 size_t transferred_size;
75} transfer_t;
76
77/**
78 * Issue a transfer in a separate exchange.
79 */
80static errno_t transfer_common(transfer_t *t)
81{
82 if (!t->pipe)
83 return EBADMEM;
84
85 /* Only control writes make sense without buffer */
86 if ((t->dir != USB_DIRECTION_OUT || !t->is_control) && t->req.size == 0)
87 return EINVAL;
88
89 /* Nonzero size requires buffer */
90 if (!dma_buffer_is_set(&t->req.buffer) && t->req.size != 0)
91 return EINVAL;
92
93 /* Check expected direction */
94 if (t->pipe->desc.direction != USB_DIRECTION_BOTH &&
95 t->pipe->desc.direction != t->dir)
96 return EBADF;
97
98 /* Check expected transfer type */
99 if ((t->pipe->desc.transfer_type == USB_TRANSFER_CONTROL) != t->is_control)
100 return EBADF;
101
102 async_exch_t *exch = async_exchange_begin(t->pipe->bus_session);
103 if (!exch)
104 return ENOMEM;
105
106 t->req.dir = t->dir;
107 t->req.endpoint = t->pipe->desc.endpoint_no;
108
109 const errno_t rc = usbhc_transfer(exch, &t->req, &t->transferred_size);
110
111 async_exchange_end(exch);
112
113 if (rc == ESTALL)
114 clear_self_endpoint_halt(t->pipe);
115
116 return rc;
117}
118
119/**
120 * Setup the transfer request inside transfer according to dma buffer provided.
121 *
122 * TODO: The buffer could have been allocated as a more strict one. Currently,
123 * we assume that the policy is just the requested one.
124 */
125static void setup_dma_buffer(transfer_t *t, void *base, void *ptr, size_t size)
126{
127 t->req.buffer.virt = base;
128 t->req.buffer.policy = t->pipe->desc.transfer_buffer_policy;
129 t->req.offset = ptr - base;
130 t->req.size = size;
131}
132
133/**
134 * Compatibility wrapper for reads/writes without preallocated buffer.
135 */
136static errno_t transfer_wrap_dma(transfer_t *t, void *buf, size_t size)
137{
138 if (size == 0) {
139 setup_dma_buffer(t, NULL, NULL, 0);
140 return transfer_common(t);
141 }
142
143 void *dma_buf = usb_pipe_alloc_buffer(t->pipe, size);
144 setup_dma_buffer(t, dma_buf, dma_buf, size);
145
146 if (t->dir == USB_DIRECTION_OUT)
147 memcpy(dma_buf, buf, size);
148
149 const errno_t err = transfer_common(t);
150
151 if (!err && t->dir == USB_DIRECTION_IN)
152 memcpy(buf, dma_buf, t->transferred_size);
153
154 usb_pipe_free_buffer(t->pipe, dma_buf);
155 return err;
156}
157
158static errno_t prepare_control(transfer_t *t, const void *setup, size_t setup_size)
159{
160 if ((setup == NULL) || (setup_size != 8))
161 return EINVAL;
162
163 memcpy(&t->req.setup, setup, 8);
164 return EOK;
165}
166
167/** Request a control read transfer on an endpoint pipe.
168 *
169 * This function encapsulates all three stages of a control transfer.
170 *
171 * @param[in] pipe Pipe used for the transfer.
172 * @param[in] setup_buffer Buffer with the setup packet.
173 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
174 * @param[out] data_buffer Buffer for incoming data.
175 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
176 * @param[out] data_transferred_size Number of bytes that were actually
177 * transferred during the DATA stage.
178 * @return Error code.
179 */
180errno_t usb_pipe_control_read(usb_pipe_t *pipe,
181 const void *setup_buffer, size_t setup_buffer_size,
182 void *buffer, size_t buffer_size, size_t *transferred_size)
183{
184 errno_t err;
185 transfer_t transfer = {
186 .pipe = pipe,
187 .dir = USB_DIRECTION_IN,
188 .is_control = true,
189 };
190
191 if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
192 return err;
193
194 if ((err = transfer_wrap_dma(&transfer, buffer, buffer_size)))
195 return err;
196
197 if (transferred_size)
198 *transferred_size = transfer.transferred_size;
199
200 return EOK;
201}
202
203/** Request a control write transfer on an endpoint pipe.
204 *
205 * This function encapsulates all three stages of a control transfer.
206 *
207 * @param[in] pipe Pipe used for the transfer.
208 * @param[in] setup_buffer Buffer with the setup packet.
209 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
210 * @param[in] data_buffer Buffer with data to be sent.
211 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
212 * @return Error code.
213 */
214errno_t usb_pipe_control_write(usb_pipe_t *pipe,
215 const void *setup_buffer, size_t setup_buffer_size,
216 const void *buffer, size_t buffer_size)
217{
218 assert(pipe);
219 errno_t err;
220 transfer_t transfer = {
221 .pipe = pipe,
222 .dir = USB_DIRECTION_OUT,
223 .is_control = true,
224 };
225
226 if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
227 return err;
228
229 return transfer_wrap_dma(&transfer, (void *) buffer, buffer_size);
230}
231
232/**
233 * Allocate a buffer for data transmission, that satisfies the constraints
234 * imposed by the host controller.
235 *
236 * @param[in] pipe Pipe for which the buffer is allocated
237 * @param[in] size Size of the required buffer
238 */
239void *usb_pipe_alloc_buffer(usb_pipe_t *pipe, size_t size)
240{
241 dma_buffer_t buf;
242 if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
243 return NULL;
244
245 return buf.virt;
246}
247
248void usb_pipe_free_buffer(usb_pipe_t *pipe, void *buffer)
249{
250 dma_buffer_t buf;
251 buf.virt = buffer;
252 dma_buffer_free(&buf);
253}
254
255/** Request a read (in) transfer on an endpoint pipe.
256 *
257 * @param[in] pipe Pipe used for the transfer.
258 * @param[out] buffer Buffer where to store the data.
259 * @param[in] size Size of the buffer (in bytes).
260 * @param[out] size_transferred Number of bytes that were actually transferred.
261 * @return Error code.
262 */
263errno_t usb_pipe_read(usb_pipe_t *pipe,
264 void *buffer, size_t size, size_t *size_transferred)
265{
266 assert(pipe);
267 errno_t err;
268 transfer_t transfer = {
269 .pipe = pipe,
270 .dir = USB_DIRECTION_IN,
271 };
272
273 if ((err = transfer_wrap_dma(&transfer, buffer, size)))
274 return err;
275
276 if (size_transferred)
277 *size_transferred = transfer.transferred_size;
278
279 return EOK;
280}
281
282/** Request a write (out) transfer on an endpoint pipe.
283 *
284 * @param[in] pipe Pipe used for the transfer.
285 * @param[in] buffer Buffer with data to transfer.
286 * @param[in] size Size of the buffer (in bytes).
287 * @return Error code.
288 */
289errno_t usb_pipe_write(usb_pipe_t *pipe, const void *buffer, size_t size)
290{
291 assert(pipe);
292 transfer_t transfer = {
293 .pipe = pipe,
294 .dir = USB_DIRECTION_OUT,
295 };
296
297 return transfer_wrap_dma(&transfer, (void *) buffer, size);
298}
299
300/**
301 * Request a read (in) transfer on an endpoint pipe, declaring that buffer
302 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
303 *
304 * @param[in] pipe Pipe used for the transfer.
305 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
306 * @param[in] size Size of the buffer (in bytes).
307 * @param[out] size_transferred Number of bytes that were actually transferred.
308 * @return Error code.
309 */
310errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size,
311 size_t *size_transferred)
312{
313 assert(pipe);
314 errno_t err;
315 transfer_t transfer = {
316 .pipe = pipe,
317 .dir = USB_DIRECTION_IN,
318 };
319
320 setup_dma_buffer(&transfer, base, ptr, size);
321
322 if ((err = transfer_common(&transfer)))
323 return err;
324
325 if (size_transferred)
326 *size_transferred = transfer.transferred_size;
327
328 return EOK;
329}
330
331/**
332 * Request a write (out) transfer on an endpoint pipe, declaring that buffer
333 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
334 *
335 * @param[in] pipe Pipe used for the transfer.
336 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
337 * @param[in] size Size of the buffer (in bytes).
338 * @return Error code.
339 */
340errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size)
341{
342 assert(pipe);
343 transfer_t transfer = {
344 .pipe = pipe,
345 .dir = USB_DIRECTION_OUT,
346 };
347
348 setup_dma_buffer(&transfer, base, ptr, size);
349
350 return transfer_common(&transfer);
351}
352
353/** Initialize USB endpoint pipe.
354 *
355 * @param pipe Endpoint pipe to be initialized.
356 * @param bus_session Endpoint pipe to be initialized.
357 * @return Error code.
358 */
359errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
360{
361 assert(pipe);
362
363 pipe->auto_reset_halt = false;
364 pipe->bus_session = bus_session;
365
366 return EOK;
367}
368
369static const usb_pipe_desc_t default_control_pipe = {
370 .endpoint_no = 0,
371 .transfer_type = USB_TRANSFER_CONTROL,
372 .direction = USB_DIRECTION_BOTH,
373 .max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
374 .transfer_buffer_policy = DMA_POLICY_STRICT,
375};
376
377/** Initialize USB default control pipe.
378 *
379 * This one is special because it must not be registered, it is registered
380 * automatically.
381 *
382 * @param pipe Endpoint pipe to be initialized.
383 * @param bus_session Endpoint pipe to be initialized.
384 * @return Error code.
385 */
386errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe,
387 usb_dev_session_t *bus_session)
388{
389 const errno_t ret = usb_pipe_initialize(pipe, bus_session);
390 if (ret)
391 return ret;
392
393 pipe->desc = default_control_pipe;
394 pipe->auto_reset_halt = true;
395
396 return EOK;
397}
398
399/** Register endpoint with the host controller.
400 *
401 * @param pipe Pipe to be registered.
402 * @param ep_desc Matched endpoint descriptor
403 * @param comp_desc Matched superspeed companion descriptro, if any
404 * @return Error code.
405 */
406errno_t usb_pipe_register(usb_pipe_t *pipe,
407 const usb_standard_endpoint_descriptor_t *ep_desc,
408 const usb_superspeed_endpoint_companion_descriptor_t *comp_desc)
409{
410 assert(pipe);
411 assert(pipe->bus_session);
412 assert(ep_desc);
413
414 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
415 if (!exch)
416 return ENOMEM;
417
418 usb_endpoint_descriptors_t descriptors = { 0 };
419
420#define COPY(field) descriptors.endpoint.field = ep_desc->field
421 COPY(endpoint_address);
422 COPY(attributes);
423 COPY(max_packet_size);
424 COPY(poll_interval);
425#undef COPY
426
427#define COPY(field) descriptors.companion.field = comp_desc->field
428 if (comp_desc) {
429 COPY(max_burst);
430 COPY(attributes);
431 COPY(bytes_per_interval);
432 }
433#undef COPY
434
435 const errno_t ret = usbhc_register_endpoint(exch,
436 &pipe->desc, &descriptors);
437 async_exchange_end(exch);
438 return ret;
439}
440
441/** Revert endpoint registration with the host controller.
442 *
443 * @param pipe Pipe to be unregistered.
444 * @return Error code.
445 */
446errno_t usb_pipe_unregister(usb_pipe_t *pipe)
447{
448 assert(pipe);
449 assert(pipe->bus_session);
450 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
451 if (!exch)
452 return ENOMEM;
453
454 const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
455
456 async_exchange_end(exch);
457 return ret;
458}
459
460/**
461 * @}
462 */
Note: See TracBrowser for help on using the repository browser.