source: mainline/uspace/lib/usbdev/src/pipesio.c@ 1d1bb0f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1d1bb0f was 79ae36dd, checked in by Martin Decky <martin@…>, 14 years ago

new async framework with integrated exchange tracking

  • strict isolation between low-level IPC and high-level async framework with integrated exchange tracking
    • each IPC connection is represented by an async_sess_t structure
    • each IPC exchange is represented by an async_exch_t structure
    • exchange management is either based on atomic messages (EXCHANGE_ATOMIC), locking (EXCHANGE_SERIALIZE) or connection cloning (EXCHANGE_CLONE)
  • async_obsolete: temporary compatibility layer to keep old async clients working (several pieces of code are currently broken, but only non-essential functionality)
  • IPC_M_PHONE_HANGUP is now method no. 0 (for elegant boolean evaluation)
  • IPC_M_DEBUG_ALL has been renamed to IPC_M_DEBUG
  • IPC_M_PING has been removed (VFS protocol now has VFS_IN_PING)
  • console routines in libc have been rewritten for better abstraction
  • additional use for libc-private header files (FILE structure opaque to the client)
  • various cstyle changes (typos, indentation, missing externs in header files, improved comments, etc.)
  • Property mode set to 100644
File size: 15.1 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
[160b75e]29/** @addtogroup libusbdev
[dc04868]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 */
[79ae36dd]46
[dc04868]47#include <usb/usb.h>
[7d521e24]48#include <usb/dev/pipes.h>
[dc04868]49#include <errno.h>
50#include <assert.h>
[47c573a]51#include <usbhc_iface.h>
[7d521e24]52#include <usb/dev/request.h>
[79ae36dd]53#include <async.h>
[a546687]54#include "pipepriv.h"
[d5ac90f]55
[47c573a]56/** Request an in transfer, no checking of input parameters.
57 *
58 * @param[in] pipe Pipe used for the transfer.
59 * @param[out] buffer Buffer where to store the data.
60 * @param[in] size Size of the buffer (in bytes).
61 * @param[out] size_transfered Number of bytes that were actually transfered.
62 * @return Error code.
63 */
[3954a63b]64static int usb_pipe_read_no_checks(usb_pipe_t *pipe,
[47c573a]65 void *buffer, size_t size, size_t *size_transfered)
66{
67 /*
68 * Get corresponding IPC method.
69 * In future, replace with static array of mappings
70 * transfer type -> method.
71 */
72 usbhc_iface_funcs_t ipc_method;
73 switch (pipe->transfer_type) {
74 case USB_TRANSFER_INTERRUPT:
75 ipc_method = IPC_M_USBHC_INTERRUPT_IN;
76 break;
[0a46c41e]77 case USB_TRANSFER_BULK:
78 ipc_method = IPC_M_USBHC_BULK_IN;
79 break;
[47c573a]80 default:
81 return ENOTSUP;
82 }
[79ae36dd]83
[d5ac90f]84 /* Ensure serialization over the phone. */
[d48fcc0]85 pipe_start_transaction(pipe);
[79ae36dd]86 async_exch_t *exch = async_exchange_begin(pipe->hc_sess);
87
[47c573a]88 /*
89 * Make call identifying target USB device and type of transfer.
90 */
[79ae36dd]91 aid_t opening_request = async_send_3(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
92 ipc_method, pipe->wire->address, pipe->endpoint_no, NULL);
93
[47c573a]94 if (opening_request == 0) {
[79ae36dd]95 async_exchange_end(exch);
[d48fcc0]96 pipe_end_transaction(pipe);
[47c573a]97 return ENOMEM;
98 }
[79ae36dd]99
[47c573a]100 /*
101 * Retrieve the data.
102 */
103 ipc_call_t data_request_call;
[79ae36dd]104 aid_t data_request = async_data_read(exch, buffer, size,
[47c573a]105 &data_request_call);
[79ae36dd]106
[d5ac90f]107 /*
108 * Since now on, someone else might access the backing phone
109 * without breaking the transfer IPC protocol.
110 */
[79ae36dd]111 async_exchange_end(exch);
[d48fcc0]112 pipe_end_transaction(pipe);
[79ae36dd]113
[47c573a]114 if (data_request == 0) {
115 /*
116 * FIXME:
117 * How to let the other side know that we want to abort?
118 */
119 async_wait_for(opening_request, NULL);
120 return ENOMEM;
[dc04868]121 }
[79ae36dd]122
[47c573a]123 /*
124 * Wait for the answer.
125 */
126 sysarg_t data_request_rc;
127 sysarg_t opening_request_rc;
128 async_wait_for(data_request, &data_request_rc);
129 async_wait_for(opening_request, &opening_request_rc);
[79ae36dd]130
[47c573a]131 if (data_request_rc != EOK) {
[d473c6b]132 /* Prefer the return code of the opening request. */
133 if (opening_request_rc != EOK) {
134 return (int) opening_request_rc;
135 } else {
136 return (int) data_request_rc;
137 }
[47c573a]138 }
139 if (opening_request_rc != EOK) {
140 return (int) opening_request_rc;
141 }
[79ae36dd]142
[47c573a]143 *size_transfered = IPC_GET_ARG2(data_request_call);
[79ae36dd]144
[47c573a]145 return EOK;
146}
147
[dc04868]148
149/** Request a read (in) transfer on an endpoint pipe.
150 *
151 * @param[in] pipe Pipe used for the transfer.
152 * @param[out] buffer Buffer where to store the data.
153 * @param[in] size Size of the buffer (in bytes).
154 * @param[out] size_transfered Number of bytes that were actually transfered.
155 * @return Error code.
156 */
[3954a63b]157int usb_pipe_read(usb_pipe_t *pipe,
[dc04868]158 void *buffer, size_t size, size_t *size_transfered)
159{
160 assert(pipe);
161
[47c573a]162 if (buffer == NULL) {
[a546687]163 return EINVAL;
[47c573a]164 }
165
166 if (size == 0) {
167 return EINVAL;
168 }
169
[dc04868]170 if (pipe->direction != USB_DIRECTION_IN) {
171 return EBADF;
172 }
173
[47c573a]174 if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
175 return EBADF;
176 }
177
[dc04868]178 int rc;
[2c2cbcf]179 rc = pipe_add_ref(pipe, false);
[a546687]180 if (rc != EOK) {
181 return rc;
182 }
183
184
185 size_t act_size = 0;
[dc04868]186
[3954a63b]187 rc = usb_pipe_read_no_checks(pipe, buffer, size, &act_size);
[a546687]188
189 pipe_drop_ref(pipe);
190
[47c573a]191 if (rc != EOK) {
192 return rc;
193 }
194
195 if (size_transfered != NULL) {
196 *size_transfered = act_size;
197 }
198
199 return EOK;
200}
201
202
203
204
205/** Request an out transfer, no checking of input parameters.
206 *
207 * @param[in] pipe Pipe used for the transfer.
208 * @param[in] buffer Buffer with data to transfer.
209 * @param[in] size Size of the buffer (in bytes).
210 * @return Error code.
211 */
[3954a63b]212static int usb_pipe_write_no_check(usb_pipe_t *pipe,
[47c573a]213 void *buffer, size_t size)
214{
215 /*
216 * Get corresponding IPC method.
217 * In future, replace with static array of mappings
218 * transfer type -> method.
219 */
220 usbhc_iface_funcs_t ipc_method;
[dc04868]221 switch (pipe->transfer_type) {
222 case USB_TRANSFER_INTERRUPT:
[47c573a]223 ipc_method = IPC_M_USBHC_INTERRUPT_OUT;
[dc04868]224 break;
[0a46c41e]225 case USB_TRANSFER_BULK:
226 ipc_method = IPC_M_USBHC_BULK_OUT;
227 break;
[dc04868]228 default:
[47c573a]229 return ENOTSUP;
[dc04868]230 }
231
[d5ac90f]232 /* Ensure serialization over the phone. */
[d48fcc0]233 pipe_start_transaction(pipe);
[79ae36dd]234 async_exch_t *exch = async_exchange_begin(pipe->hc_sess);
235
[47c573a]236 /*
237 * Make call identifying target USB device and type of transfer.
238 */
[79ae36dd]239 aid_t opening_request = async_send_3(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
240 ipc_method, pipe->wire->address, pipe->endpoint_no, NULL);
241
[47c573a]242 if (opening_request == 0) {
[79ae36dd]243 async_exchange_end(exch);
[d48fcc0]244 pipe_end_transaction(pipe);
[47c573a]245 return ENOMEM;
246 }
[79ae36dd]247
[47c573a]248 /*
249 * Send the data.
250 */
[79ae36dd]251 int rc = async_data_write_start(exch, buffer, size);
252
[d5ac90f]253 /*
254 * Since now on, someone else might access the backing phone
255 * without breaking the transfer IPC protocol.
256 */
[79ae36dd]257 async_exchange_end(exch);
[d48fcc0]258 pipe_end_transaction(pipe);
[79ae36dd]259
[567d002]260 if (rc != EOK) {
[47c573a]261 async_wait_for(opening_request, NULL);
[567d002]262 return rc;
263 }
[79ae36dd]264
[47c573a]265 /*
266 * Wait for the answer.
267 */
268 sysarg_t opening_request_rc;
269 async_wait_for(opening_request, &opening_request_rc);
[79ae36dd]270
[47c573a]271 return (int) opening_request_rc;
[dc04868]272}
273
[567d002]274/** Request a write (out) transfer on an endpoint pipe.
[dc04868]275 *
276 * @param[in] pipe Pipe used for the transfer.
277 * @param[in] buffer Buffer with data to transfer.
278 * @param[in] size Size of the buffer (in bytes).
279 * @return Error code.
280 */
[3954a63b]281int usb_pipe_write(usb_pipe_t *pipe,
[567d002]282 void *buffer, size_t size)
[dc04868]283{
284 assert(pipe);
285
[47c573a]286 if (buffer == NULL) {
287 return EINVAL;
288 }
289
290 if (size == 0) {
291 return EINVAL;
292 }
293
[dc04868]294 if (pipe->direction != USB_DIRECTION_OUT) {
295 return EBADF;
296 }
297
[47c573a]298 if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
299 return EBADF;
300 }
[dc04868]301
[a546687]302 int rc;
303
[2c2cbcf]304 rc = pipe_add_ref(pipe, false);
[a546687]305 if (rc != EOK) {
306 return rc;
307 }
308
309 rc = usb_pipe_write_no_check(pipe, buffer, size);
310
311 pipe_drop_ref(pipe);
[47c573a]312
313 return rc;
314}
315
[fa0f53b]316/** Try to clear endpoint halt of default control pipe.
317 *
318 * @param pipe Pipe for control endpoint zero.
319 */
320static void clear_self_endpoint_halt(usb_pipe_t *pipe)
321{
322 assert(pipe != NULL);
323
324 if (!pipe->auto_reset_halt || (pipe->endpoint_no != 0)) {
325 return;
326 }
327
328
329 /* Prevent indefinite recursion. */
330 pipe->auto_reset_halt = false;
331 usb_request_clear_endpoint_halt(pipe, 0);
332 pipe->auto_reset_halt = true;
333}
334
[47c573a]335
336/** Request a control read transfer, no checking of input parameters.
337 *
338 * @param[in] pipe Pipe used for the transfer.
339 * @param[in] setup_buffer Buffer with the setup packet.
340 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
341 * @param[out] data_buffer Buffer for incoming data.
342 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
343 * @param[out] data_transfered_size Number of bytes that were actually
344 * transfered during the DATA stage.
345 * @return Error code.
346 */
[3954a63b]347static int usb_pipe_control_read_no_check(usb_pipe_t *pipe,
[47c573a]348 void *setup_buffer, size_t setup_buffer_size,
349 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
350{
[d5ac90f]351 /* Ensure serialization over the phone. */
[d48fcc0]352 pipe_start_transaction(pipe);
[d5ac90f]353
[47c573a]354 /*
355 * Make call identifying target USB device and control transfer type.
356 */
[79ae36dd]357 async_exch_t *exch = async_exchange_begin(pipe->hc_sess);
358 aid_t opening_request = async_send_3(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
359 IPC_M_USBHC_CONTROL_READ, pipe->wire->address, pipe->endpoint_no,
[47c573a]360 NULL);
[79ae36dd]361
[47c573a]362 if (opening_request == 0) {
[79ae36dd]363 async_exchange_end(exch);
[47c573a]364 return ENOMEM;
[dc04868]365 }
[79ae36dd]366
[47c573a]367 /*
368 * Send the setup packet.
369 */
[79ae36dd]370 int rc = async_data_write_start(exch, setup_buffer, setup_buffer_size);
[567d002]371 if (rc != EOK) {
[79ae36dd]372 async_exchange_end(exch);
[d48fcc0]373 pipe_end_transaction(pipe);
[47c573a]374 async_wait_for(opening_request, NULL);
[567d002]375 return rc;
376 }
[79ae36dd]377
[47c573a]378 /*
379 * Retrieve the data.
380 */
381 ipc_call_t data_request_call;
[79ae36dd]382 aid_t data_request = async_data_read(exch, data_buffer,
383 data_buffer_size, &data_request_call);
384
[d5ac90f]385 /*
386 * Since now on, someone else might access the backing phone
387 * without breaking the transfer IPC protocol.
388 */
[79ae36dd]389 async_exchange_end(exch);
[d48fcc0]390 pipe_end_transaction(pipe);
[79ae36dd]391
[47c573a]392 if (data_request == 0) {
393 async_wait_for(opening_request, NULL);
394 return ENOMEM;
395 }
[567d002]396
[47c573a]397 /*
398 * Wait for the answer.
399 */
400 sysarg_t data_request_rc;
401 sysarg_t opening_request_rc;
402 async_wait_for(data_request, &data_request_rc);
403 async_wait_for(opening_request, &opening_request_rc);
404
405 if (data_request_rc != EOK) {
[d473c6b]406 /* Prefer the return code of the opening request. */
407 if (opening_request_rc != EOK) {
408 return (int) opening_request_rc;
409 } else {
410 return (int) data_request_rc;
411 }
[47c573a]412 }
413 if (opening_request_rc != EOK) {
414 return (int) opening_request_rc;
415 }
[dc04868]416
[47c573a]417 *data_transfered_size = IPC_GET_ARG2(data_request_call);
418
419 return EOK;
420}
[dc04868]421
[567d002]422/** Request a control read transfer on an endpoint pipe.
[dc04868]423 *
424 * This function encapsulates all three stages of a control transfer.
425 *
426 * @param[in] pipe Pipe used for the transfer.
427 * @param[in] setup_buffer Buffer with the setup packet.
428 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
429 * @param[out] data_buffer Buffer for incoming data.
430 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
431 * @param[out] data_transfered_size Number of bytes that were actually
432 * transfered during the DATA stage.
433 * @return Error code.
434 */
[3954a63b]435int usb_pipe_control_read(usb_pipe_t *pipe,
[dc04868]436 void *setup_buffer, size_t setup_buffer_size,
[567d002]437 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
[dc04868]438{
439 assert(pipe);
440
[47c573a]441 if ((setup_buffer == NULL) || (setup_buffer_size == 0)) {
442 return EINVAL;
443 }
444
445 if ((data_buffer == NULL) || (data_buffer_size == 0)) {
446 return EINVAL;
447 }
448
[dc04868]449 if ((pipe->direction != USB_DIRECTION_BOTH)
450 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
451 return EBADF;
452 }
453
[a546687]454 int rc;
455
[2c2cbcf]456 rc = pipe_add_ref(pipe, false);
[a546687]457 if (rc != EOK) {
458 return rc;
459 }
460
[47c573a]461 size_t act_size = 0;
[a546687]462 rc = usb_pipe_control_read_no_check(pipe,
[dc04868]463 setup_buffer, setup_buffer_size,
[47c573a]464 data_buffer, data_buffer_size, &act_size);
[567d002]465
[fa0f53b]466 if (rc == ESTALL) {
467 clear_self_endpoint_halt(pipe);
468 }
469
[a546687]470 pipe_drop_ref(pipe);
471
[567d002]472 if (rc != EOK) {
473 return rc;
474 }
475
[47c573a]476 if (data_transfered_size != NULL) {
477 *data_transfered_size = act_size;
478 }
[dc04868]479
[47c573a]480 return EOK;
[dc04868]481}
482
483
[47c573a]484/** Request a control write transfer, no checking of input parameters.
485 *
486 * @param[in] pipe Pipe used for the transfer.
487 * @param[in] setup_buffer Buffer with the setup packet.
488 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
489 * @param[in] data_buffer Buffer with data to be sent.
490 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
491 * @return Error code.
492 */
[3954a63b]493static int usb_pipe_control_write_no_check(usb_pipe_t *pipe,
[47c573a]494 void *setup_buffer, size_t setup_buffer_size,
495 void *data_buffer, size_t data_buffer_size)
496{
[d5ac90f]497 /* Ensure serialization over the phone. */
[d48fcc0]498 pipe_start_transaction(pipe);
[d5ac90f]499
[47c573a]500 /*
501 * Make call identifying target USB device and control transfer type.
502 */
[79ae36dd]503 async_exch_t *exch = async_exchange_begin(pipe->hc_sess);
504 aid_t opening_request = async_send_4(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
505 IPC_M_USBHC_CONTROL_WRITE, pipe->wire->address, pipe->endpoint_no,
506 data_buffer_size, NULL);
507
[47c573a]508 if (opening_request == 0) {
[79ae36dd]509 async_exchange_end(exch);
[d48fcc0]510 pipe_end_transaction(pipe);
[47c573a]511 return ENOMEM;
512 }
[79ae36dd]513
[47c573a]514 /*
515 * Send the setup packet.
516 */
[79ae36dd]517 int rc = async_data_write_start(exch, setup_buffer, setup_buffer_size);
[47c573a]518 if (rc != EOK) {
[79ae36dd]519 async_exchange_end(exch);
[d48fcc0]520 pipe_end_transaction(pipe);
[47c573a]521 async_wait_for(opening_request, NULL);
522 return rc;
523 }
[79ae36dd]524
[47c573a]525 /*
526 * Send the data (if any).
527 */
528 if (data_buffer_size > 0) {
[79ae36dd]529 rc = async_data_write_start(exch, data_buffer, data_buffer_size);
530
[d5ac90f]531 /* All data sent, pipe can be released. */
[79ae36dd]532 async_exchange_end(exch);
[d48fcc0]533 pipe_end_transaction(pipe);
[79ae36dd]534
[47c573a]535 if (rc != EOK) {
536 async_wait_for(opening_request, NULL);
537 return rc;
538 }
[d5ac90f]539 } else {
540 /* No data to send, we can release the pipe for others. */
[79ae36dd]541 async_exchange_end(exch);
[d48fcc0]542 pipe_end_transaction(pipe);
[47c573a]543 }
[79ae36dd]544
[47c573a]545 /*
546 * Wait for the answer.
547 */
548 sysarg_t opening_request_rc;
549 async_wait_for(opening_request, &opening_request_rc);
550
551 return (int) opening_request_rc;
552}
553
[567d002]554/** Request a control write transfer on an endpoint pipe.
[dc04868]555 *
556 * This function encapsulates all three stages of a control transfer.
557 *
558 * @param[in] pipe Pipe used for the transfer.
559 * @param[in] setup_buffer Buffer with the setup packet.
560 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
561 * @param[in] data_buffer Buffer with data to be sent.
562 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
563 * @return Error code.
564 */
[3954a63b]565int usb_pipe_control_write(usb_pipe_t *pipe,
[dc04868]566 void *setup_buffer, size_t setup_buffer_size,
[567d002]567 void *data_buffer, size_t data_buffer_size)
[dc04868]568{
569 assert(pipe);
570
[47c573a]571 if ((setup_buffer == NULL) || (setup_buffer_size == 0)) {
572 return EINVAL;
573 }
574
575 if ((data_buffer == NULL) && (data_buffer_size > 0)) {
576 return EINVAL;
577 }
578
579 if ((data_buffer != NULL) && (data_buffer_size == 0)) {
580 return EINVAL;
581 }
582
[dc04868]583 if ((pipe->direction != USB_DIRECTION_BOTH)
584 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
585 return EBADF;
586 }
587
[a546687]588 int rc;
589
[2c2cbcf]590 rc = pipe_add_ref(pipe, false);
[a546687]591 if (rc != EOK) {
592 return rc;
593 }
594
595 rc = usb_pipe_control_write_no_check(pipe,
[47c573a]596 setup_buffer, setup_buffer_size, data_buffer, data_buffer_size);
[567d002]597
[fa0f53b]598 if (rc == ESTALL) {
599 clear_self_endpoint_halt(pipe);
600 }
601
[a546687]602 pipe_drop_ref(pipe);
603
[567d002]604 return rc;
[dc04868]605}
606
607
608/**
609 * @}
610 */
Note: See TracBrowser for help on using the repository browser.