source: mainline/uspace/lib/usb/hcd.c@ 2185776

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

Async communication with HCD

Added asynchronous versions of functions for sending/retrieving
data to/from HCD. These work pretty the same as functions for
sending messages in the async framework.

  • Property mode set to 100644
File size: 14.3 KB
Line 
1/*
2 * Copyright (c) 2010 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 usb
30 * @{
31 */
32/** @file
33 * @brief USB Host Controller Driver (implementation).
34 */
35#include "hcd.h"
36#include <devmap.h>
37#include <stdlib.h>
38#include <fcntl.h>
39#include <vfs/vfs.h>
40#include <errno.h>
41
42typedef struct {
43 int phone;
44 void *buffer;
45 size_t size;
46 size_t *size_transferred;
47 ipc_call_t reply;
48 aid_t request;
49} transfer_info_t;
50
51#define NAMESPACE "usb"
52
53/** String representation for USB transfer type. */
54const char * usb_str_transfer_type(usb_transfer_type_t t)
55{
56 switch (t) {
57 case USB_TRANSFER_ISOCHRONOUS:
58 return "isochronous";
59 case USB_TRANSFER_INTERRUPT:
60 return "interrupt";
61 case USB_TRANSFER_CONTROL:
62 return "control";
63 case USB_TRANSFER_BULK:
64 return "bulk";
65 default:
66 return "unknown";
67 }
68}
69
70/** String representation of USB transaction outcome. */
71const char * usb_str_transaction_outcome(usb_transaction_outcome_t o)
72{
73 switch (o) {
74 case USB_OUTCOME_OK:
75 return "ok";
76 case USB_OUTCOME_CRCERROR:
77 return "CRC error";
78 case USB_OUTCOME_BABBLE:
79 return "babble";
80 default:
81 return "unknown";
82 }
83}
84
85/** Create necessary phones for comunicating with HCD.
86 * This function wraps following calls:
87 * -# open <code>/dev/usb/<i>hcd_path</i></code> for reading
88 * -# access phone of file opened in previous step
89 * -# create callback through just opened phone
90 * -# set handler for this callback
91 * -# return the (outgoing) phone
92 *
93 * @warning This function is wrapper for several actions and therefore
94 * it is not possible - in case of error - to determine at which point
95 * error occured.
96 *
97 * @param hcd_path HCD identification under devfs
98 * (without <code>/dev/usb/</code>).
99 * @param callback_connection Handler for callbacks from HCD.
100 * @return Phone for comunicating with HCD or error code from errno.h.
101 */
102int usb_hcd_create_phones(const char * hcd_path,
103 async_client_conn_t callback_connection)
104{
105 char dev_path[DEVMAP_NAME_MAXLEN + 1];
106 snprintf(dev_path, DEVMAP_NAME_MAXLEN,
107 "/dev/%s/%s", NAMESPACE, hcd_path);
108
109 int fd = open(dev_path, O_RDONLY);
110 if (fd < 0) {
111 return fd;
112 }
113
114 int hcd_phone = fd_phone(fd);
115
116 if (hcd_phone < 0) {
117 return hcd_phone;
118 }
119
120 ipcarg_t phonehash;
121 int rc = ipc_connect_to_me(hcd_phone, 0, 0, 0, &phonehash);
122 if (rc != EOK) {
123 return rc;
124 }
125 async_new_connection(phonehash, 0, NULL, callback_connection);
126
127 return hcd_phone;
128}
129
130/** Send data from USB host to a function.
131 *
132 * @param hcd_phone Connected phone to HCD.
133 * @param target USB function address.
134 * @param transfer_type USB transfer type.
135 * @param buffer Buffer with data to be sent.
136 * @param len Buffer @p buffer size.
137 * @param[out] transaction_handle Handle of created transaction (NULL to ignore).
138 * @return Error status.
139 * @retval EOK Everything OK, buffer transfered to HCD and queued there.
140 * @retval EINVAL Invalid phone.
141 * @retval EINVAL @p buffer is NULL.
142 */
143int usb_hcd_send_data_to_function(int hcd_phone,
144 usb_target_t target, usb_transfer_type_t transfer_type,
145 void * buffer, size_t len,
146 usb_transaction_handle_t * transaction_handle)
147{
148 if (hcd_phone < 0) {
149 return EINVAL;
150 }
151 if (buffer == NULL) {
152 return EINVAL;
153 }
154
155 ipc_call_t answer_data;
156 ipcarg_t answer_rc;
157 aid_t req;
158 int rc;
159
160 req = async_send_4(hcd_phone,
161 IPC_M_USB_HCD_SEND_DATA,
162 target.address, target.endpoint,
163 transfer_type, 0,
164 &answer_data);
165
166 rc = async_data_write_start(hcd_phone, buffer, len);
167 if (rc != EOK) {
168 async_wait_for(req, NULL);
169 return rc;
170 }
171
172 async_wait_for(req, &answer_rc);
173 rc = (int)answer_rc;
174 if (rc != EOK) {
175 return rc;
176 }
177
178 if (transaction_handle != NULL) {
179 *transaction_handle = IPC_GET_ARG1(answer_data);
180 }
181
182 return EOK;
183}
184
185
186/** Inform HCD about data reception.
187 * The actual reception is handled in callback.
188 *
189 * @param hcd_phone Connected phone to HCD.
190 * @param target USB function address.
191 * @param transfer_type USB transfer type.
192 * @param len Maximum accepted packet size.
193 * @param[out] transaction_handle Handle of created transaction (NULL to ignore).
194 * @return Error status.
195 */
196int usb_hcd_prepare_data_reception(int hcd_phone,
197 usb_target_t target, usb_transfer_type_t transfer_type,
198 size_t len,
199 usb_transaction_handle_t * transaction_handle)
200{
201 if (hcd_phone < 0) {
202 return EINVAL;
203 }
204
205 usb_transaction_handle_t handle;
206
207 int rc = ipc_call_sync_5_1(hcd_phone, IPC_M_USB_HCD_RECEIVE_DATA,
208 target.address, target.endpoint,
209 transfer_type, len, 0, &handle);
210
211 if (rc != EOK) {
212 return rc;
213 }
214
215 if (transaction_handle != NULL) {
216 *transaction_handle = handle;
217 }
218
219 return EOK;
220}
221
222
223static int send_buffer(int phone, ipcarg_t method, usb_target_t target,
224 void *buffer, size_t size, usb_transaction_handle_t * transaction_handle)
225{
226 if (phone < 0) {
227 return EINVAL;
228 }
229
230 if ((buffer == NULL) && (size > 0)) {
231 return EINVAL;
232 }
233
234 ipc_call_t answer_data;
235 ipcarg_t answer_rc;
236 aid_t req;
237 int rc;
238
239 req = async_send_3(phone,
240 method,
241 target.address, target.endpoint,
242 size,
243 &answer_data);
244
245 if (size > 0) {
246 rc = async_data_write_start(phone, buffer, size);
247 if (rc != EOK) {
248 async_wait_for(req, NULL);
249 return rc;
250 }
251 }
252
253 async_wait_for(req, &answer_rc);
254 rc = (int)answer_rc;
255 if (rc != EOK) {
256 return rc;
257 }
258
259 if (transaction_handle != NULL) {
260 *transaction_handle = IPC_GET_ARG1(answer_data);
261 }
262
263 return EOK;
264}
265
266
267static int prep_receive_data(int phone, ipcarg_t method, usb_target_t target,
268 size_t size, usb_transaction_handle_t * transaction_handle)
269{
270 if (phone < 0) {
271 return EINVAL;
272 }
273
274 usb_transaction_handle_t handle;
275
276 int rc = ipc_call_sync_3_1(phone,
277 method,
278 target.address, target.endpoint,
279 size,
280 &handle);
281
282 if (rc != EOK) {
283 return rc;
284 }
285
286 if (transaction_handle != NULL) {
287 *transaction_handle = handle;
288 }
289
290 return EOK;
291}
292
293
294int usb_hcd_transfer_interrupt_out(int hcd_phone, usb_target_t target,
295 void *buffer, size_t size, usb_transaction_handle_t *handle)
296{
297 return send_buffer(hcd_phone, IPC_M_USB_HCD_INTERRUPT_OUT,
298 target, buffer, size, handle);
299}
300
301int usb_hcd_transfer_interrupt_in(int hcd_phone, usb_target_t target,
302 size_t size, usb_transaction_handle_t *handle)
303{
304 return prep_receive_data(hcd_phone, IPC_M_USB_HCD_INTERRUPT_IN,
305 target, size, handle);
306}
307
308int usb_hcd_transfer_control_write_setup(int hcd_phone, usb_target_t target,
309 void *buffer, size_t size, usb_transaction_handle_t *handle)
310{
311 return send_buffer(hcd_phone, IPC_M_USB_HCD_CONTROL_WRITE_SETUP,
312 target, buffer, size, handle);
313}
314
315int usb_hcd_transfer_control_write_data(int hcd_phone, usb_target_t target,
316 void *buffer, size_t size, usb_transaction_handle_t *handle)
317{
318 return send_buffer(hcd_phone, IPC_M_USB_HCD_CONTROL_WRITE_DATA,
319 target, buffer, size, handle);
320
321}
322int usb_hcd_transfer_control_write_status(int hcd_phone, usb_target_t target,
323 usb_transaction_handle_t *handle)
324{
325 return prep_receive_data(hcd_phone, IPC_M_USB_HCD_CONTROL_WRITE_STATUS,
326 target, 0, handle);
327}
328
329int usb_hcd_transfer_control_read_setup(int hcd_phone, usb_target_t target,
330 void *buffer, size_t size, usb_transaction_handle_t *handle)
331{
332 return send_buffer(hcd_phone, IPC_M_USB_HCD_CONTROL_READ_SETUP,
333 target, buffer, size, handle);
334}
335int usb_hcd_transfer_control_read_data(int hcd_phone, usb_target_t target,
336 size_t size, usb_transaction_handle_t *handle)
337{
338 return prep_receive_data(hcd_phone, IPC_M_USB_HCD_CONTROL_READ_DATA,
339 target, size, handle);
340}
341int usb_hcd_transfer_control_read_status(int hcd_phone, usb_target_t target,
342 usb_transaction_handle_t *handle)
343{
344 return send_buffer(hcd_phone, IPC_M_USB_HCD_CONTROL_READ_STATUS,
345 target, NULL, 0, handle);
346}
347
348
349
350
351
352/*
353 * =================
354 * async versions of the above functions
355 * =================
356 */
357
358static int async_send_buffer(int phone, int method,
359 usb_target_t target,
360 void *buffer, size_t size,
361 usb_handle_t *handle)
362{
363 if (phone < 0) {
364 return EINVAL;
365 }
366
367 if ((buffer == NULL) && (size > 0)) {
368 return EINVAL;
369 }
370
371 if (handle == NULL) {
372 return EINVAL;
373 }
374
375 transfer_info_t *transfer
376 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
377 if (transfer == NULL) {
378 return ENOMEM;
379 }
380
381 transfer->size_transferred = NULL;
382 transfer->buffer = NULL;
383 transfer->size = 0;
384 transfer->phone = phone;
385
386 int rc;
387
388 transfer->request = async_send_3(phone,
389 method,
390 target.address, target.endpoint,
391 size,
392 &transfer->reply);
393
394 if (size > 0) {
395 rc = async_data_write_start(phone, buffer, size);
396 if (rc != EOK) {
397 async_wait_for(transfer->request, NULL);
398 return rc;
399 }
400 }
401
402 *handle = (usb_handle_t) transfer;
403
404 return EOK;
405}
406
407static int async_recv_buffer(int phone, int method,
408 usb_target_t target,
409 void *buffer, size_t size, size_t *actual_size,
410 usb_handle_t *handle)
411{
412 if (phone < 0) {
413 return EINVAL;
414 }
415
416 if ((buffer == NULL) && (size > 0)) {
417 return EINVAL;
418 }
419
420 if (handle == NULL) {
421 return EINVAL;
422 }
423
424 transfer_info_t *transfer
425 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
426 if (transfer == NULL) {
427 return ENOMEM;
428 }
429
430 transfer->size_transferred = actual_size;
431 transfer->buffer = buffer;
432 transfer->size = size;
433 transfer->phone = phone;
434
435 transfer->request = async_send_3(phone,
436 method,
437 target.address, target.endpoint,
438 size,
439 &transfer->reply);
440
441 *handle = (usb_handle_t) transfer;
442
443 return EOK;
444}
445
446static int read_buffer_in(int phone, ipcarg_t hash,
447 void *buffer, size_t size, size_t *actual_size)
448{
449 ipc_call_t answer_data;
450 ipcarg_t answer_rc;
451 aid_t req;
452 int rc;
453
454 req = async_send_1(phone,
455 IPC_M_USB_HCD_GET_BUFFER_ASYNC,
456 hash,
457 &answer_data);
458
459 rc = async_data_read_start(phone, buffer, size);
460 if (rc != EOK) {
461 async_wait_for(req, NULL);
462 return EINVAL;
463 }
464
465 async_wait_for(req, &answer_rc);
466 rc = (int)answer_rc;
467
468 if (rc != EOK) {
469 return rc;
470 }
471
472 *actual_size = IPC_GET_ARG1(answer_data);
473
474 return EOK;
475}
476
477
478int usb_hcd_async_wait_for(usb_handle_t handle)
479{
480 if (handle == 0) {
481 return EBADMEM;
482 }
483
484 int rc = EOK;
485
486 transfer_info_t *transfer = (transfer_info_t *) handle;
487
488 ipcarg_t answer_rc;
489 async_wait_for(transfer->request, &answer_rc);
490
491 if (answer_rc != EOK) {
492 rc = (int) answer_rc;
493 goto leave;
494 }
495
496 /*
497 * If the buffer is not NULL, we must accept some data.
498 */
499 if ((transfer->buffer != NULL) && (transfer->size > 0)) {
500 /*
501 * The buffer hash identifies the data on the server
502 * side.
503 * We will use it when actually reading-in the data.
504 */
505 ipcarg_t buffer_hash = IPC_GET_ARG1(transfer->reply);
506 if (buffer_hash == 0) {
507 rc = ENOENT;
508 goto leave;
509 }
510
511 size_t actual_size;
512 rc = read_buffer_in(transfer->phone, buffer_hash,
513 transfer->buffer, transfer->size, &actual_size);
514
515 if (rc != EOK) {
516 goto leave;
517 }
518
519 if (transfer->size_transferred) {
520 *(transfer->size_transferred) = actual_size;
521 }
522 }
523
524leave:
525 free(transfer);
526
527 return rc;
528}
529
530int usb_hcd_async_transfer_interrupt_out(int hcd_phone,
531 usb_target_t target,
532 void *buffer, size_t size,
533 usb_handle_t *handle)
534{
535 return async_send_buffer(hcd_phone,
536 IPC_M_USB_HCD_INTERRUPT_OUT_ASYNC,
537 target,
538 buffer, size,
539 handle);
540}
541
542int usb_hcd_async_transfer_interrupt_in(int hcd_phone,
543 usb_target_t target,
544 void *buffer, size_t size, size_t *actual_size,
545 usb_handle_t *handle)
546{
547 return async_recv_buffer(hcd_phone,
548 IPC_M_USB_HCD_INTERRUPT_IN_ASYNC,
549 target,
550 buffer, size, actual_size,
551 handle);
552}
553
554int usb_hcd_async_transfer_control_write_setup(int hcd_phone,
555 usb_target_t target,
556 void *buffer, size_t size,
557 usb_handle_t *handle)
558{
559 return async_send_buffer(hcd_phone,
560 IPC_M_USB_HCD_CONTROL_WRITE_SETUP_ASYNC,
561 target,
562 buffer, size,
563 handle);
564}
565
566int usb_hcd_async_transfer_control_write_data(int hcd_phone,
567 usb_target_t target,
568 void *buffer, size_t size,
569 usb_handle_t *handle)
570{
571 return async_send_buffer(hcd_phone,
572 IPC_M_USB_HCD_CONTROL_WRITE_DATA_ASYNC,
573 target,
574 buffer, size,
575 handle);
576}
577
578int usb_hcd_async_transfer_control_write_status(int hcd_phone,
579 usb_target_t target,
580 usb_handle_t *handle)
581{
582 return async_recv_buffer(hcd_phone,
583 IPC_M_USB_HCD_CONTROL_WRITE_STATUS_ASYNC,
584 target,
585 NULL, 0, NULL,
586 handle);
587}
588
589int usb_hcd_async_transfer_control_read_setup(int hcd_phone,
590 usb_target_t target,
591 void *buffer, size_t size,
592 usb_handle_t *handle)
593{
594 return async_send_buffer(hcd_phone,
595 IPC_M_USB_HCD_CONTROL_READ_SETUP_ASYNC,
596 target,
597 buffer, size,
598 handle);
599}
600
601int usb_hcd_async_transfer_control_read_data(int hcd_phone,
602 usb_target_t target,
603 void *buffer, size_t size, size_t *actual_size,
604 usb_handle_t *handle)
605{
606 return async_recv_buffer(hcd_phone,
607 IPC_M_USB_HCD_CONTROL_READ_DATA_ASYNC,
608 target,
609 buffer, size, actual_size,
610 handle);
611}
612
613int usb_hcd_async_transfer_control_read_status(int hcd_phone,
614 usb_target_t target,
615 usb_handle_t *handle)
616{
617 return async_send_buffer(hcd_phone,
618 IPC_M_USB_HCD_CONTROL_READ_STATUS_ASYNC,
619 target,
620 NULL, 0,
621 handle);
622}
623
624/**
625 * @}
626 */
Note: See TracBrowser for help on using the repository browser.