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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fc3dfe6d was 239eea41, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

libdrv usbhc iface: wrap transfer arguments into structure

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