source: mainline/uspace/lib/drv/generic/remote_usb.c@ 479e32d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 479e32d was 327f147, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usbhost: change parameters of methods

Pass (device_t, usb_target_t) to read and write, which finally allows to drop hash tables and access device right away. Then, all callbacks to complete transfer now uses usb_transfer_batch. This requires libdrv to include libusbhost, but it is not linked against it - it is there only to share definition of usb_transfer_batch_t.

  • Property mode set to 100644
File size: 17.1 KB
Line 
1/*
2 * Copyright (c) 2010 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libdrv
31 * @{
32 */
33/** @file
34 */
35
36#include <async.h>
37#include <macros.h>
38#include <errno.h>
39#include <devman.h>
40#include <usb/host/usb_transfer_batch.h>
41
42#include "usb_iface.h"
43#include "ddf/driver.h"
44
45
46usb_dev_session_t *usb_dev_connect(devman_handle_t handle)
47{
48 return devman_device_connect(handle, IPC_FLAG_BLOCKING);
49}
50
51usb_dev_session_t *usb_dev_connect_to_self(ddf_dev_t *dev)
52{
53 return devman_parent_device_connect(ddf_dev_get_handle(dev), IPC_FLAG_BLOCKING);
54}
55
56void usb_dev_disconnect(usb_dev_session_t *sess)
57{
58 if (sess)
59 async_hangup(sess);
60}
61
62typedef enum {
63 IPC_M_USB_GET_MY_INTERFACE,
64 IPC_M_USB_GET_MY_DEVICE_HANDLE,
65 IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
66 IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
67 IPC_M_USB_DEVICE_ENUMERATE,
68 IPC_M_USB_DEVICE_REMOVE,
69 IPC_M_USB_REGISTER_ENDPOINT,
70 IPC_M_USB_UNREGISTER_ENDPOINT,
71 IPC_M_USB_READ,
72 IPC_M_USB_WRITE,
73} usb_iface_funcs_t;
74
75/** Tell interface number given device can use.
76 * @param[in] exch IPC communication exchange
77 * @param[in] handle Id of the device
78 * @param[out] usb_iface Assigned USB interface
79 * @return Error code.
80 */
81int usb_get_my_interface(async_exch_t *exch, int *usb_iface)
82{
83 if (!exch)
84 return EBADMEM;
85 sysarg_t iface_no;
86 const int ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
87 IPC_M_USB_GET_MY_INTERFACE, &iface_no);
88 if (ret == EOK && usb_iface)
89 *usb_iface = (int)iface_no;
90 return ret;
91}
92
93/** Tell devman handle of the usb device function.
94 *
95 * @param[in] exch IPC communication exchange
96 * @param[out] handle devman handle of the HC used by the target device.
97 *
98 * @return Error code.
99 *
100 */
101int usb_get_my_device_handle(async_exch_t *exch, devman_handle_t *handle)
102{
103 devman_handle_t h = 0;
104 const int ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
105 IPC_M_USB_GET_MY_DEVICE_HANDLE, &h);
106 if (ret == EOK && handle)
107 *handle = (devman_handle_t)h;
108 return ret;
109}
110
111/** Reserve default USB address.
112 * @param[in] exch IPC communication exchange
113 * @param[in] speed Communication speed of the newly attached device
114 * @return Error code.
115 */
116int usb_reserve_default_address(async_exch_t *exch, usb_speed_t speed)
117{
118 if (!exch)
119 return EBADMEM;
120 return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
121 IPC_M_USB_RESERVE_DEFAULT_ADDRESS, speed);
122}
123
124/** Release default USB address.
125 *
126 * @param[in] exch IPC communication exchange
127 *
128 * @return Error code.
129 *
130 */
131int usb_release_default_address(async_exch_t *exch)
132{
133 if (!exch)
134 return EBADMEM;
135 return async_req_1_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
136 IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
137}
138
139/** Trigger USB device enumeration
140 *
141 * @param[in] exch IPC communication exchange
142 * @param[out] handle Identifier of the newly added device (if successful)
143 *
144 * @return Error code.
145 *
146 */
147int usb_device_enumerate(async_exch_t *exch, unsigned port)
148{
149 if (!exch)
150 return EBADMEM;
151 const int ret = async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
152 IPC_M_USB_DEVICE_ENUMERATE, port);
153 return ret;
154}
155
156/** Trigger USB device enumeration
157 *
158 * @param[in] exch IPC communication exchange
159 * @param[in] handle Identifier of the device
160 *
161 * @return Error code.
162 *
163 */
164int usb_device_remove(async_exch_t *exch, unsigned port)
165{
166 if (!exch)
167 return EBADMEM;
168 return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
169 IPC_M_USB_DEVICE_REMOVE, port);
170}
171
172int static_assert[sizeof(sysarg_t) >= 4 ? 1 : -1];
173typedef union {
174 uint8_t arr[sizeof(sysarg_t)];
175 sysarg_t arg;
176} pack8_t;
177
178int usb_register_endpoint(async_exch_t *exch,
179 usb_endpoint_desc_t *endpoint_desc)
180{
181 if (!exch)
182 return EBADMEM;
183
184 aid_t opening_request = async_send_1(exch,
185 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_REGISTER_ENDPOINT, NULL);
186
187 if (opening_request == 0) {
188 return ENOMEM;
189 }
190
191 const int ret = async_data_write_start(exch, (void *) endpoint_desc,
192 sizeof(usb_endpoint_desc_t));
193
194 if (ret != EOK) {
195 async_forget(opening_request);
196 return ret;
197 }
198
199 /* Wait for the answer. */
200 sysarg_t opening_request_rc;
201 async_wait_for(opening_request, &opening_request_rc);
202
203 return (int) opening_request_rc;
204}
205
206int usb_unregister_endpoint(async_exch_t *exch,
207 usb_endpoint_desc_t *endpoint_desc)
208{
209 if (!exch)
210 return EBADMEM;
211
212 aid_t opening_request = async_send_1(exch,
213 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_UNREGISTER_ENDPOINT, NULL);
214
215 if (opening_request == 0) {
216 return ENOMEM;
217 }
218
219 const int ret = async_data_write_start(exch, endpoint_desc,
220 sizeof(usb_endpoint_desc_t));
221 if (ret != EOK) {
222 async_forget(opening_request);
223 return ret;
224 }
225
226 /* Wait for the answer. */
227 sysarg_t opening_request_rc;
228 async_wait_for(opening_request, &opening_request_rc);
229
230 return (int) opening_request_rc;
231}
232
233int usb_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
234 void *data, size_t size, size_t *rec_size)
235{
236 if (!exch)
237 return EBADMEM;
238
239 if (size == 0 && setup == 0)
240 return EOK;
241
242 /* Make call identifying target USB device and type of transfer. */
243 aid_t opening_request = async_send_4(exch,
244 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_READ, endpoint,
245 (setup & UINT32_MAX), (setup >> 32), NULL);
246
247 if (opening_request == 0) {
248 return ENOMEM;
249 }
250
251 /* Retrieve the data. */
252 ipc_call_t data_request_call;
253 aid_t data_request =
254 async_data_read(exch, data, size, &data_request_call);
255
256 if (data_request == 0) {
257 // FIXME: How to let the other side know that we want to abort?
258 async_forget(opening_request);
259 return ENOMEM;
260 }
261
262 /* Wait for the answer. */
263 sysarg_t data_request_rc;
264 sysarg_t opening_request_rc;
265 async_wait_for(data_request, &data_request_rc);
266 async_wait_for(opening_request, &opening_request_rc);
267
268 if (data_request_rc != EOK) {
269 /* Prefer the return code of the opening request. */
270 if (opening_request_rc != EOK) {
271 return (int) opening_request_rc;
272 } else {
273 return (int) data_request_rc;
274 }
275 }
276 if (opening_request_rc != EOK) {
277 return (int) opening_request_rc;
278 }
279
280 *rec_size = IPC_GET_ARG2(data_request_call);
281 return EOK;
282}
283
284int usb_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
285 const void *data, size_t size)
286{
287 if (!exch)
288 return EBADMEM;
289
290 if (size == 0 && setup == 0)
291 return EOK;
292
293 aid_t opening_request = async_send_5(exch,
294 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
295 (setup & UINT32_MAX), (setup >> 32), NULL);
296
297 if (opening_request == 0) {
298 return ENOMEM;
299 }
300
301 /* Send the data if any. */
302 if (size > 0) {
303 const int ret = async_data_write_start(exch, data, size);
304 if (ret != EOK) {
305 async_forget(opening_request);
306 return ret;
307 }
308 }
309
310 /* Wait for the answer. */
311 sysarg_t opening_request_rc;
312 async_wait_for(opening_request, &opening_request_rc);
313
314 return (int) opening_request_rc;
315}
316
317static void remote_usb_get_my_interface(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
318static void remote_usb_get_my_device_handle(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
319static void remote_usb_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
320static void remote_usb_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
321static void remote_usb_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
322static void remote_usb_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
323static void remote_usb_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
324static void remote_usb_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
325static void remote_usb_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
326static void remote_usb_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
327
328/** Remote USB interface operations. */
329static const remote_iface_func_ptr_t remote_usb_iface_ops [] = {
330 [IPC_M_USB_GET_MY_INTERFACE] = remote_usb_get_my_interface,
331 [IPC_M_USB_GET_MY_DEVICE_HANDLE] = remote_usb_get_my_device_handle,
332 [IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usb_reserve_default_address,
333 [IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usb_release_default_address,
334 [IPC_M_USB_DEVICE_ENUMERATE] = remote_usb_device_enumerate,
335 [IPC_M_USB_DEVICE_REMOVE] = remote_usb_device_remove,
336 [IPC_M_USB_REGISTER_ENDPOINT] = remote_usb_register_endpoint,
337 [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usb_unregister_endpoint,
338 [IPC_M_USB_READ] = remote_usb_read,
339 [IPC_M_USB_WRITE] = remote_usb_write,
340};
341
342/** Remote USB interface structure.
343 */
344const remote_iface_t remote_usb_iface = {
345 .method_count = ARRAY_SIZE(remote_usb_iface_ops),
346 .methods = remote_usb_iface_ops,
347};
348
349typedef struct {
350 ipc_callid_t caller;
351 ipc_callid_t data_caller;
352 void *buffer;
353} async_transaction_t;
354
355void remote_usb_get_my_interface(ddf_fun_t *fun, void *iface,
356 ipc_callid_t callid, ipc_call_t *call)
357{
358 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
359
360 if (usb_iface->get_my_interface == NULL) {
361 async_answer_0(callid, ENOTSUP);
362 return;
363 }
364
365 int iface_no;
366 const int ret = usb_iface->get_my_interface(fun, &iface_no);
367 if (ret != EOK) {
368 async_answer_0(callid, ret);
369 } else {
370 async_answer_1(callid, EOK, iface_no);
371 }
372}
373
374void remote_usb_get_my_device_handle(ddf_fun_t *fun, void *iface,
375 ipc_callid_t callid, ipc_call_t *call)
376{
377 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
378
379 if (usb_iface->get_my_device_handle == NULL) {
380 async_answer_0(callid, ENOTSUP);
381 return;
382 }
383
384 devman_handle_t handle;
385 const int ret = usb_iface->get_my_device_handle(fun, &handle);
386 if (ret != EOK) {
387 async_answer_0(callid, ret);
388 }
389
390 async_answer_1(callid, EOK, (sysarg_t) handle);
391}
392
393void remote_usb_reserve_default_address(ddf_fun_t *fun, void *iface,
394 ipc_callid_t callid, ipc_call_t *call)
395{
396 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
397
398 if (usb_iface->reserve_default_address == NULL) {
399 async_answer_0(callid, ENOTSUP);
400 return;
401 }
402
403 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
404 const int ret = usb_iface->reserve_default_address(fun, speed);
405 async_answer_0(callid, ret);
406}
407
408void remote_usb_release_default_address(ddf_fun_t *fun, void *iface,
409 ipc_callid_t callid, ipc_call_t *call)
410{
411 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
412
413 if (usb_iface->release_default_address == NULL) {
414 async_answer_0(callid, ENOTSUP);
415 return;
416 }
417
418 const int ret = usb_iface->release_default_address(fun);
419 async_answer_0(callid, ret);
420}
421
422static void remote_usb_device_enumerate(ddf_fun_t *fun, void *iface,
423 ipc_callid_t callid, ipc_call_t *call)
424{
425 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
426
427 if (usb_iface->device_enumerate == NULL) {
428 async_answer_0(callid, ENOTSUP);
429 return;
430 }
431
432 const unsigned port = DEV_IPC_GET_ARG1(*call);
433 const int ret = usb_iface->device_enumerate(fun, port);
434 async_answer_0(callid, ret);
435}
436
437static void remote_usb_device_remove(ddf_fun_t *fun, void *iface,
438 ipc_callid_t callid, ipc_call_t *call)
439{
440 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
441
442 if (usb_iface->device_remove == NULL) {
443 async_answer_0(callid, ENOTSUP);
444 return;
445 }
446
447 const unsigned port = DEV_IPC_GET_ARG1(*call);
448 const int ret = usb_iface->device_remove(fun, port);
449 async_answer_0(callid, ret);
450}
451
452static void remote_usb_register_endpoint(ddf_fun_t *fun, void *iface,
453 ipc_callid_t callid, ipc_call_t *call)
454{
455 assert(fun);
456 assert(iface);
457 assert(call);
458
459 const usb_iface_t *usb_iface = iface;
460
461 if (!usb_iface->register_endpoint) {
462 async_answer_0(callid, ENOTSUP);
463 return;
464 }
465
466 void *buffer = NULL;
467 size_t size = 0;
468 int rc = async_data_write_accept(&buffer, false,
469 sizeof(usb_endpoint_desc_t), sizeof(usb_endpoint_desc_t), 0, &size);
470
471 if (rc != EOK) {
472 free(buffer);
473 async_answer_0(callid, rc);
474 return;
475 }
476
477 usb_endpoint_desc_t *endpoint_desc = (usb_endpoint_desc_t *) buffer;
478 rc = usb_iface->register_endpoint(fun, endpoint_desc);
479
480 free(buffer);
481 async_answer_0(callid, rc);
482}
483
484static void remote_usb_unregister_endpoint(ddf_fun_t *fun, void *iface,
485 ipc_callid_t callid, ipc_call_t *call)
486{
487 assert(fun);
488 assert(iface);
489 assert(call);
490
491 const usb_iface_t *usb_iface = iface;
492
493 if (!usb_iface->unregister_endpoint) {
494 async_answer_0(callid, ENOTSUP);
495 return;
496 }
497
498 void *buffer = NULL;
499 size_t size = 0;
500 int rc = async_data_write_accept(&buffer, false,
501 sizeof(usb_endpoint_desc_t), sizeof(usb_endpoint_desc_t), 0, &size);
502
503 if (rc != EOK) {
504 free(buffer);
505 async_answer_0(callid, rc);
506 return;
507 }
508
509 usb_endpoint_desc_t *endpoint_desc = (usb_endpoint_desc_t *) buffer;
510 usb_iface->unregister_endpoint(fun, endpoint_desc);
511
512 free(buffer);
513 async_answer_0(callid, rc);
514}
515
516static void async_transaction_destroy(async_transaction_t *trans)
517{
518 if (trans == NULL) {
519 return;
520 }
521 if (trans->buffer != NULL) {
522 free(trans->buffer);
523 }
524
525 free(trans);
526}
527
528static async_transaction_t *async_transaction_create(ipc_callid_t caller)
529{
530 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
531 if (trans == NULL) {
532 return NULL;
533 }
534
535 trans->caller = caller;
536 trans->data_caller = 0;
537 trans->buffer = NULL;
538
539 return trans;
540}
541
542static int callback_out(usb_transfer_batch_t *batch)
543{
544 async_transaction_t *trans = batch->on_complete_data;
545
546 const int err = async_answer_0(trans->caller, batch->error);
547
548 async_transaction_destroy(trans);
549
550 return err;
551}
552
553static int callback_in(usb_transfer_batch_t *batch)
554{
555 async_transaction_t *trans = batch->on_complete_data;
556
557 if (trans->data_caller) {
558 if (batch->error == EOK) {
559 batch->error = async_data_read_finalize(trans->data_caller,
560 trans->buffer, batch->transfered_size);
561 } else {
562 async_answer_0(trans->data_caller, EINTR);
563 }
564 }
565
566 const int err = async_answer_0(trans->caller, batch->error);
567 async_transaction_destroy(trans);
568 return err;
569}
570
571void remote_usb_read(
572 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
573{
574 assert(fun);
575 assert(iface);
576 assert(call);
577
578 const usb_iface_t *usb_iface = iface;
579
580 if (!usb_iface->read) {
581 async_answer_0(callid, ENOTSUP);
582 return;
583 }
584
585 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
586 const uint64_t setup =
587 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
588 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
589
590 async_transaction_t *trans = async_transaction_create(callid);
591 if (trans == NULL) {
592 async_answer_0(callid, ENOMEM);
593 return;
594 }
595
596 size_t size = 0;
597 if (!async_data_read_receive(&trans->data_caller, &size)) {
598 async_answer_0(callid, EPARTY);
599 async_transaction_destroy(trans);
600 return;
601 }
602
603 trans->buffer = malloc(size);
604 if (trans->buffer == NULL) {
605 async_answer_0(trans->data_caller, ENOMEM);
606 async_answer_0(callid, ENOMEM);
607 async_transaction_destroy(trans);
608 return;
609 }
610
611 const usb_target_t target = {{
612 /* .address is initialized by read itself */
613 .endpoint = ep,
614 }};
615
616 const int rc = usb_iface->read(
617 fun, target, setup, trans->buffer, size, callback_in, trans);
618
619 if (rc != EOK) {
620 async_answer_0(trans->data_caller, rc);
621 async_answer_0(callid, rc);
622 async_transaction_destroy(trans);
623 }
624}
625
626void remote_usb_write(
627 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
628{
629 assert(fun);
630 assert(iface);
631 assert(call);
632
633 const usb_iface_t *usb_iface = iface;
634
635 if (!usb_iface->write) {
636 async_answer_0(callid, ENOTSUP);
637 return;
638 }
639
640 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
641 const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
642 const uint64_t setup =
643 ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
644 (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
645
646 async_transaction_t *trans = async_transaction_create(callid);
647 if (trans == NULL) {
648 async_answer_0(callid, ENOMEM);
649 return;
650 }
651
652 size_t size = 0;
653 if (data_buffer_len > 0) {
654 const int rc = async_data_write_accept(&trans->buffer, false,
655 1, data_buffer_len, 0, &size);
656
657 if (rc != EOK) {
658 async_answer_0(callid, rc);
659 async_transaction_destroy(trans);
660 return;
661 }
662 }
663
664 const usb_target_t target = {{
665 /* .address is initialized by write itself */
666 .endpoint = ep,
667 }};
668
669 const int rc = usb_iface->write(
670 fun, target, setup, trans->buffer, size, callback_out, trans);
671
672 if (rc != EOK) {
673 async_answer_0(callid, rc);
674 async_transaction_destroy(trans);
675 }
676}
677/**
678 * @}
679 */
Note: See TracBrowser for help on using the repository browser.