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

Last change on this file was fafb8e5, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Mechanically lowercase IPC_SET_*/IPC_GET_*

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