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

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

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • Property mode set to 100644
File size: 13.1 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_callid_t, ipc_call_t *);
230static void remote_usbhc_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
231static void remote_usbhc_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
232static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
233static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
234static void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, ipc_callid_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 ipc_callid_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 ipc_callid_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 ipc_callid_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 ipc_callid_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 ipc_callid_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 ipc_callid_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 ipc_callid_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 ipc_callid_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(ipc_callid_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 ipc_callid_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, ipc_callid_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 ipc_callid_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.