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

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

usb: move endpoint descriptor parsing to HC

This better separates responsibilities. Now the device driver does not
care about the contents of an endpoint descriptor, and HC can parse the
values according to device's actual speed.

Currently, it is device driver's responsibility to fetch endpoint
descriptors, map them and register pipes - sending the endpoint
descriptor back to HC. HC then parses it, and fills the pipe
description, which then sends back to device driver. We shall probably
fetch the endpoint descriptor from inside the HC (also fixing the USB
spec violation of communication with EP0).

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