source: mainline/uspace/lib/drv/generic/remote_usb.c@ 8351f9a4

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8351f9a4 was 4e732f1a, checked in by Jan Vesely <jano.vesely@…>, 11 years ago

usb: Add support for multiple packets per microframe.

  • Property mode set to 100644
File size: 16.4 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
41#include "usb_iface.h"
42#include "ddf/driver.h"
43
44
45usb_dev_session_t *usb_dev_connect(devman_handle_t handle)
46{
47 return devman_device_connect(EXCHANGE_PARALLEL, handle, IPC_FLAG_BLOCKING);
48}
49
50usb_dev_session_t *usb_dev_connect_to_self(ddf_dev_t *dev)
51{
52 // TODO All usb requests are atomic so this is safe,
53 // it will need to change once USING EXCHANGE PARALLEL is safe with
54 // devman_parent_device_connect
55 return devman_parent_device_connect(EXCHANGE_ATOMIC,
56 ddf_dev_get_handle(dev), IPC_FLAG_BLOCKING);
57}
58
59void usb_dev_disconnect(usb_dev_session_t *sess)
60{
61 if (sess)
62 async_hangup(sess);
63}
64
65typedef enum {
66 IPC_M_USB_GET_MY_INTERFACE,
67 IPC_M_USB_GET_MY_DEVICE_HANDLE,
68 IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
69 IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
70 IPC_M_USB_DEVICE_ENUMERATE,
71 IPC_M_USB_DEVICE_REMOVE,
72 IPC_M_USB_REGISTER_ENDPOINT,
73 IPC_M_USB_UNREGISTER_ENDPOINT,
74 IPC_M_USB_READ,
75 IPC_M_USB_WRITE,
76} usb_iface_funcs_t;
77
78/** Tell interface number given device can use.
79 * @param[in] exch IPC communication exchange
80 * @param[in] handle Id of the device
81 * @param[out] usb_iface Assigned USB interface
82 * @return Error code.
83 */
84int usb_get_my_interface(async_exch_t *exch, int *usb_iface)
85{
86 if (!exch)
87 return EBADMEM;
88 sysarg_t iface_no;
89 const int ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
90 IPC_M_USB_GET_MY_INTERFACE, &iface_no);
91 if (ret == EOK && usb_iface)
92 *usb_iface = (int)iface_no;
93 return ret;
94}
95
96/** Tell devman handle of the usb device function.
97 * @param[in] exch IPC communication exchange
98 * @param[out] handle devman handle of the HC used by the target device.
99 * @return Error code.
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 * @param[in] exch IPC communication exchange
126 * @return Error code.
127 */
128int usb_release_default_address(async_exch_t *exch)
129{
130 if (!exch)
131 return EBADMEM;
132 return async_req_1_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
133 IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
134}
135
136/** Trigger USB device enumeration
137 * @param[in] exch IPC communication exchange
138 * @param[out] handle Identifier of the newly added device (if successful)
139 * @return Error code.
140 */
141int usb_device_enumerate(async_exch_t *exch, unsigned port)
142{
143 if (!exch)
144 return EBADMEM;
145 const int ret = async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
146 IPC_M_USB_DEVICE_ENUMERATE, port);
147 return ret;
148}
149
150/** Trigger USB device enumeration
151 * @param[in] exch IPC communication exchange
152 * @param[in] handle Identifier of the device
153 * @return Error code.
154 */
155int usb_device_remove(async_exch_t *exch, unsigned port)
156{
157 if (!exch)
158 return EBADMEM;
159 return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
160 IPC_M_USB_DEVICE_REMOVE, port);
161}
162
163int static_assert[sizeof(sysarg_t) >= 4 ? 1 : -1];
164typedef union {
165 uint8_t arr[sizeof(sysarg_t)];
166 sysarg_t arg;
167} pack8_t;
168
169int usb_register_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
170 usb_transfer_type_t type, usb_direction_t direction,
171 size_t mps, unsigned packets, unsigned interval)
172{
173 if (!exch)
174 return EBADMEM;
175 pack8_t pack;
176 pack.arr[0] = type;
177 pack.arr[1] = direction;
178 pack.arr[2] = interval;
179 pack.arr[3] = packets;
180
181 return async_req_4_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
182 IPC_M_USB_REGISTER_ENDPOINT, endpoint, pack.arg, mps);
183
184}
185
186int usb_unregister_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
187 usb_direction_t direction)
188{
189 if (!exch)
190 return EBADMEM;
191 return async_req_3_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
192 IPC_M_USB_UNREGISTER_ENDPOINT, endpoint, direction);
193}
194
195int usb_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
196 void *data, size_t size, size_t *rec_size)
197{
198 if (!exch)
199 return EBADMEM;
200
201 if (size == 0 && setup == 0)
202 return EOK;
203
204 /* Make call identifying target USB device and type of transfer. */
205 aid_t opening_request = async_send_4(exch,
206 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_READ, endpoint,
207 (setup & UINT32_MAX), (setup >> 32), NULL);
208
209 if (opening_request == 0) {
210 return ENOMEM;
211 }
212
213 /* Retrieve the data. */
214 ipc_call_t data_request_call;
215 aid_t data_request =
216 async_data_read(exch, data, size, &data_request_call);
217
218 if (data_request == 0) {
219 // FIXME: How to let the other side know that we want to abort?
220 async_forget(opening_request);
221 return ENOMEM;
222 }
223
224 /* Wait for the answer. */
225 sysarg_t data_request_rc;
226 sysarg_t opening_request_rc;
227 async_wait_for(data_request, &data_request_rc);
228 async_wait_for(opening_request, &opening_request_rc);
229
230 if (data_request_rc != EOK) {
231 /* Prefer the return code of the opening request. */
232 if (opening_request_rc != EOK) {
233 return (int) opening_request_rc;
234 } else {
235 return (int) data_request_rc;
236 }
237 }
238 if (opening_request_rc != EOK) {
239 return (int) opening_request_rc;
240 }
241
242 *rec_size = IPC_GET_ARG2(data_request_call);
243 return EOK;
244}
245
246int usb_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
247 const void *data, size_t size)
248{
249 if (!exch)
250 return EBADMEM;
251
252 if (size == 0 && setup == 0)
253 return EOK;
254
255 aid_t opening_request = async_send_5(exch,
256 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
257 (setup & UINT32_MAX), (setup >> 32), NULL);
258
259 if (opening_request == 0) {
260 return ENOMEM;
261 }
262
263 /* Send the data if any. */
264 if (size > 0) {
265 const int ret = async_data_write_start(exch, data, size);
266 if (ret != EOK) {
267 async_forget(opening_request);
268 return ret;
269 }
270 }
271
272 /* Wait for the answer. */
273 sysarg_t opening_request_rc;
274 async_wait_for(opening_request, &opening_request_rc);
275
276 return (int) opening_request_rc;
277}
278
279static void remote_usb_get_my_interface(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
280static void remote_usb_get_my_device_handle(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
281static void remote_usb_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
282static void remote_usb_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
283static void remote_usb_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
284static void remote_usb_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
285static void remote_usb_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
286static void remote_usb_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
287static void remote_usb_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
288static void remote_usb_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
289
290/** Remote USB interface operations. */
291static const remote_iface_func_ptr_t remote_usb_iface_ops [] = {
292 [IPC_M_USB_GET_MY_INTERFACE] = remote_usb_get_my_interface,
293 [IPC_M_USB_GET_MY_DEVICE_HANDLE] = remote_usb_get_my_device_handle,
294 [IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usb_reserve_default_address,
295 [IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usb_release_default_address,
296 [IPC_M_USB_DEVICE_ENUMERATE] = remote_usb_device_enumerate,
297 [IPC_M_USB_DEVICE_REMOVE] = remote_usb_device_remove,
298 [IPC_M_USB_REGISTER_ENDPOINT] = remote_usb_register_endpoint,
299 [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usb_unregister_endpoint,
300 [IPC_M_USB_READ] = remote_usb_read,
301 [IPC_M_USB_WRITE] = remote_usb_write,
302};
303
304/** Remote USB interface structure.
305 */
306const remote_iface_t remote_usb_iface = {
307 .method_count = ARRAY_SIZE(remote_usb_iface_ops),
308 .methods = remote_usb_iface_ops,
309};
310
311void remote_usb_get_my_interface(ddf_fun_t *fun, void *iface,
312 ipc_callid_t callid, ipc_call_t *call)
313{
314 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
315
316 if (usb_iface->get_my_interface == NULL) {
317 async_answer_0(callid, ENOTSUP);
318 return;
319 }
320
321 int iface_no;
322 const int ret = usb_iface->get_my_interface(fun, &iface_no);
323 if (ret != EOK) {
324 async_answer_0(callid, ret);
325 } else {
326 async_answer_1(callid, EOK, iface_no);
327 }
328}
329
330void remote_usb_get_my_device_handle(ddf_fun_t *fun, void *iface,
331 ipc_callid_t callid, ipc_call_t *call)
332{
333 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
334
335 if (usb_iface->get_my_device_handle == NULL) {
336 async_answer_0(callid, ENOTSUP);
337 return;
338 }
339
340 devman_handle_t handle;
341 const int ret = usb_iface->get_my_device_handle(fun, &handle);
342 if (ret != EOK) {
343 async_answer_0(callid, ret);
344 }
345
346 async_answer_1(callid, EOK, (sysarg_t) handle);
347}
348
349void remote_usb_reserve_default_address(ddf_fun_t *fun, void *iface,
350 ipc_callid_t callid, ipc_call_t *call)
351{
352 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
353
354 if (usb_iface->reserve_default_address == NULL) {
355 async_answer_0(callid, ENOTSUP);
356 return;
357 }
358
359 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
360 const int ret = usb_iface->reserve_default_address(fun, speed);
361 async_answer_0(callid, ret);
362}
363
364void remote_usb_release_default_address(ddf_fun_t *fun, void *iface,
365 ipc_callid_t callid, ipc_call_t *call)
366{
367 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
368
369 if (usb_iface->release_default_address == NULL) {
370 async_answer_0(callid, ENOTSUP);
371 return;
372 }
373
374 const int ret = usb_iface->release_default_address(fun);
375 async_answer_0(callid, ret);
376}
377
378static void remote_usb_device_enumerate(ddf_fun_t *fun, void *iface,
379 ipc_callid_t callid, ipc_call_t *call)
380{
381 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
382
383 if (usb_iface->device_enumerate == NULL) {
384 async_answer_0(callid, ENOTSUP);
385 return;
386 }
387
388 const unsigned port = DEV_IPC_GET_ARG1(*call);
389 const int ret = usb_iface->device_enumerate(fun, port);
390 async_answer_0(callid, ret);
391}
392
393static void remote_usb_device_remove(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->device_remove == NULL) {
399 async_answer_0(callid, ENOTSUP);
400 return;
401 }
402
403 const unsigned port = DEV_IPC_GET_ARG1(*call);
404 const int ret = usb_iface->device_remove(fun, port);
405 async_answer_0(callid, ret);
406}
407
408static void remote_usb_register_endpoint(ddf_fun_t *fun, void *iface,
409 ipc_callid_t callid, ipc_call_t *call)
410{
411 usb_iface_t *usb_iface = (usb_iface_t *) iface;
412
413 if (!usb_iface->register_endpoint) {
414 async_answer_0(callid, ENOTSUP);
415 return;
416 }
417
418 const usb_endpoint_t endpoint = DEV_IPC_GET_ARG1(*call);
419 const pack8_t pack = { .arg = DEV_IPC_GET_ARG2(*call)};
420 const size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
421
422 const usb_transfer_type_t transfer_type = pack.arr[0];
423 const usb_direction_t direction = pack.arr[1];
424 unsigned packets = pack.arr[2];
425 unsigned interval = pack.arr[3];
426
427 const int ret = usb_iface->register_endpoint(fun, endpoint,
428 transfer_type, direction, max_packet_size, packets, interval);
429
430 async_answer_0(callid, ret);
431}
432
433static void remote_usb_unregister_endpoint(ddf_fun_t *fun, void *iface,
434 ipc_callid_t callid, ipc_call_t *call)
435{
436 usb_iface_t *usb_iface = (usb_iface_t *) iface;
437
438 if (!usb_iface->unregister_endpoint) {
439 async_answer_0(callid, ENOTSUP);
440 return;
441 }
442
443 usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG1(*call);
444 usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG2(*call);
445
446 int rc = usb_iface->unregister_endpoint(fun, endpoint, direction);
447
448 async_answer_0(callid, rc);
449}
450
451typedef struct {
452 ipc_callid_t caller;
453 ipc_callid_t data_caller;
454 void *buffer;
455} async_transaction_t;
456
457static void async_transaction_destroy(async_transaction_t *trans)
458{
459 if (trans == NULL) {
460 return;
461 }
462 if (trans->buffer != NULL) {
463 free(trans->buffer);
464 }
465
466 free(trans);
467}
468
469static async_transaction_t *async_transaction_create(ipc_callid_t caller)
470{
471 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
472 if (trans == NULL) {
473 return NULL;
474 }
475
476 trans->caller = caller;
477 trans->data_caller = 0;
478 trans->buffer = NULL;
479
480 return trans;
481}
482
483static void callback_out(int outcome, void *arg)
484{
485 async_transaction_t *trans = arg;
486
487 async_answer_0(trans->caller, outcome);
488
489 async_transaction_destroy(trans);
490}
491
492static void callback_in(int outcome, size_t actual_size, void *arg)
493{
494 async_transaction_t *trans = (async_transaction_t *)arg;
495
496 if (outcome != EOK) {
497 async_answer_0(trans->caller, outcome);
498 if (trans->data_caller) {
499 async_answer_0(trans->data_caller, EINTR);
500 }
501 async_transaction_destroy(trans);
502 return;
503 }
504
505 if (trans->data_caller) {
506 async_data_read_finalize(trans->data_caller,
507 trans->buffer, actual_size);
508 }
509
510 async_answer_0(trans->caller, EOK);
511
512 async_transaction_destroy(trans);
513}
514
515void remote_usb_read(
516 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
517{
518 assert(fun);
519 assert(iface);
520 assert(call);
521
522 const usb_iface_t *usb_iface = iface;
523
524 if (!usb_iface->read) {
525 async_answer_0(callid, ENOTSUP);
526 return;
527 }
528
529 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
530 const uint64_t setup =
531 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
532 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
533
534 async_transaction_t *trans = async_transaction_create(callid);
535 if (trans == NULL) {
536 async_answer_0(callid, ENOMEM);
537 return;
538 }
539
540 size_t size = 0;
541 if (!async_data_read_receive(&trans->data_caller, &size)) {
542 async_answer_0(callid, EPARTY);
543 return;
544 }
545
546 trans->buffer = malloc(size);
547 if (trans->buffer == NULL) {
548 async_answer_0(trans->data_caller, ENOMEM);
549 async_answer_0(callid, ENOMEM);
550 async_transaction_destroy(trans);
551 }
552
553 const int rc = usb_iface->read(
554 fun, ep, setup, trans->buffer, size, callback_in, trans);
555
556 if (rc != EOK) {
557 async_answer_0(trans->data_caller, rc);
558 async_answer_0(callid, rc);
559 async_transaction_destroy(trans);
560 }
561}
562
563void remote_usb_write(
564 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
565{
566 assert(fun);
567 assert(iface);
568 assert(call);
569
570 const usb_iface_t *usb_iface = iface;
571
572 if (!usb_iface->write) {
573 async_answer_0(callid, ENOTSUP);
574 return;
575 }
576
577 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
578 const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
579 const uint64_t setup =
580 ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
581 (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
582
583 async_transaction_t *trans = async_transaction_create(callid);
584 if (trans == NULL) {
585 async_answer_0(callid, ENOMEM);
586 return;
587 }
588
589 size_t size = 0;
590 if (data_buffer_len > 0) {
591 const int rc = async_data_write_accept(&trans->buffer, false,
592 1, data_buffer_len, 0, &size);
593
594 if (rc != EOK) {
595 async_answer_0(callid, rc);
596 async_transaction_destroy(trans);
597 return;
598 }
599 }
600
601 const int rc = usb_iface->write(
602 fun, ep, setup, trans->buffer, size, callback_out, trans);
603
604 if (rc != EOK) {
605 async_answer_0(callid, rc);
606 async_transaction_destroy(trans);
607 }
608}
609/**
610 * @}
611 */
Note: See TracBrowser for help on using the repository browser.