source: mainline/uspace/lib/usb/src/pipesio.c@ fa0f53b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fa0f53b was fa0f53b, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Automatic endpoint halt clearing (#190)

To prevent total stall of endpoint 0, if some control read/write
function detects a stall, it automatically tries to clear it.

On real HW it works more or less fine, simulators does not always
handle stall condition correctly.

  • Property mode set to 100644
File size: 14.6 KB
RevLine 
[dc04868]1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusb
30 * @{
31 */
32/** @file
33 * Input and output functions (reads and writes) on endpoint pipes.
34 *
35 * Note on synchronousness of the operations: there is ABSOLUTELY NO
36 * guarantee that a call to particular function will not trigger a fibril
37 * switch.
[47c573a]38 *
39 * Note about the implementation: the transfer requests are always divided
40 * into two functions.
41 * The outer one does checking of input parameters (e.g. that session was
42 * already started, buffers are not NULL etc), while the inner one
43 * (with _no_checks suffix) does the actual IPC (it checks for IPC errors,
44 * obviously).
[dc04868]45 */
46#include <usb/usb.h>
47#include <usb/pipes.h>
48#include <errno.h>
49#include <assert.h>
[47c573a]50#include <usbhc_iface.h>
[fa0f53b]51#include <usb/request.h>
[a546687]52#include "pipepriv.h"
[d5ac90f]53
[47c573a]54/** Request an in transfer, no checking of input parameters.
55 *
56 * @param[in] pipe Pipe used for the transfer.
57 * @param[out] buffer Buffer where to store the data.
58 * @param[in] size Size of the buffer (in bytes).
59 * @param[out] size_transfered Number of bytes that were actually transfered.
60 * @return Error code.
61 */
[3954a63b]62static int usb_pipe_read_no_checks(usb_pipe_t *pipe,
[47c573a]63 void *buffer, size_t size, size_t *size_transfered)
64{
65 /*
66 * Get corresponding IPC method.
67 * In future, replace with static array of mappings
68 * transfer type -> method.
69 */
70 usbhc_iface_funcs_t ipc_method;
71 switch (pipe->transfer_type) {
72 case USB_TRANSFER_INTERRUPT:
73 ipc_method = IPC_M_USBHC_INTERRUPT_IN;
74 break;
[0a46c41e]75 case USB_TRANSFER_BULK:
76 ipc_method = IPC_M_USBHC_BULK_IN;
77 break;
[47c573a]78 default:
79 return ENOTSUP;
80 }
81
[d5ac90f]82 /* Ensure serialization over the phone. */
[d48fcc0]83 pipe_start_transaction(pipe);
[d5ac90f]84
[47c573a]85 /*
86 * Make call identifying target USB device and type of transfer.
87 */
[77223f8]88 aid_t opening_request = async_send_3(pipe->hc_phone,
[47c573a]89 DEV_IFACE_ID(USBHC_DEV_IFACE), ipc_method,
90 pipe->wire->address, pipe->endpoint_no,
91 NULL);
92 if (opening_request == 0) {
[d48fcc0]93 pipe_end_transaction(pipe);
[47c573a]94 return ENOMEM;
95 }
[dc04868]96
[47c573a]97 /*
98 * Retrieve the data.
99 */
100 ipc_call_t data_request_call;
101 aid_t data_request = async_data_read(pipe->hc_phone, buffer, size,
102 &data_request_call);
103
[d5ac90f]104 /*
105 * Since now on, someone else might access the backing phone
106 * without breaking the transfer IPC protocol.
107 */
[d48fcc0]108 pipe_end_transaction(pipe);
[d5ac90f]109
[47c573a]110 if (data_request == 0) {
111 /*
112 * FIXME:
113 * How to let the other side know that we want to abort?
114 */
115 async_wait_for(opening_request, NULL);
116 return ENOMEM;
[dc04868]117 }
118
[47c573a]119 /*
120 * Wait for the answer.
121 */
122 sysarg_t data_request_rc;
123 sysarg_t opening_request_rc;
124 async_wait_for(data_request, &data_request_rc);
125 async_wait_for(opening_request, &opening_request_rc);
126
127 if (data_request_rc != EOK) {
[d473c6b]128 /* Prefer the return code of the opening request. */
129 if (opening_request_rc != EOK) {
130 return (int) opening_request_rc;
131 } else {
132 return (int) data_request_rc;
133 }
[47c573a]134 }
135 if (opening_request_rc != EOK) {
136 return (int) opening_request_rc;
137 }
138
139 *size_transfered = IPC_GET_ARG2(data_request_call);
140
141 return EOK;
142}
143
[dc04868]144
145/** Request a read (in) transfer on an endpoint pipe.
146 *
147 * @param[in] pipe Pipe used for the transfer.
148 * @param[out] buffer Buffer where to store the data.
149 * @param[in] size Size of the buffer (in bytes).
150 * @param[out] size_transfered Number of bytes that were actually transfered.
151 * @return Error code.
152 */
[3954a63b]153int usb_pipe_read(usb_pipe_t *pipe,
[dc04868]154 void *buffer, size_t size, size_t *size_transfered)
155{
156 assert(pipe);
157
[47c573a]158 if (buffer == NULL) {
[a546687]159 return EINVAL;
[47c573a]160 }
161
162 if (size == 0) {
163 return EINVAL;
164 }
165
[dc04868]166 if (pipe->direction != USB_DIRECTION_IN) {
167 return EBADF;
168 }
169
[47c573a]170 if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
171 return EBADF;
172 }
173
[dc04868]174 int rc;
[a546687]175 rc = pipe_add_ref(pipe);
176 if (rc != EOK) {
177 return rc;
178 }
179
180
181 size_t act_size = 0;
[dc04868]182
[3954a63b]183 rc = usb_pipe_read_no_checks(pipe, buffer, size, &act_size);
[a546687]184
185 pipe_drop_ref(pipe);
186
[47c573a]187 if (rc != EOK) {
188 return rc;
189 }
190
191 if (size_transfered != NULL) {
192 *size_transfered = act_size;
193 }
194
195 return EOK;
196}
197
198
199
200
201/** Request an out transfer, no checking of input parameters.
202 *
203 * @param[in] pipe Pipe used for the transfer.
204 * @param[in] buffer Buffer with data to transfer.
205 * @param[in] size Size of the buffer (in bytes).
206 * @return Error code.
207 */
[3954a63b]208static int usb_pipe_write_no_check(usb_pipe_t *pipe,
[47c573a]209 void *buffer, size_t size)
210{
211 /*
212 * Get corresponding IPC method.
213 * In future, replace with static array of mappings
214 * transfer type -> method.
215 */
216 usbhc_iface_funcs_t ipc_method;
[dc04868]217 switch (pipe->transfer_type) {
218 case USB_TRANSFER_INTERRUPT:
[47c573a]219 ipc_method = IPC_M_USBHC_INTERRUPT_OUT;
[dc04868]220 break;
[0a46c41e]221 case USB_TRANSFER_BULK:
222 ipc_method = IPC_M_USBHC_BULK_OUT;
223 break;
[dc04868]224 default:
[47c573a]225 return ENOTSUP;
[dc04868]226 }
227
[d5ac90f]228 /* Ensure serialization over the phone. */
[d48fcc0]229 pipe_start_transaction(pipe);
[d5ac90f]230
[47c573a]231 /*
232 * Make call identifying target USB device and type of transfer.
233 */
[77223f8]234 aid_t opening_request = async_send_3(pipe->hc_phone,
[47c573a]235 DEV_IFACE_ID(USBHC_DEV_IFACE), ipc_method,
236 pipe->wire->address, pipe->endpoint_no,
237 NULL);
238 if (opening_request == 0) {
[d48fcc0]239 pipe_end_transaction(pipe);
[47c573a]240 return ENOMEM;
241 }
242
243 /*
244 * Send the data.
245 */
246 int rc = async_data_write_start(pipe->hc_phone, buffer, size);
[d5ac90f]247
248 /*
249 * Since now on, someone else might access the backing phone
250 * without breaking the transfer IPC protocol.
251 */
[d48fcc0]252 pipe_end_transaction(pipe);
[d5ac90f]253
[567d002]254 if (rc != EOK) {
[47c573a]255 async_wait_for(opening_request, NULL);
[567d002]256 return rc;
257 }
258
[47c573a]259 /*
260 * Wait for the answer.
261 */
262 sysarg_t opening_request_rc;
263 async_wait_for(opening_request, &opening_request_rc);
[567d002]264
[47c573a]265 return (int) opening_request_rc;
[dc04868]266}
267
[567d002]268/** Request a write (out) transfer on an endpoint pipe.
[dc04868]269 *
270 * @param[in] pipe Pipe used for the transfer.
271 * @param[in] buffer Buffer with data to transfer.
272 * @param[in] size Size of the buffer (in bytes).
273 * @return Error code.
274 */
[3954a63b]275int usb_pipe_write(usb_pipe_t *pipe,
[567d002]276 void *buffer, size_t size)
[dc04868]277{
278 assert(pipe);
279
[47c573a]280 if (buffer == NULL) {
281 return EINVAL;
282 }
283
284 if (size == 0) {
285 return EINVAL;
286 }
287
[dc04868]288 if (pipe->direction != USB_DIRECTION_OUT) {
289 return EBADF;
290 }
291
[47c573a]292 if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
293 return EBADF;
294 }
[dc04868]295
[a546687]296 int rc;
297
298 rc = pipe_add_ref(pipe);
299 if (rc != EOK) {
300 return rc;
301 }
302
303 rc = usb_pipe_write_no_check(pipe, buffer, size);
304
305 pipe_drop_ref(pipe);
[47c573a]306
307 return rc;
308}
309
[fa0f53b]310/** Try to clear endpoint halt of default control pipe.
311 *
312 * @param pipe Pipe for control endpoint zero.
313 */
314static void clear_self_endpoint_halt(usb_pipe_t *pipe)
315{
316 assert(pipe != NULL);
317
318 if (!pipe->auto_reset_halt || (pipe->endpoint_no != 0)) {
319 return;
320 }
321
322
323 /* Prevent indefinite recursion. */
324 pipe->auto_reset_halt = false;
325 usb_request_clear_endpoint_halt(pipe, 0);
326 pipe->auto_reset_halt = true;
327}
328
[47c573a]329
330/** Request a control read transfer, no checking of input parameters.
331 *
332 * @param[in] pipe Pipe used for the transfer.
333 * @param[in] setup_buffer Buffer with the setup packet.
334 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
335 * @param[out] data_buffer Buffer for incoming data.
336 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
337 * @param[out] data_transfered_size Number of bytes that were actually
338 * transfered during the DATA stage.
339 * @return Error code.
340 */
[3954a63b]341static int usb_pipe_control_read_no_check(usb_pipe_t *pipe,
[47c573a]342 void *setup_buffer, size_t setup_buffer_size,
343 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
344{
[d5ac90f]345 /* Ensure serialization over the phone. */
[d48fcc0]346 pipe_start_transaction(pipe);
[d5ac90f]347
[47c573a]348 /*
349 * Make call identifying target USB device and control transfer type.
350 */
[77223f8]351 aid_t opening_request = async_send_3(pipe->hc_phone,
[47c573a]352 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USBHC_CONTROL_READ,
353 pipe->wire->address, pipe->endpoint_no,
354 NULL);
355 if (opening_request == 0) {
356 return ENOMEM;
[dc04868]357 }
358
[47c573a]359 /*
360 * Send the setup packet.
361 */
362 int rc = async_data_write_start(pipe->hc_phone,
363 setup_buffer, setup_buffer_size);
[567d002]364 if (rc != EOK) {
[d48fcc0]365 pipe_end_transaction(pipe);
[47c573a]366 async_wait_for(opening_request, NULL);
[567d002]367 return rc;
368 }
369
[47c573a]370 /*
371 * Retrieve the data.
372 */
373 ipc_call_t data_request_call;
374 aid_t data_request = async_data_read(pipe->hc_phone,
375 data_buffer, data_buffer_size,
376 &data_request_call);
[d5ac90f]377
378 /*
379 * Since now on, someone else might access the backing phone
380 * without breaking the transfer IPC protocol.
381 */
[d48fcc0]382 pipe_end_transaction(pipe);
[d5ac90f]383
384
[47c573a]385 if (data_request == 0) {
386 async_wait_for(opening_request, NULL);
387 return ENOMEM;
388 }
[567d002]389
[47c573a]390 /*
391 * Wait for the answer.
392 */
393 sysarg_t data_request_rc;
394 sysarg_t opening_request_rc;
395 async_wait_for(data_request, &data_request_rc);
396 async_wait_for(opening_request, &opening_request_rc);
397
398 if (data_request_rc != EOK) {
[d473c6b]399 /* Prefer the return code of the opening request. */
400 if (opening_request_rc != EOK) {
401 return (int) opening_request_rc;
402 } else {
403 return (int) data_request_rc;
404 }
[47c573a]405 }
406 if (opening_request_rc != EOK) {
407 return (int) opening_request_rc;
408 }
[dc04868]409
[47c573a]410 *data_transfered_size = IPC_GET_ARG2(data_request_call);
411
412 return EOK;
413}
[dc04868]414
[567d002]415/** Request a control read transfer on an endpoint pipe.
[dc04868]416 *
417 * This function encapsulates all three stages of a control transfer.
418 *
419 * @param[in] pipe Pipe used for the transfer.
420 * @param[in] setup_buffer Buffer with the setup packet.
421 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
422 * @param[out] data_buffer Buffer for incoming data.
423 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
424 * @param[out] data_transfered_size Number of bytes that were actually
425 * transfered during the DATA stage.
426 * @return Error code.
427 */
[3954a63b]428int usb_pipe_control_read(usb_pipe_t *pipe,
[dc04868]429 void *setup_buffer, size_t setup_buffer_size,
[567d002]430 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
[dc04868]431{
432 assert(pipe);
433
[47c573a]434 if ((setup_buffer == NULL) || (setup_buffer_size == 0)) {
435 return EINVAL;
436 }
437
438 if ((data_buffer == NULL) || (data_buffer_size == 0)) {
439 return EINVAL;
440 }
441
[dc04868]442 if ((pipe->direction != USB_DIRECTION_BOTH)
443 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
444 return EBADF;
445 }
446
[a546687]447 int rc;
448
449 rc = pipe_add_ref(pipe);
450 if (rc != EOK) {
451 return rc;
452 }
453
[47c573a]454 size_t act_size = 0;
[a546687]455 rc = usb_pipe_control_read_no_check(pipe,
[dc04868]456 setup_buffer, setup_buffer_size,
[47c573a]457 data_buffer, data_buffer_size, &act_size);
[567d002]458
[fa0f53b]459 if (rc == ESTALL) {
460 clear_self_endpoint_halt(pipe);
461 }
462
[a546687]463 pipe_drop_ref(pipe);
464
[567d002]465 if (rc != EOK) {
466 return rc;
467 }
468
[47c573a]469 if (data_transfered_size != NULL) {
470 *data_transfered_size = act_size;
471 }
[dc04868]472
[47c573a]473 return EOK;
[dc04868]474}
475
476
[47c573a]477/** Request a control write transfer, no checking of input parameters.
478 *
479 * @param[in] pipe Pipe used for the transfer.
480 * @param[in] setup_buffer Buffer with the setup packet.
481 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
482 * @param[in] data_buffer Buffer with data to be sent.
483 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
484 * @return Error code.
485 */
[3954a63b]486static int usb_pipe_control_write_no_check(usb_pipe_t *pipe,
[47c573a]487 void *setup_buffer, size_t setup_buffer_size,
488 void *data_buffer, size_t data_buffer_size)
489{
[d5ac90f]490 /* Ensure serialization over the phone. */
[d48fcc0]491 pipe_start_transaction(pipe);
[d5ac90f]492
[47c573a]493 /*
494 * Make call identifying target USB device and control transfer type.
495 */
[77223f8]496 aid_t opening_request = async_send_4(pipe->hc_phone,
[47c573a]497 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USBHC_CONTROL_WRITE,
498 pipe->wire->address, pipe->endpoint_no,
499 data_buffer_size,
500 NULL);
501 if (opening_request == 0) {
[d48fcc0]502 pipe_end_transaction(pipe);
[47c573a]503 return ENOMEM;
504 }
505
506 /*
507 * Send the setup packet.
508 */
509 int rc = async_data_write_start(pipe->hc_phone,
510 setup_buffer, setup_buffer_size);
511 if (rc != EOK) {
[d48fcc0]512 pipe_end_transaction(pipe);
[47c573a]513 async_wait_for(opening_request, NULL);
514 return rc;
515 }
516
517 /*
518 * Send the data (if any).
519 */
520 if (data_buffer_size > 0) {
521 rc = async_data_write_start(pipe->hc_phone,
522 data_buffer, data_buffer_size);
[d5ac90f]523
524 /* All data sent, pipe can be released. */
[d48fcc0]525 pipe_end_transaction(pipe);
[d5ac90f]526
[47c573a]527 if (rc != EOK) {
528 async_wait_for(opening_request, NULL);
529 return rc;
530 }
[d5ac90f]531 } else {
532 /* No data to send, we can release the pipe for others. */
[d48fcc0]533 pipe_end_transaction(pipe);
[47c573a]534 }
535
536 /*
537 * Wait for the answer.
538 */
539 sysarg_t opening_request_rc;
540 async_wait_for(opening_request, &opening_request_rc);
541
542 return (int) opening_request_rc;
543}
544
[567d002]545/** Request a control write transfer on an endpoint pipe.
[dc04868]546 *
547 * This function encapsulates all three stages of a control transfer.
548 *
549 * @param[in] pipe Pipe used for the transfer.
550 * @param[in] setup_buffer Buffer with the setup packet.
551 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
552 * @param[in] data_buffer Buffer with data to be sent.
553 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
554 * @return Error code.
555 */
[3954a63b]556int usb_pipe_control_write(usb_pipe_t *pipe,
[dc04868]557 void *setup_buffer, size_t setup_buffer_size,
[567d002]558 void *data_buffer, size_t data_buffer_size)
[dc04868]559{
560 assert(pipe);
561
[47c573a]562 if ((setup_buffer == NULL) || (setup_buffer_size == 0)) {
563 return EINVAL;
564 }
565
566 if ((data_buffer == NULL) && (data_buffer_size > 0)) {
567 return EINVAL;
568 }
569
570 if ((data_buffer != NULL) && (data_buffer_size == 0)) {
571 return EINVAL;
572 }
573
[dc04868]574 if ((pipe->direction != USB_DIRECTION_BOTH)
575 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
576 return EBADF;
577 }
578
[a546687]579 int rc;
580
581 rc = pipe_add_ref(pipe);
582 if (rc != EOK) {
583 return rc;
584 }
585
586 rc = usb_pipe_control_write_no_check(pipe,
[47c573a]587 setup_buffer, setup_buffer_size, data_buffer, data_buffer_size);
[567d002]588
[fa0f53b]589 if (rc == ESTALL) {
590 clear_self_endpoint_halt(pipe);
591 }
592
[a546687]593 pipe_drop_ref(pipe);
594
[567d002]595 return rc;
[dc04868]596}
597
598
599/**
600 * @}
601 */
Note: See TracBrowser for help on using the repository browser.