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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d3ce33fa 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
Line 
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 libusbdev
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.
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).
45 */
46
47#include <usb/usb.h>
48#include <usb/dev/pipes.h>
49#include <errno.h>
50#include <assert.h>
51#include <usbhc_iface.h>
52#include <usb/dev/request.h>
53#include <async.h>
54#include "pipepriv.h"
55
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 */
64static int usb_pipe_read_no_checks(usb_pipe_t *pipe,
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;
77 case USB_TRANSFER_BULK:
78 ipc_method = IPC_M_USBHC_BULK_IN;
79 break;
80 default:
81 return ENOTSUP;
82 }
83
84 /* Ensure serialization over the phone. */
85 pipe_start_transaction(pipe);
86 async_exch_t *exch = async_exchange_begin(pipe->hc_sess);
87
88 /*
89 * Make call identifying target USB device and type of transfer.
90 */
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
94 if (opening_request == 0) {
95 async_exchange_end(exch);
96 pipe_end_transaction(pipe);
97 return ENOMEM;
98 }
99
100 /*
101 * Retrieve the data.
102 */
103 ipc_call_t data_request_call;
104 aid_t data_request = async_data_read(exch, buffer, size,
105 &data_request_call);
106
107 /*
108 * Since now on, someone else might access the backing phone
109 * without breaking the transfer IPC protocol.
110 */
111 async_exchange_end(exch);
112 pipe_end_transaction(pipe);
113
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;
121 }
122
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);
130
131 if (data_request_rc != EOK) {
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 }
138 }
139 if (opening_request_rc != EOK) {
140 return (int) opening_request_rc;
141 }
142
143 *size_transfered = IPC_GET_ARG2(data_request_call);
144
145 return EOK;
146}
147
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 */
157int usb_pipe_read(usb_pipe_t *pipe,
158 void *buffer, size_t size, size_t *size_transfered)
159{
160 assert(pipe);
161
162 if (buffer == NULL) {
163 return EINVAL;
164 }
165
166 if (size == 0) {
167 return EINVAL;
168 }
169
170 if (pipe->direction != USB_DIRECTION_IN) {
171 return EBADF;
172 }
173
174 if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
175 return EBADF;
176 }
177
178 int rc;
179 rc = pipe_add_ref(pipe, false);
180 if (rc != EOK) {
181 return rc;
182 }
183
184
185 size_t act_size = 0;
186
187 rc = usb_pipe_read_no_checks(pipe, buffer, size, &act_size);
188
189 pipe_drop_ref(pipe);
190
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 */
212static int usb_pipe_write_no_check(usb_pipe_t *pipe,
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;
221 switch (pipe->transfer_type) {
222 case USB_TRANSFER_INTERRUPT:
223 ipc_method = IPC_M_USBHC_INTERRUPT_OUT;
224 break;
225 case USB_TRANSFER_BULK:
226 ipc_method = IPC_M_USBHC_BULK_OUT;
227 break;
228 default:
229 return ENOTSUP;
230 }
231
232 /* Ensure serialization over the phone. */
233 pipe_start_transaction(pipe);
234 async_exch_t *exch = async_exchange_begin(pipe->hc_sess);
235
236 /*
237 * Make call identifying target USB device and type of transfer.
238 */
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
242 if (opening_request == 0) {
243 async_exchange_end(exch);
244 pipe_end_transaction(pipe);
245 return ENOMEM;
246 }
247
248 /*
249 * Send the data.
250 */
251 int rc = async_data_write_start(exch, buffer, size);
252
253 /*
254 * Since now on, someone else might access the backing phone
255 * without breaking the transfer IPC protocol.
256 */
257 async_exchange_end(exch);
258 pipe_end_transaction(pipe);
259
260 if (rc != EOK) {
261 async_wait_for(opening_request, NULL);
262 return rc;
263 }
264
265 /*
266 * Wait for the answer.
267 */
268 sysarg_t opening_request_rc;
269 async_wait_for(opening_request, &opening_request_rc);
270
271 return (int) opening_request_rc;
272}
273
274/** Request a write (out) transfer on an endpoint pipe.
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 */
281int usb_pipe_write(usb_pipe_t *pipe,
282 void *buffer, size_t size)
283{
284 assert(pipe);
285
286 if (buffer == NULL) {
287 return EINVAL;
288 }
289
290 if (size == 0) {
291 return EINVAL;
292 }
293
294 if (pipe->direction != USB_DIRECTION_OUT) {
295 return EBADF;
296 }
297
298 if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
299 return EBADF;
300 }
301
302 int rc;
303
304 rc = pipe_add_ref(pipe, false);
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);
312
313 return rc;
314}
315
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
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 */
347static int usb_pipe_control_read_no_check(usb_pipe_t *pipe,
348 void *setup_buffer, size_t setup_buffer_size,
349 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
350{
351 /* Ensure serialization over the phone. */
352 pipe_start_transaction(pipe);
353
354 /*
355 * Make call identifying target USB device and control transfer type.
356 */
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,
360 NULL);
361
362 if (opening_request == 0) {
363 async_exchange_end(exch);
364 return ENOMEM;
365 }
366
367 /*
368 * Send the setup packet.
369 */
370 int rc = async_data_write_start(exch, setup_buffer, setup_buffer_size);
371 if (rc != EOK) {
372 async_exchange_end(exch);
373 pipe_end_transaction(pipe);
374 async_wait_for(opening_request, NULL);
375 return rc;
376 }
377
378 /*
379 * Retrieve the data.
380 */
381 ipc_call_t data_request_call;
382 aid_t data_request = async_data_read(exch, data_buffer,
383 data_buffer_size, &data_request_call);
384
385 /*
386 * Since now on, someone else might access the backing phone
387 * without breaking the transfer IPC protocol.
388 */
389 async_exchange_end(exch);
390 pipe_end_transaction(pipe);
391
392 if (data_request == 0) {
393 async_wait_for(opening_request, NULL);
394 return ENOMEM;
395 }
396
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) {
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 }
412 }
413 if (opening_request_rc != EOK) {
414 return (int) opening_request_rc;
415 }
416
417 *data_transfered_size = IPC_GET_ARG2(data_request_call);
418
419 return EOK;
420}
421
422/** Request a control read transfer on an endpoint pipe.
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 */
435int usb_pipe_control_read(usb_pipe_t *pipe,
436 void *setup_buffer, size_t setup_buffer_size,
437 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
438{
439 assert(pipe);
440
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
449 if ((pipe->direction != USB_DIRECTION_BOTH)
450 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
451 return EBADF;
452 }
453
454 int rc;
455
456 rc = pipe_add_ref(pipe, false);
457 if (rc != EOK) {
458 return rc;
459 }
460
461 size_t act_size = 0;
462 rc = usb_pipe_control_read_no_check(pipe,
463 setup_buffer, setup_buffer_size,
464 data_buffer, data_buffer_size, &act_size);
465
466 if (rc == ESTALL) {
467 clear_self_endpoint_halt(pipe);
468 }
469
470 pipe_drop_ref(pipe);
471
472 if (rc != EOK) {
473 return rc;
474 }
475
476 if (data_transfered_size != NULL) {
477 *data_transfered_size = act_size;
478 }
479
480 return EOK;
481}
482
483
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 */
493static int usb_pipe_control_write_no_check(usb_pipe_t *pipe,
494 void *setup_buffer, size_t setup_buffer_size,
495 void *data_buffer, size_t data_buffer_size)
496{
497 /* Ensure serialization over the phone. */
498 pipe_start_transaction(pipe);
499
500 /*
501 * Make call identifying target USB device and control transfer type.
502 */
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
508 if (opening_request == 0) {
509 async_exchange_end(exch);
510 pipe_end_transaction(pipe);
511 return ENOMEM;
512 }
513
514 /*
515 * Send the setup packet.
516 */
517 int rc = async_data_write_start(exch, setup_buffer, setup_buffer_size);
518 if (rc != EOK) {
519 async_exchange_end(exch);
520 pipe_end_transaction(pipe);
521 async_wait_for(opening_request, NULL);
522 return rc;
523 }
524
525 /*
526 * Send the data (if any).
527 */
528 if (data_buffer_size > 0) {
529 rc = async_data_write_start(exch, data_buffer, data_buffer_size);
530
531 /* All data sent, pipe can be released. */
532 async_exchange_end(exch);
533 pipe_end_transaction(pipe);
534
535 if (rc != EOK) {
536 async_wait_for(opening_request, NULL);
537 return rc;
538 }
539 } else {
540 /* No data to send, we can release the pipe for others. */
541 async_exchange_end(exch);
542 pipe_end_transaction(pipe);
543 }
544
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
554/** Request a control write transfer on an endpoint pipe.
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 */
565int usb_pipe_control_write(usb_pipe_t *pipe,
566 void *setup_buffer, size_t setup_buffer_size,
567 void *data_buffer, size_t data_buffer_size)
568{
569 assert(pipe);
570
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
583 if ((pipe->direction != USB_DIRECTION_BOTH)
584 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
585 return EBADF;
586 }
587
588 int rc;
589
590 rc = pipe_add_ref(pipe, false);
591 if (rc != EOK) {
592 return rc;
593 }
594
595 rc = usb_pipe_control_write_no_check(pipe,
596 setup_buffer, setup_buffer_size, data_buffer, data_buffer_size);
597
598 if (rc == ESTALL) {
599 clear_self_endpoint_halt(pipe);
600 }
601
602 pipe_drop_ref(pipe);
603
604 return rc;
605}
606
607
608/**
609 * @}
610 */
Note: See TracBrowser for help on using the repository browser.