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

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

usb: move endpoint descriptor parsing to HC

This better separates responsibilities. Now the device driver does not
care about the contents of an endpoint descriptor, and HC can parse the
values according to device's actual speed.

Currently, it is device driver's responsibility to fetch endpoint
descriptors, map them and register pipes - sending the endpoint
descriptor back to HC. HC then parses it, and fills the pipe
description, which then sends back to device driver. We shall probably
fetch the endpoint descriptor from inside the HC (also fixing the USB
spec violation of communication with EP0).

  • Property mode set to 100644
File size: 10.0 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 <usbhc_iface.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_request_clear_endpoint_halt(pipe, 0);
60 pipe->auto_reset_halt = true;
61}
62
63/** Request a control read transfer on an endpoint pipe.
64 *
65 * This function encapsulates all three stages of a control transfer.
66 *
67 * @param[in] pipe Pipe used for the transfer.
68 * @param[in] setup_buffer Buffer with the setup packet.
69 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
70 * @param[out] data_buffer Buffer for incoming data.
71 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
72 * @param[out] data_transfered_size Number of bytes that were actually
73 * transfered during the DATA stage.
74 * @return Error code.
75 */
76int usb_pipe_control_read(usb_pipe_t *pipe,
77 const void *setup_buffer, size_t setup_buffer_size,
78 void *buffer, size_t buffer_size, size_t *transfered_size)
79{
80 assert(pipe);
81
82 if ((setup_buffer == NULL) || (setup_buffer_size != 8)) {
83 return EINVAL;
84 }
85
86 if ((buffer == NULL) || (buffer_size == 0)) {
87 return EINVAL;
88 }
89
90 if ((pipe->desc.direction != USB_DIRECTION_BOTH)
91 || (pipe->desc.transfer_type != USB_TRANSFER_CONTROL)) {
92 return EBADF;
93 }
94
95 uint64_t setup_packet;
96 memcpy(&setup_packet, setup_buffer, 8);
97
98 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
99 size_t act_size = 0;
100 const int rc = usbhc_read(exch, pipe->desc.endpoint_no, setup_packet, buffer,
101 buffer_size, &act_size);
102 async_exchange_end(exch);
103
104 if (rc == ESTALL) {
105 clear_self_endpoint_halt(pipe);
106 }
107
108 if (rc == EOK && transfered_size != NULL) {
109 *transfered_size = act_size;
110 }
111
112 return rc;
113}
114
115/** Request a control write transfer on an endpoint pipe.
116 *
117 * This function encapsulates all three stages of a control transfer.
118 *
119 * @param[in] pipe Pipe used for the transfer.
120 * @param[in] setup_buffer Buffer with the setup packet.
121 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
122 * @param[in] data_buffer Buffer with data to be sent.
123 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
124 * @return Error code.
125 */
126int usb_pipe_control_write(usb_pipe_t *pipe,
127 const void *setup_buffer, size_t setup_buffer_size,
128 const void *buffer, size_t buffer_size)
129{
130 assert(pipe);
131
132 if ((setup_buffer == NULL) || (setup_buffer_size != 8)) {
133 return EINVAL;
134 }
135
136 if ((buffer == NULL) && (buffer_size > 0)) {
137 return EINVAL;
138 }
139
140 if ((buffer != NULL) && (buffer_size == 0)) {
141 return EINVAL;
142 }
143
144 if ((pipe->desc.direction != USB_DIRECTION_BOTH)
145 || (pipe->desc.transfer_type != USB_TRANSFER_CONTROL)) {
146 return EBADF;
147 }
148
149 uint64_t setup_packet;
150 memcpy(&setup_packet, setup_buffer, 8);
151
152 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
153 const int rc = usbhc_write(exch,
154 pipe->desc.endpoint_no, setup_packet, buffer, buffer_size);
155 async_exchange_end(exch);
156
157 if (rc == ESTALL) {
158 clear_self_endpoint_halt(pipe);
159 }
160
161 return rc;
162}
163
164/** Request a read (in) transfer on an endpoint pipe.
165 *
166 * @param[in] pipe Pipe used for the transfer.
167 * @param[out] buffer Buffer where to store the data.
168 * @param[in] size Size of the buffer (in bytes).
169 * @param[out] size_transfered Number of bytes that were actually transfered.
170 * @return Error code.
171 */
172int usb_pipe_read(usb_pipe_t *pipe,
173 void *buffer, size_t size, size_t *size_transfered)
174{
175 assert(pipe);
176
177 if (buffer == NULL) {
178 return EINVAL;
179 }
180
181 if (size == 0) {
182 return EINVAL;
183 }
184
185 if (pipe->desc.direction != USB_DIRECTION_IN) {
186 return EBADF;
187 }
188
189 if (pipe->desc.transfer_type == USB_TRANSFER_CONTROL) {
190 return EBADF;
191 }
192
193 async_exch_t *exch;
194 if (pipe->desc.transfer_type == USB_TRANSFER_ISOCHRONOUS)
195 exch = async_exchange_begin(pipe->isoch_session);
196 else
197 exch = async_exchange_begin(pipe->bus_session);
198 size_t act_size = 0;
199 const int rc =
200 usbhc_read(exch, pipe->desc.endpoint_no, 0, buffer, size, &act_size);
201 async_exchange_end(exch);
202
203 if (rc == EOK && size_transfered != NULL) {
204 *size_transfered = act_size;
205 }
206
207 return rc;
208}
209
210/** Request a write (out) transfer on an endpoint pipe.
211 *
212 * @param[in] pipe Pipe used for the transfer.
213 * @param[in] buffer Buffer with data to transfer.
214 * @param[in] size Size of the buffer (in bytes).
215 * @return Error code.
216 */
217int usb_pipe_write(usb_pipe_t *pipe, const void *buffer, size_t size)
218{
219 assert(pipe);
220
221 if (buffer == NULL || size == 0) {
222 return EINVAL;
223 }
224
225 if (pipe->desc.direction != USB_DIRECTION_OUT) {
226 return EBADF;
227 }
228
229 if (pipe->desc.transfer_type == USB_TRANSFER_CONTROL) {
230 return EBADF;
231 }
232
233 async_exch_t *exch;
234 if (pipe->desc.transfer_type == USB_TRANSFER_ISOCHRONOUS)
235 exch = async_exchange_begin(pipe->isoch_session);
236 else
237 exch = async_exchange_begin(pipe->bus_session);
238
239 const int rc = usbhc_write(exch, pipe->desc.endpoint_no, 0, buffer, size);
240 async_exchange_end(exch);
241 return rc;
242}
243
244/** Setup isochronous session for isochronous communication.
245 * Isochronous endpoints need a different session as they might block while waiting for data.
246 *
247 * @param pipe Endpoint pipe being initialized.
248 * @return Error code.
249 */
250static int usb_isoch_session_initialize(usb_pipe_t *pipe) {
251 devman_handle_t handle;
252
253 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
254 if (!exch)
255 return EPARTY;
256
257 int ret = usb_get_my_device_handle(exch, &handle);
258
259 async_exchange_end(exch);
260 if (ret != EOK)
261 return ret;
262
263 pipe->isoch_session = usb_dev_connect(handle);
264 if (!pipe->isoch_session)
265 return ENAK;
266
267 return EOK;
268}
269
270/** Initialize USB endpoint pipe.
271 *
272 * @param pipe Endpoint pipe to be initialized.
273 * @param bus_session Endpoint pipe to be initialized.
274 * @return Error code.
275 */
276int usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session, usb_transfer_type_t transfer_type)
277{
278 assert(pipe);
279
280 pipe->auto_reset_halt = false;
281 pipe->bus_session = bus_session;
282
283 if (transfer_type == USB_TRANSFER_ISOCHRONOUS)
284 return usb_isoch_session_initialize(pipe);
285
286 return EOK;
287}
288
289static const usb_pipe_desc_t default_control_pipe = {
290 .endpoint_no = 0,
291 .transfer_type = USB_TRANSFER_CONTROL,
292 .direction = USB_DIRECTION_BOTH,
293 .max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
294};
295
296/** Initialize USB default control pipe.
297 *
298 * This one is special because it must not be registered, it is registered automatically.
299 *
300 * @param pipe Endpoint pipe to be initialized.
301 * @param bus_session Endpoint pipe to be initialized.
302 * @return Error code.
303 */
304int usb_pipe_initialize_default_control(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
305{
306 const int ret = usb_pipe_initialize(pipe, bus_session, USB_TRANSFER_CONTROL);
307 if (ret)
308 return ret;
309
310 pipe->desc = default_control_pipe;
311
312 return EOK;
313}
314
315/** Register endpoint with the host controller.
316 *
317 * @param pipe Pipe to be registered.
318 * @param ep_desc Matched endpoint descriptor
319 * @param comp_desc Matched superspeed companion descriptro, if any
320 * @return Error code.
321 */
322int usb_pipe_register(usb_pipe_t *pipe, const usb_standard_endpoint_descriptor_t *ep_desc, const usb_superspeed_endpoint_companion_descriptor_t *comp_desc)
323{
324 assert(pipe);
325 assert(pipe->bus_session);
326 assert(ep_desc);
327
328 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
329 if (!exch)
330 return ENOMEM;
331
332 usb_endpoint_descriptors_t descriptors;
333
334#define COPY(field) descriptors.endpoint.field = ep_desc->field
335 COPY(endpoint_address);
336 COPY(attributes);
337 COPY(max_packet_size);
338 COPY(poll_interval);
339#undef COPY
340
341#define COPY(field) descriptors.companion.field = comp_desc->field
342 if (comp_desc) {
343 COPY(max_burst);
344 COPY(attributes);
345 COPY(bytes_per_interval);
346 }
347#undef COPY
348
349 const int ret = usbhc_register_endpoint(exch, &pipe->desc, &descriptors);
350 async_exchange_end(exch);
351 return ret;
352}
353
354/** Revert endpoint registration with the host controller.
355 *
356 * @param pipe Pipe to be unregistered.
357 * @return Error code.
358 */
359int usb_pipe_unregister(usb_pipe_t *pipe)
360{
361 assert(pipe);
362 assert(pipe->bus_session);
363 async_exch_t *exch = async_exchange_begin(pipe->bus_session);
364 if (!exch)
365 return ENOMEM;
366
367 const int ret = usbhc_unregister_endpoint(exch, &pipe->desc);
368
369 async_exchange_end(exch);
370 return ret;
371}
372
373/**
374 * @}
375 */
Note: See TracBrowser for help on using the repository browser.