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
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 *, 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);
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 cap_call_handle_t caller;
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 cap_call_handle_t callid, 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(callid, 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(callid, ret);
271}
272
273
274static void remote_usbhc_device_enumerate(ddf_fun_t *fun, void *iface,
275 cap_call_handle_t callid, 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(callid, 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(callid, ret);
288}
289
290static void remote_usbhc_device_remove(ddf_fun_t *fun, void *iface,
291 cap_call_handle_t callid, 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(callid, 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(callid, ret);
303}
304
305static void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
306 cap_call_handle_t callid, 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(callid, ENOTSUP);
316 return;
317 }
318
319 usb_endpoint_descriptors_t ep_desc;
320 cap_call_handle_t data_callid;
321 size_t len;
322
323 if (!async_data_write_receive(&data_callid, &len)
324 || len != sizeof(ep_desc)) {
325 async_answer_0(callid, EINVAL);
326 return;
327 }
328 async_data_write_finalize(data_callid, &ep_desc, sizeof(ep_desc));
329
330 usb_pipe_desc_t pipe_desc;
331
332 const errno_t rc = usbhc_iface->register_endpoint(fun, &pipe_desc, &ep_desc);
333 async_answer_0(callid, rc);
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));
340}
341
342static void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
343 cap_call_handle_t callid, ipc_call_t *call)
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
356 usb_pipe_desc_t pipe_desc;
357 cap_call_handle_t data_callid;
358 size_t len;
359
360 if (!async_data_write_receive(&data_callid, &len)
361 || len != sizeof(pipe_desc)) {
362 async_answer_0(callid, EINVAL);
363 return;
364 }
365 async_data_write_finalize(data_callid, &pipe_desc, sizeof(pipe_desc));
366
367 const errno_t rc = usbhc_iface->unregister_endpoint(fun, &pipe_desc);
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 }
376 if (trans->request.buffer.virt != NULL) {
377 as_area_destroy(trans->request.buffer.virt);
378 }
379
380 free(trans);
381}
382
383static async_transaction_t *async_transaction_create(cap_call_handle_t caller)
384{
385 async_transaction_t *trans = calloc(1, sizeof(async_transaction_t));
386
387 if (trans != NULL)
388 trans->caller = caller;
389
390 return trans;
391}
392
393static errno_t transfer_finished(void *arg, errno_t error, size_t transferred_size)
394{
395 async_transaction_t *trans = arg;
396 const errno_t err = async_answer_1(trans->caller, error, transferred_size);
397 async_transaction_destroy(trans);
398 return err;
399}
400
401static errno_t receive_memory_buffer(async_transaction_t *trans)
402{
403 assert(trans);
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;
410
411 errno_t err;
412 cap_call_handle_t data_callid;
413 size_t size;
414 unsigned flags;
415
416 if (!async_share_out_receive(&data_callid, &size, &flags))
417 return EPARTY;
418
419 if (size < required_size || (flags & required_flags) != required_flags) {
420 async_answer_0(data_callid, EINVAL);
421 return EINVAL;
422 }
423
424 if ((err = async_share_out_finalize(data_callid, &trans->request.buffer.virt)))
425 return err;
426
427 /*
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.
432 */
433 if (flags & AS_AREA_READ) {
434 char foo = 0;
435 volatile const char *buf = trans->request.buffer.virt + trans->request.offset;
436 for (size_t i = 0; i < size; i += PAGE_SIZE)
437 foo += buf[i];
438 } else {
439 volatile char *buf = trans->request.buffer.virt + trans->request.offset;
440 for (size_t i = 0; i < size; i += PAGE_SIZE)
441 buf[i] = 0xff;
442 }
443
444 return EOK;
445}
446
447void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, cap_call_handle_t callid, ipc_call_t *call)
448{
449 assert(fun);
450 assert(iface);
451 assert(call);
452
453 const usbhc_iface_t *usbhc_iface = iface;
454
455 if (!usbhc_iface->transfer) {
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
466 errno_t err = EPARTY;
467
468 cap_call_handle_t data_callid;
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;
474 }
475
476 if ((err = async_data_write_finalize(data_callid,
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(callid, err);
497 async_transaction_destroy(trans);
498}
499
500/**
501 * @}
502 */
Note: See TracBrowser for help on using the repository browser.