source: mainline/uspace/lib/drv/generic/remote_usbhc.c@ 6b433a8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6b433a8 was f9d0a86, checked in by Aearsis <Hlavaty.Ondrej@…>, 8 years ago

Merge tag '0.7.1'

The merge wasn't clean, because of changes in build system. The most
significant change was partial revert of usbhc callback refactoring,
which now does not take usb transfer batch, but few named fields again.

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