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

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

Serialization of IPC requests over pipe phone

The pipe phone is now guarded by a fibril mutex.

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