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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3be9d10 was 3be9d10, checked in by Jakub Jermar <jakub@…>, 7 years ago

Get rid of ipc_callid_t

  • Property mode set to 100644
File size: 13.2 KB
RevLine 
[41df71f9]1/*
2 * Copyright (c) 2010 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
[e0a5d4c]4 * Copyright (c) 2018 Ondrej Hlavaty, Petr Manek
[41df71f9]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,
[239eea41]53 IPC_M_USB_TRANSFER,
[41df71f9]54} usbhc_iface_funcs_t;
55
56/** Reserve default USB address.
57 * @param[in] exch IPC communication exchange
58 * @return Error code.
59 */
[5c69377]60errno_t usbhc_reserve_default_address(async_exch_t *exch)
[41df71f9]61{
62 if (!exch)
63 return EBADMEM;
[4603b35]64 return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION, true);
[41df71f9]65}
66
67/** Release default USB address.
68 *
69 * @param[in] exch IPC communication exchange
70 *
71 * @return Error code.
72 */
[5c69377]73errno_t usbhc_release_default_address(async_exch_t *exch)
[41df71f9]74{
75 if (!exch)
76 return EBADMEM;
[4603b35]77 return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION, false);
[41df71f9]78}
79
[eeca8a6]80/**
81 * Trigger USB device enumeration
[41df71f9]82 *
[eeca8a6]83 * @param[in] exch IPC communication exchange
84 * @param[in] port Port number at which the device is attached
85 * @param[in] speed Communication speed of the newly attached device
[41df71f9]86 *
87 * @return Error code.
88 */
[5c69377]89errno_t usbhc_device_enumerate(async_exch_t *exch, unsigned port, usb_speed_t speed)
[41df71f9]90{
91 if (!exch)
92 return EBADMEM;
[5c69377]93 const errno_t ret = async_req_3_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
[eeca8a6]94 IPC_M_USB_DEVICE_ENUMERATE, port, speed);
[41df71f9]95 return ret;
96}
97
98/** Trigger USB device enumeration
99 *
100 * @param[in] exch IPC communication exchange
101 * @param[in] handle Identifier of the device
102 *
103 * @return Error code.
104 *
105 */
[5a6cc679]106errno_t usbhc_device_remove(async_exch_t *exch, unsigned port)
[41df71f9]107{
108 if (!exch)
109 return EBADMEM;
110 return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
111 IPC_M_USB_DEVICE_REMOVE, port);
112}
113
[5a6cc679]114errno_t usbhc_register_endpoint(async_exch_t *exch, usb_pipe_desc_t *pipe_desc,
[9efad54]115 const usb_endpoint_descriptors_t *desc)
[41df71f9]116{
117 if (!exch)
118 return EBADMEM;
119
[9efad54]120 if (!desc)
121 return EINVAL;
122
[41df71f9]123 aid_t opening_request = async_send_1(exch,
124 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_REGISTER_ENDPOINT, NULL);
125
126 if (opening_request == 0) {
127 return ENOMEM;
128 }
129
[5c69377]130 errno_t ret = async_data_write_start(exch, desc, sizeof(*desc));
[41df71f9]131 if (ret != EOK) {
132 async_forget(opening_request);
133 return ret;
134 }
135
136 /* Wait for the answer. */
[5c69377]137 errno_t opening_request_rc;
[41df71f9]138 async_wait_for(opening_request, &opening_request_rc);
139
[9efad54]140 if (opening_request_rc)
[5c69377]141 return (errno_t) opening_request_rc;
[9efad54]142
143 usb_pipe_desc_t dest;
144 ret = async_data_read_start(exch, &dest, sizeof(dest));
145 if (ret != EOK) {
146 return ret;
147 }
148
149 if (pipe_desc)
150 *pipe_desc = dest;
151
152 return EOK;
[41df71f9]153}
154
[5a6cc679]155errno_t usbhc_unregister_endpoint(async_exch_t *exch, const usb_pipe_desc_t *pipe_desc)
[41df71f9]156{
157 if (!exch)
158 return EBADMEM;
159
160 aid_t opening_request = async_send_1(exch,
161 DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_UNREGISTER_ENDPOINT, NULL);
162
163 if (opening_request == 0) {
164 return ENOMEM;
165 }
166
[5c69377]167 const errno_t ret = async_data_write_start(exch, pipe_desc, sizeof(*pipe_desc));
[41df71f9]168 if (ret != EOK) {
169 async_forget(opening_request);
170 return ret;
171 }
172
173 /* Wait for the answer. */
[5c69377]174 errno_t opening_request_rc;
[41df71f9]175 async_wait_for(opening_request, &opening_request_rc);
176
[5c69377]177 return (errno_t) opening_request_rc;
[41df71f9]178}
179
[5595841]180/**
181 * Issue a USB transfer with a data contained in memory area. That area is
182 * temporarily shared with the HC.
183 */
[239eea41]184errno_t usbhc_transfer(async_exch_t *exch,
185 const usbhc_iface_transfer_request_t *req, size_t *transferred)
[41df71f9]186{
[5595841]187 if (transferred)
188 *transferred = 0;
189
[41df71f9]190 if (!exch)
191 return EBADMEM;
192
[5595841]193 ipc_call_t call;
[41df71f9]194
[239eea41]195 aid_t opening_request = async_send_1(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
196 IPC_M_USB_TRANSFER, &call);
[41df71f9]197
[5595841]198 if (opening_request == 0)
[41df71f9]199 return ENOMEM;
200
[239eea41]201 const errno_t ret = async_data_write_start(exch, req, sizeof(*req));
202 if (ret != EOK) {
203 async_forget(opening_request);
204 return ret;
205 }
206
207 /* Share the data, if any. */
208 if (req->size > 0) {
209 unsigned flags = (req->dir == USB_DIRECTION_IN)
[5595841]210 ? AS_AREA_WRITE : AS_AREA_READ;
211
[1d758fc]212 const errno_t ret = async_share_out_start(exch, req->buffer.virt, flags);
[41df71f9]213 if (ret != EOK) {
214 async_forget(opening_request);
215 return ret;
216 }
217 }
218
219 /* Wait for the answer. */
[5a6cc679]220 errno_t opening_request_rc;
[41df71f9]221 async_wait_for(opening_request, &opening_request_rc);
222
[5595841]223 if (transferred)
224 *transferred = IPC_GET_ARG1(call);
225
[5a6cc679]226 return (errno_t) opening_request_rc;
[41df71f9]227}
228
[3be9d10]229static void remote_usbhc_default_address_reservation(ddf_fun_t *, void *, cap_call_handle_t, ipc_call_t *);
230static void remote_usbhc_device_enumerate(ddf_fun_t *, void *, cap_call_handle_t, ipc_call_t *);
231static void remote_usbhc_device_remove(ddf_fun_t *, void *, cap_call_handle_t, ipc_call_t *);
232static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, cap_call_handle_t, ipc_call_t *);
233static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, cap_call_handle_t, ipc_call_t *);
234static void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, cap_call_handle_t callid, ipc_call_t *call);
[41df71f9]235
236/** Remote USB interface operations. */
237static const remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
[4603b35]238 [IPC_M_USB_DEFAULT_ADDRESS_RESERVATION] = remote_usbhc_default_address_reservation,
[41df71f9]239 [IPC_M_USB_DEVICE_ENUMERATE] = remote_usbhc_device_enumerate,
240 [IPC_M_USB_DEVICE_REMOVE] = remote_usbhc_device_remove,
241 [IPC_M_USB_REGISTER_ENDPOINT] = remote_usbhc_register_endpoint,
242 [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usbhc_unregister_endpoint,
[239eea41]243 [IPC_M_USB_TRANSFER] = remote_usbhc_transfer,
[41df71f9]244};
245
246/** Remote USB interface structure.
247 */
248const remote_iface_t remote_usbhc_iface = {
249 .method_count = ARRAY_SIZE(remote_usbhc_iface_ops),
250 .methods = remote_usbhc_iface_ops,
251};
252
253typedef struct {
[3be9d10]254 cap_call_handle_t caller;
[239eea41]255 usbhc_iface_transfer_request_t request;
[41df71f9]256} async_transaction_t;
257
[4603b35]258void remote_usbhc_default_address_reservation(ddf_fun_t *fun, void *iface,
[3be9d10]259 cap_call_handle_t callid, ipc_call_t *call)
[41df71f9]260{
261 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
262
[4603b35]263 if (usbhc_iface->default_address_reservation == NULL) {
[41df71f9]264 async_answer_0(callid, ENOTSUP);
265 return;
266 }
267
[4603b35]268 const bool reserve = IPC_GET_ARG2(*call);
[5c69377]269 const errno_t ret = usbhc_iface->default_address_reservation(fun, reserve);
[41df71f9]270 async_answer_0(callid, ret);
271}
272
273
274static void remote_usbhc_device_enumerate(ddf_fun_t *fun, void *iface,
[3be9d10]275 cap_call_handle_t callid, ipc_call_t *call)
[41df71f9]276{
277 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
278
279 if (usbhc_iface->device_enumerate == NULL) {
280 async_answer_0(callid, ENOTSUP);
281 return;
282 }
283
284 const unsigned port = DEV_IPC_GET_ARG1(*call);
[eeca8a6]285 usb_speed_t speed = DEV_IPC_GET_ARG2(*call);
[5c69377]286 const errno_t ret = usbhc_iface->device_enumerate(fun, port, speed);
[41df71f9]287 async_answer_0(callid, ret);
288}
289
290static void remote_usbhc_device_remove(ddf_fun_t *fun, void *iface,
[3be9d10]291 cap_call_handle_t callid, ipc_call_t *call)
[41df71f9]292{
293 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
294
295 if (usbhc_iface->device_remove == NULL) {
296 async_answer_0(callid, ENOTSUP);
297 return;
298 }
299
300 const unsigned port = DEV_IPC_GET_ARG1(*call);
[5c69377]301 const errno_t ret = usbhc_iface->device_remove(fun, port);
[41df71f9]302 async_answer_0(callid, ret);
303}
304
305static void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
[3be9d10]306 cap_call_handle_t callid, ipc_call_t *call)
[41df71f9]307{
308 assert(fun);
309 assert(iface);
310 assert(call);
311
312 const usbhc_iface_t *usbhc_iface = iface;
313
314 if (!usbhc_iface->register_endpoint) {
315 async_answer_0(callid, ENOTSUP);
316 return;
317 }
318
[9efad54]319 usb_endpoint_descriptors_t ep_desc;
[3be9d10]320 cap_call_handle_t data_callid;
[9efad54]321 size_t len;
[41df71f9]322
[9efad54]323 if (!async_data_write_receive(&data_callid, &len)
324 || len != sizeof(ep_desc)) {
325 async_answer_0(callid, EINVAL);
[41df71f9]326 return;
327 }
[9efad54]328 async_data_write_finalize(data_callid, &ep_desc, sizeof(ep_desc));
[41df71f9]329
[9efad54]330 usb_pipe_desc_t pipe_desc;
[41df71f9]331
[5c69377]332 const errno_t rc = usbhc_iface->register_endpoint(fun, &pipe_desc, &ep_desc);
[41df71f9]333 async_answer_0(callid, rc);
[9efad54]334
335 if (!async_data_read_receive(&data_callid, &len)
336 || len != sizeof(pipe_desc)) {
337 return;
338 }
339 async_data_read_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
[41df71f9]340}
341
342static void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
[3be9d10]343 cap_call_handle_t callid, ipc_call_t *call)
[41df71f9]344{
345 assert(fun);
346 assert(iface);
347 assert(call);
348
349 const usbhc_iface_t *usbhc_iface = iface;
350
351 if (!usbhc_iface->unregister_endpoint) {
352 async_answer_0(callid, ENOTSUP);
353 return;
354 }
355
[9efad54]356 usb_pipe_desc_t pipe_desc;
[3be9d10]357 cap_call_handle_t data_callid;
[9efad54]358 size_t len;
[41df71f9]359
[9efad54]360 if (!async_data_write_receive(&data_callid, &len)
361 || len != sizeof(pipe_desc)) {
362 async_answer_0(callid, EINVAL);
[41df71f9]363 return;
364 }
[9efad54]365 async_data_write_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
[41df71f9]366
[5c69377]367 const errno_t rc = usbhc_iface->unregister_endpoint(fun, &pipe_desc);
[41df71f9]368 async_answer_0(callid, rc);
369}
370
371static void async_transaction_destroy(async_transaction_t *trans)
372{
373 if (trans == NULL) {
374 return;
375 }
[1d758fc]376 if (trans->request.buffer.virt != NULL) {
377 as_area_destroy(trans->request.buffer.virt);
[41df71f9]378 }
379
380 free(trans);
381}
382
[3be9d10]383static async_transaction_t *async_transaction_create(cap_call_handle_t caller)
[41df71f9]384{
[239eea41]385 async_transaction_t *trans = calloc(1, sizeof(async_transaction_t));
[41df71f9]386
[239eea41]387 if (trans != NULL)
388 trans->caller = caller;
[41df71f9]389
390 return trans;
391}
392
[5c69377]393static errno_t transfer_finished(void *arg, errno_t error, size_t transferred_size)
[41df71f9]394{
[272f46f8]395 async_transaction_t *trans = arg;
[5595841]396 const errno_t err = async_answer_1(trans->caller, error, transferred_size);
[41df71f9]397 async_transaction_destroy(trans);
398 return err;
399}
400
[239eea41]401static errno_t receive_memory_buffer(async_transaction_t *trans)
[41df71f9]402{
[5595841]403 assert(trans);
[239eea41]404 assert(trans->request.size > 0);
405
406 const size_t required_size = trans->request.offset + trans->request.size;
407 const unsigned required_flags =
408 (trans->request.dir == USB_DIRECTION_IN)
409 ? AS_AREA_WRITE : AS_AREA_READ;
[41df71f9]410
[5595841]411 errno_t err;
[3be9d10]412 cap_call_handle_t data_callid;
[5595841]413 size_t size;
414 unsigned flags;
[41df71f9]415
[5595841]416 if (!async_share_out_receive(&data_callid, &size, &flags))
417 return EPARTY;
[41df71f9]418
[5595841]419 if (size < required_size || (flags & required_flags) != required_flags) {
420 async_answer_0(data_callid, EINVAL);
421 return EINVAL;
[41df71f9]422 }
423
[1d758fc]424 if ((err = async_share_out_finalize(data_callid, &trans->request.buffer.virt)))
[5595841]425 return err;
[41df71f9]426
[c21e6a5]427 /*
[1d758fc]428 * As we're going to get physical addresses of the mapping, we must make
429 * sure the memory is actually mapped. We must do it right now, because
430 * the area might be read-only or write-only, and we may be unsure
431 * later.
[c21e6a5]432 */
433 if (flags & AS_AREA_READ) {
434 char foo = 0;
[1d758fc]435 volatile const char *buf = trans->request.buffer.virt + trans->request.offset;
[c21e6a5]436 for (size_t i = 0; i < size; i += PAGE_SIZE)
437 foo += buf[i];
438 } else {
[1d758fc]439 volatile char *buf = trans->request.buffer.virt + trans->request.offset;
[c21e6a5]440 for (size_t i = 0; i < size; i += PAGE_SIZE)
441 buf[i] = 0xff;
442 }
443
[5595841]444 return EOK;
[41df71f9]445}
446
[3be9d10]447void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, cap_call_handle_t callid, ipc_call_t *call)
[41df71f9]448{
449 assert(fun);
450 assert(iface);
451 assert(call);
452
453 const usbhc_iface_t *usbhc_iface = iface;
454
[5595841]455 if (!usbhc_iface->transfer) {
[41df71f9]456 async_answer_0(callid, ENOTSUP);
457 return;
458 }
459
460 async_transaction_t *trans = async_transaction_create(callid);
461 if (trans == NULL) {
462 async_answer_0(callid, ENOMEM);
463 return;
464 }
465
[239eea41]466 errno_t err = EPARTY;
[41df71f9]467
[3be9d10]468 cap_call_handle_t data_callid;
[239eea41]469 size_t len;
470 if (!async_data_write_receive(&data_callid, &len)
471 || len != sizeof(trans->request)) {
472 async_answer_0(data_callid, EINVAL);
473 goto err;
[41df71f9]474 }
475
[239eea41]476 if ((err = async_data_write_finalize(data_callid,
477 &trans->request, sizeof(trans->request))))
478 goto err;
[41df71f9]479
[239eea41]480 if (trans->request.size > 0) {
481 if ((err = receive_memory_buffer(trans)))
482 goto err;
483 } else {
484 /* The value was valid on the other side, for us, its garbage. */
[1d758fc]485 trans->request.buffer.virt = NULL;
[41df71f9]486 }
[239eea41]487
488 if ((err = usbhc_iface->transfer(fun, &trans->request,
489 &transfer_finished, trans)))
490 goto err;
491
492 /* The call will be answered asynchronously by the callback. */
493 return;
494
495err:
496 async_answer_0(callid, err);
497 async_transaction_destroy(trans);
[41df71f9]498}
[5595841]499
[41df71f9]500/**
501 * @}
502 */
Note: See TracBrowser for help on using the repository browser.