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

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

usb pipes: refactor transfers

  • Property mode set to 100644
File size: 12.1 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 <async.h>
42#include <errno.h>
43#include <mem.h>
44
45/** Try to clear endpoint halt of default control pipe.
46 *
47 * @param pipe Pipe for control endpoint zero.
48 */
49static void clear_self_endpoint_halt(usb_pipe_t *pipe)
50{
51 assert(pipe != NULL);
52
53 if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
54 return;
55 }
56
57 /* Prevent infinite recursion. */
58 pipe->auto_reset_halt = false;
59 usb_pipe_clear_halt(pipe, pipe);
60 pipe->auto_reset_halt = true;
61}
62
63typedef struct {
64 usb_pipe_t *pipe;
65 usb_direction_t dir;
66 bool is_control;
67 uint64_t setup;
68 void *buffer;
69 size_t buffer_size;
70 size_t transferred_size;
71} transfer_t;
72
73/**
74 * Issue a transfer in a separate exchange.
75 */
76static errno_t transfer_common(transfer_t *t)
77{
78 async_exch_t *exch = async_exchange_begin(t->pipe->bus_session);
79 if (!exch)
80 return ENOMEM;
81
82 const errno_t rc = usbhc_transfer(exch, t->pipe->desc.endpoint_no,
83 t->dir, t->setup, t->buffer, t->buffer_size, &t->transferred_size);
84
85 async_exchange_end(exch);
86
87 if (rc == ESTALL)
88 clear_self_endpoint_halt(t->pipe);
89
90 return rc;
91}
92
93/**
94 * Compatibility wrapper for reads/writes without preallocated buffer.
95 */
96static errno_t transfer_wrap_dma(transfer_t *t)
97{
98 void *orig_buffer = t->buffer;
99 t->buffer = usb_pipe_alloc_buffer(t->pipe, t->buffer_size);
100
101 if (t->dir == USB_DIRECTION_OUT)
102 memcpy(t->buffer, orig_buffer, t->buffer_size);
103
104 const errno_t err = transfer_common(t);
105
106 if (!err && t->dir == USB_DIRECTION_IN)
107 memcpy(orig_buffer, t->buffer, t->transferred_size);
108
109 usb_pipe_free_buffer(t->pipe, t->buffer);
110 t->buffer = orig_buffer;
111 return err;
112}
113
114static errno_t transfer_check(const transfer_t *t)
115{
116 if (!t->pipe)
117 return EBADMEM;
118
119 /* Only control writes make sense without buffer */
120 if ((t->dir != USB_DIRECTION_OUT || !t->is_control)
121 && (t->buffer == NULL || t->buffer_size == 0))
122 return EINVAL;
123
124 /* Nonzero size requires buffer */
125 if (t->buffer == NULL && t->buffer_size != 0)
126 return EINVAL;
127
128 /* Check expected direction */
129 if (t->pipe->desc.direction != USB_DIRECTION_BOTH &&
130 t->pipe->desc.direction != t->dir)
131 return EBADF;
132
133 /* Check expected transfer type */
134 if ((t->pipe->desc.transfer_type == USB_TRANSFER_CONTROL) != t->is_control)
135 return EBADF;
136
137 return EOK;
138}
139
140static errno_t prepare_control(transfer_t *t, const void *setup, size_t setup_size)
141{
142 if ((setup == NULL) || (setup_size != 8))
143 return EINVAL;
144
145 memcpy(&t->setup, setup, 8);
146 return EOK;
147}
148
149/** Request a control read transfer on an endpoint pipe.
150 *
151 * This function encapsulates all three stages of a control transfer.
152 *
153 * @param[in] pipe Pipe used for the transfer.
154 * @param[in] setup_buffer Buffer with the setup packet.
155 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
156 * @param[out] data_buffer Buffer for incoming data.
157 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
158 * @param[out] data_transferred_size Number of bytes that were actually
159 * transferred during the DATA stage.
160 * @return Error code.
161 */
162errno_t usb_pipe_control_read(usb_pipe_t *pipe,
163 const void *setup_buffer, size_t setup_buffer_size,
164 void *buffer, size_t buffer_size, size_t *transferred_size)
165{
166 errno_t err;
167 transfer_t transfer = {
168 .pipe = pipe,
169 .dir = USB_DIRECTION_IN,
170 .is_control = true,
171 .buffer = buffer,
172 .buffer_size = buffer_size
173 };
174
175 if ((err = transfer_check(&transfer)))
176 return err;
177
178 if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
179 return err;
180
181 if ((err = transfer_wrap_dma(&transfer)))
182 return err;
183
184 if (transferred_size)
185 *transferred_size = transfer.transferred_size;
186
187 return EOK;
188}
189
190/** Request a control write transfer on an endpoint pipe.
191 *
192 * This function encapsulates all three stages of a control transfer.
193 *
194 * @param[in] pipe Pipe used for the transfer.
195 * @param[in] setup_buffer Buffer with the setup packet.
196 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
197 * @param[in] data_buffer Buffer with data to be sent.
198 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
199 * @return Error code.
200 */
201errno_t usb_pipe_control_write(usb_pipe_t *pipe,
202 const void *setup_buffer, size_t setup_buffer_size,
203 const void *buffer, size_t buffer_size)
204{
205 assert(pipe);
206 errno_t err;
207 transfer_t transfer = {
208 .pipe = pipe,
209 .dir = USB_DIRECTION_OUT,
210 .is_control = true,
211 .buffer = (void *) buffer,
212 .buffer_size = buffer_size
213 };
214
215 if ((err = transfer_check(&transfer)))
216 return err;
217
218 if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
219 return err;
220
221 return transfer_wrap_dma(&transfer);
222}
223
224/**
225 * Allocate a buffer for data transmission, that satisfies the constraints
226 * imposed by the host controller.
227 *
228 * @param[in] pipe Pipe for which the buffer is allocated
229 * @param[in] size Size of the required buffer
230 */
231void *usb_pipe_alloc_buffer(usb_pipe_t *pipe, size_t size)
232{
233 dma_buffer_t buf;
234 if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
235 return NULL;
236
237 return buf.virt;
238}
239
240void usb_pipe_free_buffer(usb_pipe_t *pipe, void *buffer)
241{
242 dma_buffer_t buf;
243 buf.virt = buffer;
244 dma_buffer_free(&buf);
245}
246
247/** Request a read (in) transfer on an endpoint pipe.
248 *
249 * @param[in] pipe Pipe used for the transfer.
250 * @param[out] buffer Buffer where to store the data.
251 * @param[in] size Size of the buffer (in bytes).
252 * @param[out] size_transferred Number of bytes that were actually transferred.
253 * @return Error code.
254 */
255errno_t usb_pipe_read(usb_pipe_t *pipe,
256 void *buffer, size_t size, size_t *size_transferred)
257{
258 assert(pipe);
259 errno_t err;
260 transfer_t transfer = {
261 .pipe = pipe,
262 .dir = USB_DIRECTION_IN,
263 .buffer = buffer,
264 .buffer_size = size,
265 };
266
267 if ((err = transfer_check(&transfer)))
268 return err;
269
270 if ((err = transfer_wrap_dma(&transfer)))
271 return err;
272
273 if (size_transferred)
274 *size_transferred = transfer.transferred_size;
275
276 return EOK;
277}
278
279/** Request a write (out) transfer on an endpoint pipe.
280 *
281 * @param[in] pipe Pipe used for the transfer.
282 * @param[in] buffer Buffer with data to transfer.
283 * @param[in] size Size of the buffer (in bytes).
284 * @return Error code.
285 */
286errno_t usb_pipe_write(usb_pipe_t *pipe, const void *buffer, size_t size)
287{
288 assert(pipe);
289 errno_t err;
290 transfer_t transfer = {
291 .pipe = pipe,
292 .dir = USB_DIRECTION_OUT,
293 .buffer = (void *) buffer,
294 .buffer_size = size
295 };
296
297 if ((err = transfer_check(&transfer)))
298 return err;
299
300 if ((err = transfer_wrap_dma(&transfer)))
301 return err;
302
303 return EOK;
304}
305
306/**
307 * Request a read (in) transfer on an endpoint pipe, declaring that buffer
308 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
309 *
310 * @param[in] pipe Pipe used for the transfer.
311 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
312 * @param[in] size Size of the buffer (in bytes).
313 * @param[out] size_transferred Number of bytes that were actually transferred.
314 * @return Error code.
315 */
316errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *buffer, size_t size,
317 size_t *size_transferred)
318{
319 assert(pipe);
320 errno_t err;
321 transfer_t transfer = {
322 .pipe = pipe,
323 .dir = USB_DIRECTION_IN,
324 .buffer = buffer,
325 .buffer_size = size
326 };
327
328 if ((err = transfer_check(&transfer)))
329 return err;
330
331 if ((err = transfer_common(&transfer)))
332 return err;
333
334 if (size_transferred)
335 *size_transferred = transfer.transferred_size;
336
337 return EOK;
338}
339
340/**
341 * Request a write (out) transfer on an endpoint pipe, declaring that buffer
342 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
343 *
344 * @param[in] pipe Pipe used for the transfer.
345 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
346 * @param[in] size Size of the buffer (in bytes).
347 * @return Error code.
348 */
349errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *buffer, size_t size)
350{
351 assert(pipe);
352 errno_t err;
353 transfer_t transfer = {
354 .pipe = pipe,
355 .dir = USB_DIRECTION_OUT,
356 .buffer = buffer,
357 .buffer_size = size
358 };
359
360 if ((err = transfer_check(&transfer)))
361 return err;
362
363 if ((err = transfer_common(&transfer)))
364 return err;
365
366 return EOK;
367}
368
369/** Initialize USB endpoint pipe.
370 *
371 * @param pipe Endpoint pipe to be initialized.
372 * @param bus_session Endpoint pipe to be initialized.
373 * @return Error code.
374 */
375errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
376{
377 assert(pipe);
378
379 pipe->auto_reset_halt = false;
380 pipe->bus_session = bus_session;
381
382 return EOK;
383}
384
385static const usb_pipe_desc_t default_control_pipe = {
386 .endpoint_no = 0,
387 .transfer_type = USB_TRANSFER_CONTROL,
388 .direction = USB_DIRECTION_BOTH,
389 .max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
390};
391
392/** Initialize USB default control pipe.
393 *
394 * This one is special because it must not be registered, it is registered automatically.
395 *
396 * @param pipe Endpoint pipe to be initialized.
397 * @param bus_session Endpoint pipe to be initialized.
398 * @return Error code.
399 */
400errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
401{
402 const errno_t ret = usb_pipe_initialize(pipe, bus_session);
403 if (ret)
404 return ret;
405
406 pipe->desc = default_control_pipe;
407 pipe->auto_reset_halt = true;
408
409 return EOK;
410}
411
412/** Register endpoint with the host controller.
413 *
414 * @param pipe Pipe to be registered.
415 * @param ep_desc Matched endpoint descriptor
416 * @param comp_desc Matched superspeed companion descriptro, if any
417 * @return Error code.
418 */
419errno_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)
420{
421 assert(pipe);
422 assert(pipe->bus_session);
423 assert(ep_desc);
424
425 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
426 if (!exch)
427 return ENOMEM;
428
429 usb_endpoint_descriptors_t descriptors;
430
431#define COPY(field) descriptors.endpoint.field = ep_desc->field
432 COPY(endpoint_address);
433 COPY(attributes);
434 COPY(max_packet_size);
435 COPY(poll_interval);
436#undef COPY
437
438#define COPY(field) descriptors.companion.field = comp_desc->field
439 if (comp_desc) {
440 COPY(max_burst);
441 COPY(attributes);
442 COPY(bytes_per_interval);
443 }
444#undef COPY
445
446 const errno_t ret = usbhc_register_endpoint(exch, &pipe->desc, &descriptors);
447 async_exchange_end(exch);
448 return ret;
449}
450
451/** Revert endpoint registration with the host controller.
452 *
453 * @param pipe Pipe to be unregistered.
454 * @return Error code.
455 */
456errno_t usb_pipe_unregister(usb_pipe_t *pipe)
457{
458 assert(pipe);
459 assert(pipe->bus_session);
460 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
461 if (!exch)
462 return ENOMEM;
463
464 const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
465
466 async_exchange_end(exch);
467 return ret;
468}
469
470/**
471 * @}
472 */
Note: See TracBrowser for help on using the repository browser.