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

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

Fine grain locking of USB pipes

  • Property mode set to 100644
File size: 14.2 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>
[a546687]51#include "pipepriv.h"
[d5ac90f]52
[47c573a]53/** Request an in transfer, no checking of input parameters.
54 *
55 * @param[in] pipe Pipe used for the transfer.
56 * @param[out] buffer Buffer where to store the data.
57 * @param[in] size Size of the buffer (in bytes).
58 * @param[out] size_transfered Number of bytes that were actually transfered.
59 * @return Error code.
60 */
[3954a63b]61static int usb_pipe_read_no_checks(usb_pipe_t *pipe,
[47c573a]62 void *buffer, size_t size, size_t *size_transfered)
63{
64 /*
65 * Get corresponding IPC method.
66 * In future, replace with static array of mappings
67 * transfer type -> method.
68 */
69 usbhc_iface_funcs_t ipc_method;
70 switch (pipe->transfer_type) {
71 case USB_TRANSFER_INTERRUPT:
72 ipc_method = IPC_M_USBHC_INTERRUPT_IN;
73 break;
[0a46c41e]74 case USB_TRANSFER_BULK:
75 ipc_method = IPC_M_USBHC_BULK_IN;
76 break;
[47c573a]77 default:
78 return ENOTSUP;
79 }
80
[d5ac90f]81 /* Ensure serialization over the phone. */
[d48fcc0]82 pipe_start_transaction(pipe);
[d5ac90f]83
[47c573a]84 /*
85 * Make call identifying target USB device and type of transfer.
86 */
[228f251]87 aid_t opening_request = async_send_4(pipe->hc_phone,
[47c573a]88 DEV_IFACE_ID(USBHC_DEV_IFACE), ipc_method,
89 pipe->wire->address, pipe->endpoint_no,
[228f251]90 pipe->max_packet_size,
[47c573a]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 */
[228f251]234 aid_t opening_request = async_send_4(pipe->hc_phone,
[47c573a]235 DEV_IFACE_ID(USBHC_DEV_IFACE), ipc_method,
236 pipe->wire->address, pipe->endpoint_no,
[228f251]237 pipe->max_packet_size,
[47c573a]238 NULL);
239 if (opening_request == 0) {
[d48fcc0]240 pipe_end_transaction(pipe);
[47c573a]241 return ENOMEM;
242 }
243
244 /*
245 * Send the data.
246 */
247 int rc = async_data_write_start(pipe->hc_phone, buffer, size);
[d5ac90f]248
249 /*
250 * Since now on, someone else might access the backing phone
251 * without breaking the transfer IPC protocol.
252 */
[d48fcc0]253 pipe_end_transaction(pipe);
[d5ac90f]254
[567d002]255 if (rc != EOK) {
[47c573a]256 async_wait_for(opening_request, NULL);
[567d002]257 return rc;
258 }
259
[47c573a]260 /*
261 * Wait for the answer.
262 */
263 sysarg_t opening_request_rc;
264 async_wait_for(opening_request, &opening_request_rc);
[567d002]265
[47c573a]266 return (int) opening_request_rc;
[dc04868]267}
268
[567d002]269/** Request a write (out) transfer on an endpoint pipe.
[dc04868]270 *
271 * @param[in] pipe Pipe used for the transfer.
272 * @param[in] buffer Buffer with data to transfer.
273 * @param[in] size Size of the buffer (in bytes).
274 * @return Error code.
275 */
[3954a63b]276int usb_pipe_write(usb_pipe_t *pipe,
[567d002]277 void *buffer, size_t size)
[dc04868]278{
279 assert(pipe);
280
[47c573a]281 if (buffer == NULL) {
282 return EINVAL;
283 }
284
285 if (size == 0) {
286 return EINVAL;
287 }
288
[dc04868]289 if (pipe->direction != USB_DIRECTION_OUT) {
290 return EBADF;
291 }
292
[47c573a]293 if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
294 return EBADF;
295 }
[dc04868]296
[a546687]297 int rc;
298
299 rc = pipe_add_ref(pipe);
300 if (rc != EOK) {
301 return rc;
302 }
303
304 rc = usb_pipe_write_no_check(pipe, buffer, size);
305
306 pipe_drop_ref(pipe);
[47c573a]307
308 return rc;
309}
310
311
312/** Request a control read transfer, no checking of input parameters.
313 *
314 * @param[in] pipe Pipe used for the transfer.
315 * @param[in] setup_buffer Buffer with the setup packet.
316 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
317 * @param[out] data_buffer Buffer for incoming data.
318 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
319 * @param[out] data_transfered_size Number of bytes that were actually
320 * transfered during the DATA stage.
321 * @return Error code.
322 */
[3954a63b]323static int usb_pipe_control_read_no_check(usb_pipe_t *pipe,
[47c573a]324 void *setup_buffer, size_t setup_buffer_size,
325 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
326{
[d5ac90f]327 /* Ensure serialization over the phone. */
[d48fcc0]328 pipe_start_transaction(pipe);
[d5ac90f]329
[47c573a]330 /*
331 * Make call identifying target USB device and control transfer type.
332 */
[228f251]333 aid_t opening_request = async_send_4(pipe->hc_phone,
[47c573a]334 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USBHC_CONTROL_READ,
335 pipe->wire->address, pipe->endpoint_no,
[228f251]336 pipe->max_packet_size,
[47c573a]337 NULL);
338 if (opening_request == 0) {
339 return ENOMEM;
[dc04868]340 }
341
[47c573a]342 /*
343 * Send the setup packet.
344 */
345 int rc = async_data_write_start(pipe->hc_phone,
346 setup_buffer, setup_buffer_size);
[567d002]347 if (rc != EOK) {
[d48fcc0]348 pipe_end_transaction(pipe);
[47c573a]349 async_wait_for(opening_request, NULL);
[567d002]350 return rc;
351 }
352
[47c573a]353 /*
354 * Retrieve the data.
355 */
356 ipc_call_t data_request_call;
357 aid_t data_request = async_data_read(pipe->hc_phone,
358 data_buffer, data_buffer_size,
359 &data_request_call);
[d5ac90f]360
361 /*
362 * Since now on, someone else might access the backing phone
363 * without breaking the transfer IPC protocol.
364 */
[d48fcc0]365 pipe_end_transaction(pipe);
[d5ac90f]366
367
[47c573a]368 if (data_request == 0) {
369 async_wait_for(opening_request, NULL);
370 return ENOMEM;
371 }
[567d002]372
[47c573a]373 /*
374 * Wait for the answer.
375 */
376 sysarg_t data_request_rc;
377 sysarg_t opening_request_rc;
378 async_wait_for(data_request, &data_request_rc);
379 async_wait_for(opening_request, &opening_request_rc);
380
381 if (data_request_rc != EOK) {
[d473c6b]382 /* Prefer the return code of the opening request. */
383 if (opening_request_rc != EOK) {
384 return (int) opening_request_rc;
385 } else {
386 return (int) data_request_rc;
387 }
[47c573a]388 }
389 if (opening_request_rc != EOK) {
390 return (int) opening_request_rc;
391 }
[dc04868]392
[47c573a]393 *data_transfered_size = IPC_GET_ARG2(data_request_call);
394
395 return EOK;
396}
[dc04868]397
[567d002]398/** Request a control read transfer on an endpoint pipe.
[dc04868]399 *
400 * This function encapsulates all three stages of a control transfer.
401 *
402 * @param[in] pipe Pipe used for the transfer.
403 * @param[in] setup_buffer Buffer with the setup packet.
404 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
405 * @param[out] data_buffer Buffer for incoming data.
406 * @param[in] data_buffer_size Size of the buffer for incoming data (in bytes).
407 * @param[out] data_transfered_size Number of bytes that were actually
408 * transfered during the DATA stage.
409 * @return Error code.
410 */
[3954a63b]411int usb_pipe_control_read(usb_pipe_t *pipe,
[dc04868]412 void *setup_buffer, size_t setup_buffer_size,
[567d002]413 void *data_buffer, size_t data_buffer_size, size_t *data_transfered_size)
[dc04868]414{
415 assert(pipe);
416
[47c573a]417 if ((setup_buffer == NULL) || (setup_buffer_size == 0)) {
418 return EINVAL;
419 }
420
421 if ((data_buffer == NULL) || (data_buffer_size == 0)) {
422 return EINVAL;
423 }
424
[dc04868]425 if ((pipe->direction != USB_DIRECTION_BOTH)
426 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
427 return EBADF;
428 }
429
[a546687]430 int rc;
431
432 rc = pipe_add_ref(pipe);
433 if (rc != EOK) {
434 return rc;
435 }
436
[47c573a]437 size_t act_size = 0;
[a546687]438 rc = usb_pipe_control_read_no_check(pipe,
[dc04868]439 setup_buffer, setup_buffer_size,
[47c573a]440 data_buffer, data_buffer_size, &act_size);
[567d002]441
[a546687]442 pipe_drop_ref(pipe);
443
[567d002]444 if (rc != EOK) {
445 return rc;
446 }
447
[47c573a]448 if (data_transfered_size != NULL) {
449 *data_transfered_size = act_size;
450 }
[dc04868]451
[47c573a]452 return EOK;
[dc04868]453}
454
455
[47c573a]456/** Request a control write transfer, no checking of input parameters.
457 *
458 * @param[in] pipe Pipe used for the transfer.
459 * @param[in] setup_buffer Buffer with the setup packet.
460 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
461 * @param[in] data_buffer Buffer with data to be sent.
462 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
463 * @return Error code.
464 */
[3954a63b]465static int usb_pipe_control_write_no_check(usb_pipe_t *pipe,
[47c573a]466 void *setup_buffer, size_t setup_buffer_size,
467 void *data_buffer, size_t data_buffer_size)
468{
[d5ac90f]469 /* Ensure serialization over the phone. */
[d48fcc0]470 pipe_start_transaction(pipe);
[d5ac90f]471
[47c573a]472 /*
473 * Make call identifying target USB device and control transfer type.
474 */
[228f251]475 aid_t opening_request = async_send_5(pipe->hc_phone,
[47c573a]476 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USBHC_CONTROL_WRITE,
477 pipe->wire->address, pipe->endpoint_no,
478 data_buffer_size,
[228f251]479 pipe->max_packet_size,
[47c573a]480 NULL);
481 if (opening_request == 0) {
[d48fcc0]482 pipe_end_transaction(pipe);
[47c573a]483 return ENOMEM;
484 }
485
486 /*
487 * Send the setup packet.
488 */
489 int rc = async_data_write_start(pipe->hc_phone,
490 setup_buffer, setup_buffer_size);
491 if (rc != EOK) {
[d48fcc0]492 pipe_end_transaction(pipe);
[47c573a]493 async_wait_for(opening_request, NULL);
494 return rc;
495 }
496
497 /*
498 * Send the data (if any).
499 */
500 if (data_buffer_size > 0) {
501 rc = async_data_write_start(pipe->hc_phone,
502 data_buffer, data_buffer_size);
[d5ac90f]503
504 /* All data sent, pipe can be released. */
[d48fcc0]505 pipe_end_transaction(pipe);
[d5ac90f]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. */
[d48fcc0]513 pipe_end_transaction(pipe);
[47c573a]514 }
515
516 /*
517 * Wait for the answer.
518 */
519 sysarg_t opening_request_rc;
520 async_wait_for(opening_request, &opening_request_rc);
521
522 return (int) opening_request_rc;
523}
524
[567d002]525/** Request a control write transfer on an endpoint pipe.
[dc04868]526 *
527 * This function encapsulates all three stages of a control transfer.
528 *
529 * @param[in] pipe Pipe used for the transfer.
530 * @param[in] setup_buffer Buffer with the setup packet.
531 * @param[in] setup_buffer_size Size of the setup packet (in bytes).
532 * @param[in] data_buffer Buffer with data to be sent.
533 * @param[in] data_buffer_size Size of the buffer with outgoing data (in bytes).
534 * @return Error code.
535 */
[3954a63b]536int usb_pipe_control_write(usb_pipe_t *pipe,
[dc04868]537 void *setup_buffer, size_t setup_buffer_size,
[567d002]538 void *data_buffer, size_t data_buffer_size)
[dc04868]539{
540 assert(pipe);
541
[47c573a]542 if ((setup_buffer == NULL) || (setup_buffer_size == 0)) {
543 return EINVAL;
544 }
545
546 if ((data_buffer == NULL) && (data_buffer_size > 0)) {
547 return EINVAL;
548 }
549
550 if ((data_buffer != NULL) && (data_buffer_size == 0)) {
551 return EINVAL;
552 }
553
[dc04868]554 if ((pipe->direction != USB_DIRECTION_BOTH)
555 || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
556 return EBADF;
557 }
558
[a546687]559 int rc;
560
561 rc = pipe_add_ref(pipe);
562 if (rc != EOK) {
563 return rc;
564 }
565
566 rc = usb_pipe_control_write_no_check(pipe,
[47c573a]567 setup_buffer, setup_buffer_size, data_buffer, data_buffer_size);
[567d002]568
[a546687]569 pipe_drop_ref(pipe);
570
[567d002]571 return rc;
[dc04868]572}
573
574
575/**
576 * @}
577 */
Note: See TracBrowser for help on using the repository browser.