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

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

lilbusbdev: Make setup_buffer const as there is no reason not to.

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