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

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

usb: speed moved from default address reservation to enumeration callback

  • Property mode set to 100644
File size: 14.9 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 * @return Error code.
60 */
61int usbhc_reserve_default_address(async_exch_t *exch)
62{
63 if (!exch)
64 return EBADMEM;
65 return async_req_1_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_RESERVE_DEFAULT_ADDRESS);
66}
67
68/** Release default USB address.
69 *
70 * @param[in] exch IPC communication exchange
71 *
72 * @return Error code.
73 */
74int usbhc_release_default_address(async_exch_t *exch)
75{
76 if (!exch)
77 return EBADMEM;
78 return async_req_1_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
79}
80
81/**
82 * Trigger USB device enumeration
83 *
84 * @param[in] exch IPC communication exchange
85 * @param[in] port Port number at which the device is attached
86 * @param[in] speed Communication speed of the newly attached device
87 *
88 * @return Error code.
89 */
90int usbhc_device_enumerate(async_exch_t *exch, unsigned port, usb_speed_t speed)
91{
92 if (!exch)
93 return EBADMEM;
94 const int ret = async_req_3_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
95 IPC_M_USB_DEVICE_ENUMERATE, port, speed);
96 return ret;
97}
98
99/** Trigger USB device enumeration
100 *
101 * @param[in] exch IPC communication exchange
102 * @param[in] handle Identifier of the device
103 *
104 * @return Error code.
105 *
106 */
107int usbhc_device_remove(async_exch_t *exch, unsigned port)
108{
109 if (!exch)
110 return EBADMEM;
111 return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
112 IPC_M_USB_DEVICE_REMOVE, port);
113}
114
115int usbhc_register_endpoint(async_exch_t *exch, usb_pipe_desc_t *pipe_desc,
116 const usb_endpoint_descriptors_t *desc)
117{
118 if (!exch)
119 return EBADMEM;
120
121 if (!desc)
122 return EINVAL;
123
124 aid_t opening_request = async_send_1(exch,
125 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_REGISTER_ENDPOINT, NULL);
126
127 if (opening_request == 0) {
128 return ENOMEM;
129 }
130
131 int ret = async_data_write_start(exch, desc, sizeof(*desc));
132 if (ret != EOK) {
133 async_forget(opening_request);
134 return ret;
135 }
136
137 /* Wait for the answer. */
138 sysarg_t opening_request_rc;
139 async_wait_for(opening_request, &opening_request_rc);
140
141 if (opening_request_rc)
142 return (int) opening_request_rc;
143
144 usb_pipe_desc_t dest;
145 ret = async_data_read_start(exch, &dest, sizeof(dest));
146 if (ret != EOK) {
147 return ret;
148 }
149
150 if (pipe_desc)
151 *pipe_desc = dest;
152
153 return EOK;
154}
155
156int usbhc_unregister_endpoint(async_exch_t *exch, const usb_pipe_desc_t *pipe_desc)
157{
158 if (!exch)
159 return EBADMEM;
160
161 aid_t opening_request = async_send_1(exch,
162 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_UNREGISTER_ENDPOINT, NULL);
163
164 if (opening_request == 0) {
165 return ENOMEM;
166 }
167
168 const int ret = async_data_write_start(exch, pipe_desc, sizeof(*pipe_desc));
169 if (ret != EOK) {
170 async_forget(opening_request);
171 return ret;
172 }
173
174 /* Wait for the answer. */
175 sysarg_t opening_request_rc;
176 async_wait_for(opening_request, &opening_request_rc);
177
178 return (int) opening_request_rc;
179}
180
181int usbhc_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
182 void *data, size_t size, size_t *rec_size)
183{
184 if (!exch)
185 return EBADMEM;
186
187 if (size == 0 && setup == 0)
188 return EOK;
189
190 /* Make call identifying target USB device and type of transfer. */
191 aid_t opening_request = async_send_4(exch,
192 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_READ, endpoint,
193 (setup & UINT32_MAX), (setup >> 32), NULL);
194
195 if (opening_request == 0) {
196 return ENOMEM;
197 }
198
199 /* Retrieve the data. */
200 ipc_call_t data_request_call;
201 aid_t data_request =
202 async_data_read(exch, data, size, &data_request_call);
203
204 if (data_request == 0) {
205 // FIXME: How to let the other side know that we want to abort?
206 async_forget(opening_request);
207 return ENOMEM;
208 }
209
210 /* Wait for the answer. */
211 sysarg_t data_request_rc;
212 sysarg_t opening_request_rc;
213 async_wait_for(data_request, &data_request_rc);
214 async_wait_for(opening_request, &opening_request_rc);
215
216 if (data_request_rc != EOK) {
217 /* Prefer the return code of the opening request. */
218 if (opening_request_rc != EOK) {
219 return (int) opening_request_rc;
220 } else {
221 return (int) data_request_rc;
222 }
223 }
224 if (opening_request_rc != EOK) {
225 return (int) opening_request_rc;
226 }
227
228 *rec_size = IPC_GET_ARG2(data_request_call);
229 return EOK;
230}
231
232int usbhc_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
233 const void *data, size_t size)
234{
235 if (!exch)
236 return EBADMEM;
237
238 if (size == 0 && setup == 0)
239 return EOK;
240
241 aid_t opening_request = async_send_5(exch,
242 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
243 (setup & UINT32_MAX), (setup >> 32), NULL);
244
245 if (opening_request == 0) {
246 return ENOMEM;
247 }
248
249 /* Send the data if any. */
250 if (size > 0) {
251 const int ret = async_data_write_start(exch, data, size);
252 if (ret != EOK) {
253 async_forget(opening_request);
254 return ret;
255 }
256 }
257
258 /* Wait for the answer. */
259 sysarg_t opening_request_rc;
260 async_wait_for(opening_request, &opening_request_rc);
261
262 return (int) opening_request_rc;
263}
264
265static void remote_usbhc_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
266static void remote_usbhc_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
267static void remote_usbhc_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
268static void remote_usbhc_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
269static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
270static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
271static void remote_usbhc_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
272static void remote_usbhc_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
273
274/** Remote USB interface operations. */
275static const remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
276 [IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usbhc_reserve_default_address,
277 [IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usbhc_release_default_address,
278 [IPC_M_USB_DEVICE_ENUMERATE] = remote_usbhc_device_enumerate,
279 [IPC_M_USB_DEVICE_REMOVE] = remote_usbhc_device_remove,
280 [IPC_M_USB_REGISTER_ENDPOINT] = remote_usbhc_register_endpoint,
281 [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usbhc_unregister_endpoint,
282 [IPC_M_USB_READ] = remote_usbhc_read,
283 [IPC_M_USB_WRITE] = remote_usbhc_write,
284};
285
286/** Remote USB interface structure.
287 */
288const remote_iface_t remote_usbhc_iface = {
289 .method_count = ARRAY_SIZE(remote_usbhc_iface_ops),
290 .methods = remote_usbhc_iface_ops,
291};
292
293typedef struct {
294 ipc_callid_t caller;
295 ipc_callid_t data_caller;
296 void *buffer;
297} async_transaction_t;
298
299void remote_usbhc_reserve_default_address(ddf_fun_t *fun, void *iface,
300 ipc_callid_t callid, ipc_call_t *call)
301{
302 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
303
304 if (usbhc_iface->reserve_default_address == NULL) {
305 async_answer_0(callid, ENOTSUP);
306 return;
307 }
308
309 const int ret = usbhc_iface->reserve_default_address(fun);
310 async_answer_0(callid, ret);
311}
312
313void remote_usbhc_release_default_address(ddf_fun_t *fun, void *iface,
314 ipc_callid_t callid, ipc_call_t *call)
315{
316 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
317
318 if (usbhc_iface->release_default_address == NULL) {
319 async_answer_0(callid, ENOTSUP);
320 return;
321 }
322
323 const int ret = usbhc_iface->release_default_address(fun);
324 async_answer_0(callid, ret);
325}
326
327static void remote_usbhc_device_enumerate(ddf_fun_t *fun, void *iface,
328 ipc_callid_t callid, ipc_call_t *call)
329{
330 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
331
332 if (usbhc_iface->device_enumerate == NULL) {
333 async_answer_0(callid, ENOTSUP);
334 return;
335 }
336
337 const unsigned port = DEV_IPC_GET_ARG1(*call);
338 usb_speed_t speed = DEV_IPC_GET_ARG2(*call);
339 const int ret = usbhc_iface->device_enumerate(fun, port, speed);
340 async_answer_0(callid, ret);
341}
342
343static void remote_usbhc_device_remove(ddf_fun_t *fun, void *iface,
344 ipc_callid_t callid, ipc_call_t *call)
345{
346 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
347
348 if (usbhc_iface->device_remove == NULL) {
349 async_answer_0(callid, ENOTSUP);
350 return;
351 }
352
353 const unsigned port = DEV_IPC_GET_ARG1(*call);
354 const int ret = usbhc_iface->device_remove(fun, port);
355 async_answer_0(callid, ret);
356}
357
358static void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
359 ipc_callid_t callid, ipc_call_t *call)
360{
361 assert(fun);
362 assert(iface);
363 assert(call);
364
365 const usbhc_iface_t *usbhc_iface = iface;
366
367 if (!usbhc_iface->register_endpoint) {
368 async_answer_0(callid, ENOTSUP);
369 return;
370 }
371
372 usb_endpoint_descriptors_t ep_desc;
373 ipc_callid_t data_callid;
374 size_t len;
375
376 if (!async_data_write_receive(&data_callid, &len)
377 || len != sizeof(ep_desc)) {
378 async_answer_0(callid, EINVAL);
379 return;
380 }
381 async_data_write_finalize(data_callid, &ep_desc, sizeof(ep_desc));
382
383 usb_pipe_desc_t pipe_desc;
384
385 const int rc = usbhc_iface->register_endpoint(fun, &pipe_desc, &ep_desc);
386 async_answer_0(callid, rc);
387
388 if (!async_data_read_receive(&data_callid, &len)
389 || len != sizeof(pipe_desc)) {
390 return;
391 }
392 async_data_read_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
393}
394
395static void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
396 ipc_callid_t callid, ipc_call_t *call)
397{
398 assert(fun);
399 assert(iface);
400 assert(call);
401
402 const usbhc_iface_t *usbhc_iface = iface;
403
404 if (!usbhc_iface->unregister_endpoint) {
405 async_answer_0(callid, ENOTSUP);
406 return;
407 }
408
409 usb_pipe_desc_t pipe_desc;
410 ipc_callid_t data_callid;
411 size_t len;
412
413 if (!async_data_write_receive(&data_callid, &len)
414 || len != sizeof(pipe_desc)) {
415 async_answer_0(callid, EINVAL);
416 return;
417 }
418 async_data_write_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
419
420 const int rc = usbhc_iface->unregister_endpoint(fun, &pipe_desc);
421 async_answer_0(callid, rc);
422}
423
424static void async_transaction_destroy(async_transaction_t *trans)
425{
426 if (trans == NULL) {
427 return;
428 }
429 if (trans->buffer != NULL) {
430 free(trans->buffer);
431 }
432
433 free(trans);
434}
435
436static async_transaction_t *async_transaction_create(ipc_callid_t caller)
437{
438 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
439 if (trans == NULL) {
440 return NULL;
441 }
442
443 trans->caller = caller;
444 trans->data_caller = 0;
445 trans->buffer = NULL;
446
447 return trans;
448}
449
450static int callback_out(void *arg, int error, size_t transfered_size)
451{
452 async_transaction_t *trans = arg;
453
454 const int err = async_answer_0(trans->caller, error);
455
456 async_transaction_destroy(trans);
457
458 return err;
459}
460
461static int callback_in(void *arg, int error, size_t transfered_size)
462{
463 async_transaction_t *trans = arg;
464
465 if (trans->data_caller) {
466 if (error == EOK) {
467 error = async_data_read_finalize(trans->data_caller,
468 trans->buffer, transfered_size);
469 } else {
470 async_answer_0(trans->data_caller, EINTR);
471 }
472 }
473
474 const int err = async_answer_0(trans->caller, error);
475 async_transaction_destroy(trans);
476 return err;
477}
478
479void remote_usbhc_read(
480 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
481{
482 assert(fun);
483 assert(iface);
484 assert(call);
485
486 const usbhc_iface_t *usbhc_iface = iface;
487
488 if (!usbhc_iface->read) {
489 async_answer_0(callid, ENOTSUP);
490 return;
491 }
492
493 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
494 const uint64_t setup =
495 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
496 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
497
498 async_transaction_t *trans = async_transaction_create(callid);
499 if (trans == NULL) {
500 async_answer_0(callid, ENOMEM);
501 return;
502 }
503
504 size_t size = 0;
505 if (!async_data_read_receive(&trans->data_caller, &size)) {
506 async_answer_0(callid, EPARTY);
507 async_transaction_destroy(trans);
508 return;
509 }
510
511 trans->buffer = malloc(size);
512 if (trans->buffer == NULL) {
513 async_answer_0(trans->data_caller, ENOMEM);
514 async_answer_0(callid, ENOMEM);
515 async_transaction_destroy(trans);
516 return;
517 }
518
519 const usb_target_t target = {{
520 /* .address is initialized by read itself */
521 .endpoint = ep,
522 }};
523
524 const int rc = usbhc_iface->read(
525 fun, target, setup, trans->buffer, size, callback_in, trans);
526
527 if (rc != EOK) {
528 async_answer_0(trans->data_caller, rc);
529 async_answer_0(callid, rc);
530 async_transaction_destroy(trans);
531 }
532}
533
534void remote_usbhc_write(
535 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
536{
537 assert(fun);
538 assert(iface);
539 assert(call);
540
541 const usbhc_iface_t *usbhc_iface = iface;
542
543 if (!usbhc_iface->write) {
544 async_answer_0(callid, ENOTSUP);
545 return;
546 }
547
548 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
549 const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
550 const uint64_t setup =
551 ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
552 (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
553
554 async_transaction_t *trans = async_transaction_create(callid);
555 if (trans == NULL) {
556 async_answer_0(callid, ENOMEM);
557 return;
558 }
559
560 size_t size = 0;
561 if (data_buffer_len > 0) {
562 const int rc = async_data_write_accept(&trans->buffer, false,
563 1, data_buffer_len, 0, &size);
564
565 if (rc != EOK) {
566 async_answer_0(callid, rc);
567 async_transaction_destroy(trans);
568 return;
569 }
570 }
571
572 const usb_target_t target = {{
573 /* .address is initialized by write itself */
574 .endpoint = ep,
575 }};
576
577 const int rc = usbhc_iface->write(
578 fun, target, setup, trans->buffer, size, callback_out, trans);
579
580 if (rc != EOK) {
581 async_answer_0(callid, rc);
582 async_transaction_destroy(trans);
583 }
584}
585/**
586 * @}
587 */
Note: See TracBrowser for help on using the repository browser.