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
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.