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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 56e9fb0 was 7eb32a8, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

libusbdev: Use genric read/write iface for interrupt and bulk transfers.

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