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

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

libdrv: usb iface callbacks joined

In addition to handle and current interface, it is good for the device
to know its address and speed. Also, it is expected to add some other
fields (stats, info tied to devices of specific speed and so on) in the
future. Instead of adding yet another call, join those two and let HC
fill a description structure.

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