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

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

usb: pass buffers as base + ptr to support partial writes/reads

  • Property mode set to 100644
File size: 12.0 KB
RevLine 
[6865243c]1/*
2 * Copyright (c) 2011 Vojtech Horky
[bd575647]3 * Copyright (c) 2011 Jan Vesely
[6865243c]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 */
[160b75e]29/** @addtogroup libusbdev
[6865243c]30 * @{
31 */
32/** @file
[3538b0e]33 * USB endpoint pipes functions.
[6865243c]34 */
[bd575647]35#include <usb/dev/pipes.h>
[023a902]36#include <usb/dev/request.h>
[c01987c]37#include <usb/usb.h>
[50206e9]38#include <usb/dma_buffer.h>
[c01987c]39
[43f698b]40#include <assert.h>
[239eea41]41#include <bitops.h>
[c01987c]42#include <async.h>
[239eea41]43#include <as.h>
[c01987c]44#include <errno.h>
45#include <mem.h>
[563fb40]46
[023a902]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
[816f5f4]55 if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
[023a902]56 return;
57 }
58
59 /* Prevent infinite recursion. */
60 pipe->auto_reset_halt = false;
[d08aa42d]61 usb_pipe_clear_halt(pipe, pipe);
[023a902]62 pipe->auto_reset_halt = true;
63}
[a76b01b4]64
[239eea41]65/* Helper structure to avoid passing loads of arguments through */
[d14688d]66typedef struct {
67 usb_pipe_t *pipe;
68 usb_direction_t dir;
[239eea41]69 bool is_control; // Only for checking purposes
70
71 usbhc_iface_transfer_request_t req;
72
[d14688d]73 size_t transferred_size;
74} transfer_t;
75
76/**
77 * Issue a transfer in a separate exchange.
78 */
79static errno_t transfer_common(transfer_t *t)
80{
[d345ce2]81 if (!t->pipe)
82 return EBADMEM;
83
84 /* Only control writes make sense without buffer */
85 if ((t->dir != USB_DIRECTION_OUT || !t->is_control)
86 && (t->req.base == NULL || t->req.size == 0))
87 return EINVAL;
88
89 /* Nonzero size requires buffer */
90 if (t->req.base == NULL && 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
[d14688d]102 async_exch_t *exch = async_exchange_begin(t->pipe->bus_session);
103 if (!exch)
104 return ENOMEM;
105
[239eea41]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);
[d14688d]110
111 async_exchange_end(exch);
112
113 if (rc == ESTALL)
114 clear_self_endpoint_halt(t->pipe);
115
116 return rc;
117}
118
[d345ce2]119/**
120 * Setup the transfer request inside transfer according to dma buffer provided.
121 */
122static void setup_dma_buffer(transfer_t *t, void *base, void *ptr, size_t size)
123{
124 t->req.base = base;
125 t->req.offset = ptr - base;
126 t->req.size = size;
127 t->req.buffer_policy = t->pipe->desc.transfer_buffer_policy;
128}
129
[d14688d]130/**
131 * Compatibility wrapper for reads/writes without preallocated buffer.
132 */
[d345ce2]133static errno_t transfer_wrap_dma(transfer_t *t, void *buf, size_t size)
[d14688d]134{
[d345ce2]135 void *dma_buf = usb_pipe_alloc_buffer(t->pipe, size);
136 setup_dma_buffer(t, dma_buf, dma_buf, size);
[d14688d]137
138 if (t->dir == USB_DIRECTION_OUT)
[d345ce2]139 memcpy(dma_buf, buf, size);
[d14688d]140
141 const errno_t err = transfer_common(t);
142
143 if (!err && t->dir == USB_DIRECTION_IN)
[d345ce2]144 memcpy(buf, dma_buf, t->transferred_size);
[d14688d]145
[d345ce2]146 usb_pipe_free_buffer(t->pipe, dma_buf);
[d14688d]147 return err;
148}
149
150static errno_t prepare_control(transfer_t *t, const void *setup, size_t setup_size)
151{
152 if ((setup == NULL) || (setup_size != 8))
153 return EINVAL;
154
[239eea41]155 memcpy(&t->req.setup, setup, 8);
[d14688d]156 return EOK;
157}
158
[023a902]159/** Request a control read transfer on an endpoint pipe.
160 *
161 * This function encapsulates all three stages of a control transfer.
162 *
163 * @param[in] pipe Pipe used for the transfer.
164 * @param[in] setup_buffer Buffer with the setup packet.
165 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
166 * @param[out] data_buffer Buffer for incoming data.
167 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
[db51a6a6]168 * @param[out] data_transferred_size Number of bytes that were actually
169 * transferred during the DATA stage.
[023a902]170 * @return Error code.
171 */
[5a6cc679]172errno_t usb_pipe_control_read(usb_pipe_t *pipe,
[023a902]173 const void *setup_buffer, size_t setup_buffer_size,
[db51a6a6]174 void *buffer, size_t buffer_size, size_t *transferred_size)
[023a902]175{
[d14688d]176 errno_t err;
177 transfer_t transfer = {
178 .pipe = pipe,
179 .dir = USB_DIRECTION_IN,
180 .is_control = true,
181 };
[023a902]182
[d14688d]183 if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
184 return err;
[023a902]185
[d345ce2]186 if ((err = transfer_wrap_dma(&transfer, buffer, buffer_size)))
[d14688d]187 return err;
[023a902]188
[d14688d]189 if (transferred_size)
190 *transferred_size = transfer.transferred_size;
[023a902]191
[d14688d]192 return EOK;
[023a902]193}
[a76b01b4]194
[023a902]195/** Request a control write transfer on an endpoint pipe.
196 *
197 * This function encapsulates all three stages of a control transfer.
198 *
199 * @param[in] pipe Pipe used for the transfer.
200 * @param[in] setup_buffer Buffer with the setup packet.
201 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
202 * @param[in] data_buffer Buffer with data to be sent.
203 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
204 * @return Error code.
205 */
[5a6cc679]206errno_t usb_pipe_control_write(usb_pipe_t *pipe,
[023a902]207 const void *setup_buffer, size_t setup_buffer_size,
[22ecbde]208 const void *buffer, size_t buffer_size)
[023a902]209{
210 assert(pipe);
[d14688d]211 errno_t err;
212 transfer_t transfer = {
213 .pipe = pipe,
214 .dir = USB_DIRECTION_OUT,
215 .is_control = true,
216 };
217
218 if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
219 return err;
220
[d345ce2]221 return transfer_wrap_dma(&transfer, (void *) buffer, buffer_size);
[023a902]222}
[a76b01b4]223
[50206e9]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;
[fdc2253b]234 if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
[50206e9]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
[023a902]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).
[db51a6a6]252 * @param[out] size_transferred Number of bytes that were actually transferred.
[023a902]253 * @return Error code.
254 */
[5a6cc679]255errno_t usb_pipe_read(usb_pipe_t *pipe,
[db51a6a6]256 void *buffer, size_t size, size_t *size_transferred)
[023a902]257{
258 assert(pipe);
[d14688d]259 errno_t err;
260 transfer_t transfer = {
261 .pipe = pipe,
262 .dir = USB_DIRECTION_IN,
263 };
[023a902]264
[d345ce2]265 if ((err = transfer_wrap_dma(&transfer, buffer, size)))
[d14688d]266 return err;
[023a902]267
[d14688d]268 if (size_transferred)
269 *size_transferred = transfer.transferred_size;
[023a902]270
[d14688d]271 return EOK;
[023a902]272}
[a76b01b4]273
[023a902]274/** Request a write (out) transfer on an endpoint pipe.
275 *
276 * @param[in] pipe Pipe used for the transfer.
277 * @param[in] buffer Buffer with data to transfer.
278 * @param[in] size Size of the buffer (in bytes).
279 * @return Error code.
280 */
[5a6cc679]281errno_t usb_pipe_write(usb_pipe_t *pipe, const void *buffer, size_t size)
[023a902]282{
283 assert(pipe);
[d14688d]284 transfer_t transfer = {
285 .pipe = pipe,
286 .dir = USB_DIRECTION_OUT,
287 };
[023a902]288
[d345ce2]289 return transfer_wrap_dma(&transfer, (void *) buffer, size);
[023a902]290}
[a76b01b4]291
[4e17d54]292/**
293 * Request a read (in) transfer on an endpoint pipe, declaring that buffer
294 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
295 *
296 * @param[in] pipe Pipe used for the transfer.
297 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
298 * @param[in] size Size of the buffer (in bytes).
299 * @param[out] size_transferred Number of bytes that were actually transferred.
300 * @return Error code.
301 */
[d345ce2]302errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size,
[4e17d54]303 size_t *size_transferred)
304{
305 assert(pipe);
[d14688d]306 errno_t err;
307 transfer_t transfer = {
308 .pipe = pipe,
309 .dir = USB_DIRECTION_IN,
310 };
[4e17d54]311
[d345ce2]312 setup_dma_buffer(&transfer, base, ptr, size);
[4e17d54]313
[d14688d]314 if ((err = transfer_common(&transfer)))
315 return err;
[4e17d54]316
[d14688d]317 if (size_transferred)
318 *size_transferred = transfer.transferred_size;
[4e17d54]319
[d14688d]320 return EOK;
[4e17d54]321}
322
323/**
324 * Request a write (out) transfer on an endpoint pipe, declaring that buffer
325 * is pointing to a memory area previously allocated by usb_pipe_alloc_buffer.
326 *
327 * @param[in] pipe Pipe used for the transfer.
328 * @param[in] buffer Buffer, previously allocated with usb_pipe_alloc_buffer.
329 * @param[in] size Size of the buffer (in bytes).
330 * @return Error code.
331 */
[d345ce2]332errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *base, void* ptr, size_t size)
[4e17d54]333{
334 assert(pipe);
[d14688d]335 transfer_t transfer = {
336 .pipe = pipe,
337 .dir = USB_DIRECTION_OUT,
338 };
[4e17d54]339
[d345ce2]340 setup_dma_buffer(&transfer, base, ptr, size);
[4e17d54]341
[d345ce2]342 return transfer_common(&transfer);
[4e17d54]343}
344
[c804484]345/** Initialize USB endpoint pipe.
346 *
347 * @param pipe Endpoint pipe to be initialized.
[9e5b162]348 * @param bus_session Endpoint pipe to be initialized.
[c804484]349 * @return Error code.
350 */
[5a6cc679]351errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
[c804484]352{
353 assert(pipe);
354
355 pipe->auto_reset_halt = false;
[8582076]356 pipe->bus_session = bus_session;
[c804484]357
[9efad54]358 return EOK;
[c804484]359}
[a76b01b4]360
[9efad54]361static const usb_pipe_desc_t default_control_pipe = {
362 .endpoint_no = 0,
363 .transfer_type = USB_TRANSFER_CONTROL,
[9e5b162]364 .direction = USB_DIRECTION_BOTH,
[9efad54]365 .max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
[9e5b162]366};
367
[9efad54]368/** Initialize USB default control pipe.
369 *
370 * This one is special because it must not be registered, it is registered automatically.
[c804484]371 *
372 * @param pipe Endpoint pipe to be initialized.
[9efad54]373 * @param bus_session Endpoint pipe to be initialized.
[c804484]374 * @return Error code.
375 */
[5a6cc679]376errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
[c804484]377{
[5a6cc679]378 const errno_t ret = usb_pipe_initialize(pipe, bus_session);
[9efad54]379 if (ret)
380 return ret;
381
382 pipe->desc = default_control_pipe;
[ba654f2]383 pipe->auto_reset_halt = true;
[9efad54]384
385 return EOK;
[c804484]386}
[a76b01b4]387
[c804484]388/** Register endpoint with the host controller.
389 *
390 * @param pipe Pipe to be registered.
[9efad54]391 * @param ep_desc Matched endpoint descriptor
392 * @param comp_desc Matched superspeed companion descriptro, if any
[c804484]393 * @return Error code.
394 */
[5a6cc679]395errno_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)
[c804484]396{
397 assert(pipe);
[11e9e613]398 assert(pipe->bus_session);
[9efad54]399 assert(ep_desc);
[816f5f4]400
[11e9e613]401 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
402 if (!exch)
403 return ENOMEM;
[816f5f4]404
[9efad54]405 usb_endpoint_descriptors_t descriptors;
406
407#define COPY(field) descriptors.endpoint.field = ep_desc->field
408 COPY(endpoint_address);
409 COPY(attributes);
410 COPY(max_packet_size);
411 COPY(poll_interval);
412#undef COPY
413
414#define COPY(field) descriptors.companion.field = comp_desc->field
415 if (comp_desc) {
416 COPY(max_burst);
417 COPY(attributes);
418 COPY(bytes_per_interval);
419 }
420#undef COPY
[816f5f4]421
[5a6cc679]422 const errno_t ret = usbhc_register_endpoint(exch, &pipe->desc, &descriptors);
[11e9e613]423 async_exchange_end(exch);
424 return ret;
[c804484]425}
[a76b01b4]426
[c804484]427/** Revert endpoint registration with the host controller.
428 *
429 * @param pipe Pipe to be unregistered.
430 * @return Error code.
431 */
[5a6cc679]432errno_t usb_pipe_unregister(usb_pipe_t *pipe)
[c804484]433{
434 assert(pipe);
[11e9e613]435 assert(pipe->bus_session);
436 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
437 if (!exch)
438 return ENOMEM;
[816f5f4]439
[5a6cc679]440 const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
[816f5f4]441
[11e9e613]442 async_exchange_end(exch);
443 return ret;
[c804484]444}
445
[6865243c]446/**
447 * @}
448 */
Note: See TracBrowser for help on using the repository browser.