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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 65dde99 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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