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

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

usb: fix uninitialized descriptors

  • Property mode set to 100644
File size: 12.3 KB
RevLine 
[6865243c]1/*
2 * Copyright (c) 2011 Vojtech Horky
[bd575647]3 * Copyright (c) 2011 Jan Vesely
[e0a5d4c]4 * Copyright (c) 2018 Ondrej Hlavaty, Michal Staruch
[6865243c]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 */
[160b75e]30/** @addtogroup libusbdev
[6865243c]31 * @{
32 */
33/** @file
[3538b0e]34 * USB endpoint pipes functions.
[6865243c]35 */
[bd575647]36#include <usb/dev/pipes.h>
[023a902]37#include <usb/dev/request.h>
[c01987c]38#include <usb/usb.h>
[50206e9]39#include <usb/dma_buffer.h>
[c01987c]40
[43f698b]41#include <assert.h>
[239eea41]42#include <bitops.h>
[c01987c]43#include <async.h>
[239eea41]44#include <as.h>
[c01987c]45#include <errno.h>
46#include <mem.h>
[563fb40]47
[023a902]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
[816f5f4]56 if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
[023a902]57 return;
58 }
59
60 /* Prevent infinite recursion. */
61 pipe->auto_reset_halt = false;
[d08aa42d]62 usb_pipe_clear_halt(pipe, pipe);
[023a902]63 pipe->auto_reset_halt = true;
64}
[a76b01b4]65
[239eea41]66/* Helper structure to avoid passing loads of arguments through */
[d14688d]67typedef struct {
68 usb_pipe_t *pipe;
69 usb_direction_t dir;
[239eea41]70 bool is_control; // Only for checking purposes
71
72 usbhc_iface_transfer_request_t req;
73
[d14688d]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{
[d345ce2]82 if (!t->pipe)
83 return EBADMEM;
84
85 /* Only control writes make sense without buffer */
[1d758fc]86 if ((t->dir != USB_DIRECTION_OUT || !t->is_control) && t->req.size == 0)
[d345ce2]87 return EINVAL;
88
89 /* Nonzero size requires buffer */
[1d758fc]90 if (!dma_buffer_is_set(&t->req.buffer) && t->req.size != 0)
[d345ce2]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.
[1d758fc]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.
[d345ce2]124 */
125static void setup_dma_buffer(transfer_t *t, void *base, void *ptr, size_t size)
126{
[1d758fc]127 t->req.buffer.virt = base;
128 t->req.buffer.policy = t->pipe->desc.transfer_buffer_policy;
[d345ce2]129 t->req.offset = ptr - base;
130 t->req.size = size;
131}
132
[d14688d]133/**
134 * Compatibility wrapper for reads/writes without preallocated buffer.
135 */
[d345ce2]136static errno_t transfer_wrap_dma(transfer_t *t, void *buf, size_t size)
[d14688d]137{
[1d758fc]138 if (size == 0) {
139 setup_dma_buffer(t, NULL, NULL, 0);
140 return transfer_common(t);
141 }
142
[d345ce2]143 void *dma_buf = usb_pipe_alloc_buffer(t->pipe, size);
144 setup_dma_buffer(t, dma_buf, dma_buf, size);
[d14688d]145
146 if (t->dir == USB_DIRECTION_OUT)
[d345ce2]147 memcpy(dma_buf, buf, size);
[d14688d]148
149 const errno_t err = transfer_common(t);
150
151 if (!err && t->dir == USB_DIRECTION_IN)
[d345ce2]152 memcpy(buf, dma_buf, t->transferred_size);
[d14688d]153
[d345ce2]154 usb_pipe_free_buffer(t->pipe, dma_buf);
[d14688d]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
[239eea41]163 memcpy(&t->req.setup, setup, 8);
[d14688d]164 return EOK;
165}
166
[023a902]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).
[db51a6a6]176 * @param[out] data_transferred_size Number of bytes that were actually
177 * transferred during the DATA stage.
[023a902]178 * @return Error code.
179 */
[5a6cc679]180errno_t usb_pipe_control_read(usb_pipe_t *pipe,
[023a902]181 const void *setup_buffer, size_t setup_buffer_size,
[db51a6a6]182 void *buffer, size_t buffer_size, size_t *transferred_size)
[023a902]183{
[d14688d]184 errno_t err;
185 transfer_t transfer = {
186 .pipe = pipe,
187 .dir = USB_DIRECTION_IN,
188 .is_control = true,
189 };
[023a902]190
[d14688d]191 if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
192 return err;
[023a902]193
[d345ce2]194 if ((err = transfer_wrap_dma(&transfer, buffer, buffer_size)))
[d14688d]195 return err;
[023a902]196
[d14688d]197 if (transferred_size)
198 *transferred_size = transfer.transferred_size;
[023a902]199
[d14688d]200 return EOK;
[023a902]201}
[a76b01b4]202
[023a902]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 */
[5a6cc679]214errno_t usb_pipe_control_write(usb_pipe_t *pipe,
[023a902]215 const void *setup_buffer, size_t setup_buffer_size,
[22ecbde]216 const void *buffer, size_t buffer_size)
[023a902]217{
218 assert(pipe);
[d14688d]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
[d345ce2]229 return transfer_wrap_dma(&transfer, (void *) buffer, buffer_size);
[023a902]230}
[a76b01b4]231
[50206e9]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;
[fdc2253b]242 if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
[50206e9]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
[023a902]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).
[db51a6a6]260 * @param[out] size_transferred Number of bytes that were actually transferred.
[023a902]261 * @return Error code.
262 */
[5a6cc679]263errno_t usb_pipe_read(usb_pipe_t *pipe,
[db51a6a6]264 void *buffer, size_t size, size_t *size_transferred)
[023a902]265{
266 assert(pipe);
[d14688d]267 errno_t err;
268 transfer_t transfer = {
269 .pipe = pipe,
270 .dir = USB_DIRECTION_IN,
271 };
[023a902]272
[d345ce2]273 if ((err = transfer_wrap_dma(&transfer, buffer, size)))
[d14688d]274 return err;
[023a902]275
[d14688d]276 if (size_transferred)
277 *size_transferred = transfer.transferred_size;
[023a902]278
[d14688d]279 return EOK;
[023a902]280}
[a76b01b4]281
[023a902]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 */
[5a6cc679]289errno_t usb_pipe_write(usb_pipe_t *pipe, const void *buffer, size_t size)
[023a902]290{
291 assert(pipe);
[d14688d]292 transfer_t transfer = {
293 .pipe = pipe,
294 .dir = USB_DIRECTION_OUT,
295 };
[023a902]296
[d345ce2]297 return transfer_wrap_dma(&transfer, (void *) buffer, size);
[023a902]298}
[a76b01b4]299
[4e17d54]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 */
[d345ce2]310errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size,
[4e17d54]311 size_t *size_transferred)
312{
313 assert(pipe);
[d14688d]314 errno_t err;
315 transfer_t transfer = {
316 .pipe = pipe,
317 .dir = USB_DIRECTION_IN,
318 };
[4e17d54]319
[d345ce2]320 setup_dma_buffer(&transfer, base, ptr, size);
[4e17d54]321
[d14688d]322 if ((err = transfer_common(&transfer)))
323 return err;
[4e17d54]324
[d14688d]325 if (size_transferred)
326 *size_transferred = transfer.transferred_size;
[4e17d54]327
[d14688d]328 return EOK;
[4e17d54]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 */
[ae3a941]340errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size)
[4e17d54]341{
342 assert(pipe);
[d14688d]343 transfer_t transfer = {
344 .pipe = pipe,
345 .dir = USB_DIRECTION_OUT,
346 };
[4e17d54]347
[d345ce2]348 setup_dma_buffer(&transfer, base, ptr, size);
[4e17d54]349
[d345ce2]350 return transfer_common(&transfer);
[4e17d54]351}
352
[c804484]353/** Initialize USB endpoint pipe.
354 *
355 * @param pipe Endpoint pipe to be initialized.
[9e5b162]356 * @param bus_session Endpoint pipe to be initialized.
[c804484]357 * @return Error code.
358 */
[5a6cc679]359errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
[c804484]360{
361 assert(pipe);
362
363 pipe->auto_reset_halt = false;
[8582076]364 pipe->bus_session = bus_session;
[c804484]365
[9efad54]366 return EOK;
[c804484]367}
[a76b01b4]368
[9efad54]369static const usb_pipe_desc_t default_control_pipe = {
370 .endpoint_no = 0,
371 .transfer_type = USB_TRANSFER_CONTROL,
[9e5b162]372 .direction = USB_DIRECTION_BOTH,
[9efad54]373 .max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
[1d758fc]374 .transfer_buffer_policy = DMA_POLICY_STRICT,
[9e5b162]375};
376
[9efad54]377/** Initialize USB default control pipe.
378 *
[ae3a941]379 * This one is special because it must not be registered, it is registered
380 * automatically.
[c804484]381 *
382 * @param pipe Endpoint pipe to be initialized.
[9efad54]383 * @param bus_session Endpoint pipe to be initialized.
[c804484]384 * @return Error code.
385 */
[ae3a941]386errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe,
387 usb_dev_session_t *bus_session)
[c804484]388{
[5a6cc679]389 const errno_t ret = usb_pipe_initialize(pipe, bus_session);
[9efad54]390 if (ret)
391 return ret;
392
393 pipe->desc = default_control_pipe;
[ba654f2]394 pipe->auto_reset_halt = true;
[9efad54]395
396 return EOK;
[c804484]397}
[a76b01b4]398
[c804484]399/** Register endpoint with the host controller.
400 *
401 * @param pipe Pipe to be registered.
[9efad54]402 * @param ep_desc Matched endpoint descriptor
403 * @param comp_desc Matched superspeed companion descriptro, if any
[c804484]404 * @return Error code.
405 */
[ae3a941]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)
[c804484]409{
410 assert(pipe);
[11e9e613]411 assert(pipe->bus_session);
[9efad54]412 assert(ep_desc);
[816f5f4]413
[11e9e613]414 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
415 if (!exch)
416 return ENOMEM;
[816f5f4]417
[e773f58]418 usb_endpoint_descriptors_t descriptors = { 0 };
[9efad54]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
[816f5f4]434
[ae3a941]435 const errno_t ret = usbhc_register_endpoint(exch,
436 &pipe->desc, &descriptors);
[11e9e613]437 async_exchange_end(exch);
438 return ret;
[c804484]439}
[a76b01b4]440
[c804484]441/** Revert endpoint registration with the host controller.
442 *
443 * @param pipe Pipe to be unregistered.
444 * @return Error code.
445 */
[5a6cc679]446errno_t usb_pipe_unregister(usb_pipe_t *pipe)
[c804484]447{
448 assert(pipe);
[11e9e613]449 assert(pipe->bus_session);
450 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
451 if (!exch)
452 return ENOMEM;
[816f5f4]453
[5a6cc679]454 const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
[816f5f4]455
[11e9e613]456 async_exchange_end(exch);
457 return ret;
[c804484]458}
459
[6865243c]460/**
461 * @}
462 */
Note: See TracBrowser for help on using the repository browser.