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

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

libdrv: usbhc_iface now shares memory instead of sending data

Two more things that come along:
1) The two callbacks (read, write) were joined. As they were joined in

the only implementation (ddf_helpers) anyway, and now the code is
almost the same, we took the opportunity and joined them. The bad
thing about it is that there are not enough IPC arguments to
transfer the direction, so we still have to use two "interface IPC
methods".

2) The copying is still done by the former methods (usbhc_read and

usbhc_write) to ensure the page alignment, so this method just
moves the burden of copying from kernel to the caller (which is why
this is actually a performance regression).

But, the two sides can now resolve their issues separately. The caller
can prepare the memory area in advance, and HC can use the memory
directly if it can.

  • Property mode set to 100644
File size: 13.7 KB
RevLine 
[41df71f9]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>
[5595841]41#include <as.h>
[41df71f9]42
43#include "usbhc_iface.h"
44#include "ddf/driver.h"
45
46
47typedef enum {
[4603b35]48 IPC_M_USB_DEFAULT_ADDRESS_RESERVATION,
[41df71f9]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 */
[eeca8a6]61int usbhc_reserve_default_address(async_exch_t *exch)
[41df71f9]62{
63 if (!exch)
64 return EBADMEM;
[4603b35]65 return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION, true);
[41df71f9]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;
[4603b35]78 return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION, false);
[41df71f9]79}
80
[eeca8a6]81/**
82 * Trigger USB device enumeration
[41df71f9]83 *
[eeca8a6]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
[41df71f9]87 *
88 * @return Error code.
89 */
[eeca8a6]90int usbhc_device_enumerate(async_exch_t *exch, unsigned port, usb_speed_t speed)
[41df71f9]91{
92 if (!exch)
93 return EBADMEM;
[eeca8a6]94 const int ret = async_req_3_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
95 IPC_M_USB_DEVICE_ENUMERATE, port, speed);
[41df71f9]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 */
[5a6cc679]107errno_t usbhc_device_remove(async_exch_t *exch, unsigned port)
[41df71f9]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
[5a6cc679]115errno_t usbhc_register_endpoint(async_exch_t *exch, usb_pipe_desc_t *pipe_desc,
[9efad54]116 const usb_endpoint_descriptors_t *desc)
[41df71f9]117{
118 if (!exch)
119 return EBADMEM;
120
[9efad54]121 if (!desc)
122 return EINVAL;
123
[41df71f9]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
[9efad54]131 int ret = async_data_write_start(exch, desc, sizeof(*desc));
[41df71f9]132 if (ret != EOK) {
133 async_forget(opening_request);
134 return ret;
135 }
136
137 /* Wait for the answer. */
[132ab5d1]138 int opening_request_rc;
[41df71f9]139 async_wait_for(opening_request, &opening_request_rc);
140
[9efad54]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;
[41df71f9]154}
155
[5a6cc679]156errno_t usbhc_unregister_endpoint(async_exch_t *exch, const usb_pipe_desc_t *pipe_desc)
[41df71f9]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
[9efad54]168 const int ret = async_data_write_start(exch, pipe_desc, sizeof(*pipe_desc));
[41df71f9]169 if (ret != EOK) {
170 async_forget(opening_request);
171 return ret;
172 }
173
174 /* Wait for the answer. */
[132ab5d1]175 int opening_request_rc;
[41df71f9]176 async_wait_for(opening_request, &opening_request_rc);
177
178 return (int) opening_request_rc;
179}
180
[5595841]181/**
182 * Issue a USB transfer with a data contained in memory area. That area is
183 * temporarily shared with the HC.
184 */
185errno_t usbhc_transfer(async_exch_t *exch, usb_endpoint_t endpoint,
186 usb_direction_t dir, uint64_t setup, void *area, size_t size,
187 size_t *transferred)
[41df71f9]188{
[5595841]189 if (transferred)
190 *transferred = 0;
191
[41df71f9]192 if (!exch)
193 return EBADMEM;
194
195 if (size == 0 && setup == 0)
196 return EOK;
197
[5595841]198 sysarg_t method = (dir == USB_DIRECTION_IN)
199 ? IPC_M_USB_READ : IPC_M_USB_WRITE;
[41df71f9]200
[5595841]201 ipc_call_t call;
[41df71f9]202
203
[5595841]204 aid_t opening_request = async_send_5(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
205 method, endpoint, size, (setup & UINT32_MAX), (setup >> 32), &call);
[41df71f9]206
[5595841]207 if (opening_request == 0)
[41df71f9]208 return ENOMEM;
209
210 /* Send the data if any. */
211 if (size > 0) {
[5595841]212 unsigned flags = (dir == USB_DIRECTION_IN)
213 ? AS_AREA_WRITE : AS_AREA_READ;
214
215 const errno_t ret = async_share_out_start(exch, area, flags);
[41df71f9]216 if (ret != EOK) {
217 async_forget(opening_request);
218 return ret;
219 }
220 }
221
222 /* Wait for the answer. */
[5a6cc679]223 errno_t opening_request_rc;
[41df71f9]224 async_wait_for(opening_request, &opening_request_rc);
225
[5595841]226 if (transferred)
227 *transferred = IPC_GET_ARG1(call);
228
[5a6cc679]229 return (errno_t) opening_request_rc;
[41df71f9]230}
231
[5595841]232errno_t usbhc_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
233 void *data, size_t size, size_t *rec_size)
234{
235 if (size == 0)
236 return usbhc_transfer(exch, endpoint, USB_DIRECTION_IN,
237 setup, NULL, 0, NULL);
238
239 /* Prepare an area to read */
240 void *area = as_area_create(AS_AREA_ANY, size,
241 AS_AREA_READ | AS_AREA_WRITE, AS_AREA_UNPAGED);
242 if (!area)
243 return ENOMEM;
244
245 const errno_t err = usbhc_transfer(exch, endpoint, USB_DIRECTION_IN,
246 setup, area, size, rec_size);
247 if (err == EOK)
248 memcpy(data, area, *rec_size);
249
250 as_area_destroy(area);
251 return err;
252}
253
254errno_t usbhc_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
255 const void *data, size_t size)
256{
257 if (size == 0)
258 return usbhc_transfer(exch, endpoint, USB_DIRECTION_OUT,
259 setup, NULL, 0, NULL);
260
261 /* Prepare an area to read */
262 void *area = as_area_create(AS_AREA_ANY, size,
263 AS_AREA_READ | AS_AREA_WRITE, AS_AREA_UNPAGED);
264 if (!area)
265 return ENOMEM;
266
267 memcpy(area, data, size);
268 const errno_t err = usbhc_transfer(exch, endpoint, USB_DIRECTION_OUT,
269 setup, area, size, NULL);
270 as_area_destroy(area);
271 return err;
272}
273
[4603b35]274static void remote_usbhc_default_address_reservation(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
[41df71f9]275static void remote_usbhc_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
276static void remote_usbhc_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
277static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
278static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
[5595841]279static void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
[41df71f9]280
281/** Remote USB interface operations. */
282static const remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
[4603b35]283 [IPC_M_USB_DEFAULT_ADDRESS_RESERVATION] = remote_usbhc_default_address_reservation,
[41df71f9]284 [IPC_M_USB_DEVICE_ENUMERATE] = remote_usbhc_device_enumerate,
285 [IPC_M_USB_DEVICE_REMOVE] = remote_usbhc_device_remove,
286 [IPC_M_USB_REGISTER_ENDPOINT] = remote_usbhc_register_endpoint,
287 [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usbhc_unregister_endpoint,
[5595841]288 [IPC_M_USB_READ] = remote_usbhc_transfer,
289 [IPC_M_USB_WRITE] = remote_usbhc_transfer,
[41df71f9]290};
291
292/** Remote USB interface structure.
293 */
294const remote_iface_t remote_usbhc_iface = {
295 .method_count = ARRAY_SIZE(remote_usbhc_iface_ops),
296 .methods = remote_usbhc_iface_ops,
297};
298
299typedef struct {
300 ipc_callid_t caller;
301 void *buffer;
302} async_transaction_t;
303
[4603b35]304void remote_usbhc_default_address_reservation(ddf_fun_t *fun, void *iface,
[41df71f9]305 ipc_callid_t callid, ipc_call_t *call)
306{
307 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
308
[4603b35]309 if (usbhc_iface->default_address_reservation == NULL) {
[41df71f9]310 async_answer_0(callid, ENOTSUP);
311 return;
312 }
313
[4603b35]314 const bool reserve = IPC_GET_ARG2(*call);
315 const int ret = usbhc_iface->default_address_reservation(fun, reserve);
[41df71f9]316 async_answer_0(callid, ret);
317}
318
319
320static void remote_usbhc_device_enumerate(ddf_fun_t *fun, void *iface,
321 ipc_callid_t callid, ipc_call_t *call)
322{
323 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
324
325 if (usbhc_iface->device_enumerate == NULL) {
326 async_answer_0(callid, ENOTSUP);
327 return;
328 }
329
330 const unsigned port = DEV_IPC_GET_ARG1(*call);
[eeca8a6]331 usb_speed_t speed = DEV_IPC_GET_ARG2(*call);
332 const int ret = usbhc_iface->device_enumerate(fun, port, speed);
[41df71f9]333 async_answer_0(callid, ret);
334}
335
336static void remote_usbhc_device_remove(ddf_fun_t *fun, void *iface,
337 ipc_callid_t callid, ipc_call_t *call)
338{
339 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
340
341 if (usbhc_iface->device_remove == NULL) {
342 async_answer_0(callid, ENOTSUP);
343 return;
344 }
345
346 const unsigned port = DEV_IPC_GET_ARG1(*call);
347 const int ret = usbhc_iface->device_remove(fun, port);
348 async_answer_0(callid, ret);
349}
350
351static void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
352 ipc_callid_t callid, ipc_call_t *call)
353{
354 assert(fun);
355 assert(iface);
356 assert(call);
357
358 const usbhc_iface_t *usbhc_iface = iface;
359
360 if (!usbhc_iface->register_endpoint) {
361 async_answer_0(callid, ENOTSUP);
362 return;
363 }
364
[9efad54]365 usb_endpoint_descriptors_t ep_desc;
366 ipc_callid_t data_callid;
367 size_t len;
[41df71f9]368
[9efad54]369 if (!async_data_write_receive(&data_callid, &len)
370 || len != sizeof(ep_desc)) {
371 async_answer_0(callid, EINVAL);
[41df71f9]372 return;
373 }
[9efad54]374 async_data_write_finalize(data_callid, &ep_desc, sizeof(ep_desc));
[41df71f9]375
[9efad54]376 usb_pipe_desc_t pipe_desc;
[41df71f9]377
[9efad54]378 const int rc = usbhc_iface->register_endpoint(fun, &pipe_desc, &ep_desc);
[41df71f9]379 async_answer_0(callid, rc);
[9efad54]380
381 if (!async_data_read_receive(&data_callid, &len)
382 || len != sizeof(pipe_desc)) {
383 return;
384 }
385 async_data_read_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
[41df71f9]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
[9efad54]402 usb_pipe_desc_t pipe_desc;
403 ipc_callid_t data_callid;
404 size_t len;
[41df71f9]405
[9efad54]406 if (!async_data_write_receive(&data_callid, &len)
407 || len != sizeof(pipe_desc)) {
408 async_answer_0(callid, EINVAL);
[41df71f9]409 return;
410 }
[9efad54]411 async_data_write_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
[41df71f9]412
[9efad54]413 const int rc = usbhc_iface->unregister_endpoint(fun, &pipe_desc);
[41df71f9]414 async_answer_0(callid, rc);
415}
416
417static void async_transaction_destroy(async_transaction_t *trans)
418{
419 if (trans == NULL) {
420 return;
421 }
422 if (trans->buffer != NULL) {
[5595841]423 as_area_destroy(trans->buffer);
[41df71f9]424 }
425
426 free(trans);
427}
428
429static async_transaction_t *async_transaction_create(ipc_callid_t caller)
430{
431 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
432 if (trans == NULL) {
433 return NULL;
434 }
435
436 trans->caller = caller;
437 trans->buffer = NULL;
438
439 return trans;
440}
441
[5595841]442static errno_t transfer_finished(void *arg, int error, size_t transferred_size)
[41df71f9]443{
[272f46f8]444 async_transaction_t *trans = arg;
[5595841]445 const errno_t err = async_answer_1(trans->caller, error, transferred_size);
[41df71f9]446 async_transaction_destroy(trans);
447 return err;
448}
449
[5595841]450static errno_t receive_memory_buffer(async_transaction_t *trans,
451 size_t required_size, unsigned required_flags)
[41df71f9]452{
[5595841]453 assert(trans);
454 assert(required_size > 0);
[41df71f9]455
[5595841]456 errno_t err;
457 ipc_callid_t data_callid;
458 size_t size;
459 unsigned flags;
[41df71f9]460
[5595841]461 if (!async_share_out_receive(&data_callid, &size, &flags))
462 return EPARTY;
[41df71f9]463
[5595841]464 if (size < required_size || (flags & required_flags) != required_flags) {
465 async_answer_0(data_callid, EINVAL);
466 return EINVAL;
[41df71f9]467 }
468
[5595841]469 if ((err = async_share_out_finalize(data_callid, &trans->buffer)))
470 return err;
[41df71f9]471
[5595841]472 return EOK;
[41df71f9]473}
474
[5595841]475void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
[41df71f9]476{
477 assert(fun);
478 assert(iface);
479 assert(call);
480
481 const usbhc_iface_t *usbhc_iface = iface;
482
[5595841]483 if (!usbhc_iface->transfer) {
[41df71f9]484 async_answer_0(callid, ENOTSUP);
485 return;
486 }
487
[5595841]488 const sysarg_t method = IPC_GET_ARG1(*call);
489 const usb_direction_t dir =
490 method == IPC_M_USB_READ ? USB_DIRECTION_IN : USB_DIRECTION_OUT;
491
492 const usb_endpoint_t ep = IPC_GET_ARG2(*call);
493 const size_t size = IPC_GET_ARG3(*call);
494 const uint64_t setup = ((uint64_t)IPC_GET_ARG4(*call)) |
495 (((uint64_t)IPC_GET_ARG5(*call)) << 32);
[41df71f9]496
497 async_transaction_t *trans = async_transaction_create(callid);
498 if (trans == NULL) {
499 async_answer_0(callid, ENOMEM);
500 return;
501 }
502
[5595841]503 if (size > 0) {
504 const unsigned required_flags = (dir == USB_DIRECTION_IN)
505 ? AS_AREA_WRITE : AS_AREA_READ;
[41df71f9]506
[5595841]507 const errno_t rc = receive_memory_buffer(trans, size, required_flags);
[41df71f9]508 if (rc != EOK) {
509 async_transaction_destroy(trans);
[5595841]510 async_answer_0(callid, rc);
[41df71f9]511 return;
512 }
513 }
514
515 const usb_target_t target = {{
516 /* .address is initialized by write itself */
517 .endpoint = ep,
[0f803831]518 /* streams are not given by the API call yet */
519 .stream = 0,
[41df71f9]520 }};
521
[5595841]522 const errno_t rc = usbhc_iface->transfer(fun, target, dir, setup,
523 trans->buffer, size, &transfer_finished, trans);
[41df71f9]524
525 if (rc != EOK) {
526 async_answer_0(callid, rc);
527 async_transaction_destroy(trans);
528 }
529}
[5595841]530
[41df71f9]531/**
532 * @}
533 */
Note: See TracBrowser for help on using the repository browser.