2 * Copyright (c) 2010 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * Copyright (c) 2018 Ondrej Hlavaty, Petr Manek
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
31 /** @addtogroup libdrv
43 #include "usbhc_iface.h"
44 #include "ddf/driver.h"
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
,
53 } usbhc_iface_funcs_t
;
55 /** Reserve default USB address.
56 * @param[in] exch IPC communication exchange
59 errno_t
usbhc_reserve_default_address(async_exch_t
*exch
)
63 return async_req_2_0(exch
, DEV_IFACE_ID(USBHC_DEV_IFACE
), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION
, true);
66 /** Release default USB address.
68 * @param[in] exch IPC communication exchange
72 errno_t
usbhc_release_default_address(async_exch_t
*exch
)
76 return async_req_2_0(exch
, DEV_IFACE_ID(USBHC_DEV_IFACE
), IPC_M_USB_DEFAULT_ADDRESS_RESERVATION
, false);
80 * Trigger USB device enumeration
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
88 errno_t
usbhc_device_enumerate(async_exch_t
*exch
, unsigned port
, usb_speed_t speed
)
92 const errno_t ret
= async_req_3_0(exch
, DEV_IFACE_ID(USBHC_DEV_IFACE
),
93 IPC_M_USB_DEVICE_ENUMERATE
, port
, speed
);
97 /** Trigger USB device enumeration
99 * @param[in] exch IPC communication exchange
100 * @param[in] handle Identifier of the device
102 * @return Error code.
105 errno_t
usbhc_device_remove(async_exch_t
*exch
, unsigned port
)
109 return async_req_2_0(exch
, DEV_IFACE_ID(USBHC_DEV_IFACE
),
110 IPC_M_USB_DEVICE_REMOVE
, port
);
113 errno_t
usbhc_register_endpoint(async_exch_t
*exch
, usb_pipe_desc_t
*pipe_desc
,
114 const usb_endpoint_descriptors_t
*desc
)
122 aid_t opening_request
= async_send_1(exch
,
123 DEV_IFACE_ID(USBHC_DEV_IFACE
), IPC_M_USB_REGISTER_ENDPOINT
, NULL
);
125 if (opening_request
== 0) {
129 errno_t ret
= async_data_write_start(exch
, desc
, sizeof(*desc
));
131 async_forget(opening_request
);
135 /* Wait for the answer. */
136 errno_t opening_request_rc
;
137 async_wait_for(opening_request
, &opening_request_rc
);
139 if (opening_request_rc
)
140 return (errno_t
) opening_request_rc
;
142 usb_pipe_desc_t dest
;
143 ret
= async_data_read_start(exch
, &dest
, sizeof(dest
));
154 errno_t
usbhc_unregister_endpoint(async_exch_t
*exch
, const usb_pipe_desc_t
*pipe_desc
)
159 aid_t opening_request
= async_send_1(exch
,
160 DEV_IFACE_ID(USBHC_DEV_IFACE
), IPC_M_USB_UNREGISTER_ENDPOINT
, NULL
);
162 if (opening_request
== 0) {
166 const errno_t ret
= async_data_write_start(exch
, pipe_desc
, sizeof(*pipe_desc
));
168 async_forget(opening_request
);
172 /* Wait for the answer. */
173 errno_t opening_request_rc
;
174 async_wait_for(opening_request
, &opening_request_rc
);
176 return (errno_t
) opening_request_rc
;
180 * Issue a USB transfer with a data contained in memory area. That area is
181 * temporarily shared with the HC.
183 errno_t
usbhc_transfer(async_exch_t
*exch
,
184 const usbhc_iface_transfer_request_t
*req
, size_t *transferred
)
194 aid_t opening_request
= async_send_1(exch
, DEV_IFACE_ID(USBHC_DEV_IFACE
),
195 IPC_M_USB_TRANSFER
, &call
);
197 if (opening_request
== 0)
200 const errno_t ret
= async_data_write_start(exch
, req
, sizeof(*req
));
202 async_forget(opening_request
);
206 /* Share the data, if any. */
208 unsigned flags
= (req
->dir
== USB_DIRECTION_IN
) ?
209 AS_AREA_WRITE
: AS_AREA_READ
;
211 const errno_t ret
= async_share_out_start(exch
, req
->buffer
.virt
, flags
);
213 async_forget(opening_request
);
218 /* Wait for the answer. */
219 errno_t opening_request_rc
;
220 async_wait_for(opening_request
, &opening_request_rc
);
223 *transferred
= IPC_GET_ARG1(call
);
225 return (errno_t
) opening_request_rc
;
228 static void remote_usbhc_default_address_reservation(ddf_fun_t
*, void *, ipc_call_t
*);
229 static void remote_usbhc_device_enumerate(ddf_fun_t
*, void *, ipc_call_t
*);
230 static void remote_usbhc_device_remove(ddf_fun_t
*, void *, ipc_call_t
*);
231 static void remote_usbhc_register_endpoint(ddf_fun_t
*, void *, ipc_call_t
*);
232 static void remote_usbhc_unregister_endpoint(ddf_fun_t
*, void *, ipc_call_t
*);
233 static void remote_usbhc_transfer(ddf_fun_t
*fun
, void *iface
, ipc_call_t
*call
);
235 /** Remote USB interface operations. */
236 static 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
,
245 /** Remote USB interface structure.
247 const remote_iface_t remote_usbhc_iface
= {
248 .method_count
= ARRAY_SIZE(remote_usbhc_iface_ops
),
249 .methods
= remote_usbhc_iface_ops
,
254 usbhc_iface_transfer_request_t request
;
255 } async_transaction_t
;
257 void remote_usbhc_default_address_reservation(ddf_fun_t
*fun
, void *iface
,
260 const usbhc_iface_t
*usbhc_iface
= (usbhc_iface_t
*) iface
;
262 if (usbhc_iface
->default_address_reservation
== NULL
) {
263 async_answer_0(call
, ENOTSUP
);
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
);
272 static void remote_usbhc_device_enumerate(ddf_fun_t
*fun
, void *iface
,
275 const usbhc_iface_t
*usbhc_iface
= (usbhc_iface_t
*) iface
;
277 if (usbhc_iface
->device_enumerate
== NULL
) {
278 async_answer_0(call
, ENOTSUP
);
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
);
288 static void remote_usbhc_device_remove(ddf_fun_t
*fun
, void *iface
,
291 const usbhc_iface_t
*usbhc_iface
= (usbhc_iface_t
*) iface
;
293 if (usbhc_iface
->device_remove
== NULL
) {
294 async_answer_0(call
, ENOTSUP
);
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
);
303 static void remote_usbhc_register_endpoint(ddf_fun_t
*fun
, void *iface
,
310 const usbhc_iface_t
*usbhc_iface
= iface
;
312 if (!usbhc_iface
->register_endpoint
) {
313 async_answer_0(call
, ENOTSUP
);
317 usb_endpoint_descriptors_t ep_desc
;
321 if (!async_data_write_receive(&data
, &len
) ||
322 len
!= sizeof(ep_desc
)) {
323 async_answer_0(call
, EINVAL
);
327 async_data_write_finalize(&data
, &ep_desc
, sizeof(ep_desc
));
329 usb_pipe_desc_t pipe_desc
;
331 const errno_t rc
= usbhc_iface
->register_endpoint(fun
, &pipe_desc
, &ep_desc
);
332 async_answer_0(call
, rc
);
334 if (!async_data_read_receive(&data
, &len
) ||
335 len
!= sizeof(pipe_desc
)) {
338 async_data_read_finalize(&data
, &pipe_desc
, sizeof(pipe_desc
));
341 static void remote_usbhc_unregister_endpoint(ddf_fun_t
*fun
, void *iface
,
348 const usbhc_iface_t
*usbhc_iface
= iface
;
350 if (!usbhc_iface
->unregister_endpoint
) {
351 async_answer_0(call
, ENOTSUP
);
355 usb_pipe_desc_t pipe_desc
;
359 if (!async_data_write_receive(&data
, &len
) ||
360 len
!= sizeof(pipe_desc
)) {
361 async_answer_0(call
, EINVAL
);
364 async_data_write_finalize(&data
, &pipe_desc
, sizeof(pipe_desc
));
366 const errno_t rc
= usbhc_iface
->unregister_endpoint(fun
, &pipe_desc
);
367 async_answer_0(call
, rc
);
370 static void async_transaction_destroy(async_transaction_t
*trans
)
375 if (trans
->request
.buffer
.virt
!= NULL
) {
376 as_area_destroy(trans
->request
.buffer
.virt
);
382 static async_transaction_t
*async_transaction_create(ipc_call_t
*call
)
384 async_transaction_t
*trans
= calloc(1, sizeof(async_transaction_t
));
392 static errno_t
transfer_finished(void *arg
, errno_t error
, size_t transferred_size
)
394 async_transaction_t
*trans
= arg
;
395 const errno_t err
= async_answer_1(&trans
->call
, error
, transferred_size
);
396 async_transaction_destroy(trans
);
400 static errno_t
receive_memory_buffer(async_transaction_t
*trans
)
403 assert(trans
->request
.size
> 0);
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
;
415 if (!async_share_out_receive(&data
, &size
, &flags
))
418 if (size
< required_size
|| (flags
& required_flags
) != required_flags
) {
419 async_answer_0(&data
, EINVAL
);
423 if ((err
= async_share_out_finalize(&data
, &trans
->request
.buffer
.virt
)))
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
432 if (flags
& AS_AREA_READ
) {
434 volatile const char *buf
= trans
->request
.buffer
.virt
+ trans
->request
.offset
;
435 for (size_t i
= 0; i
< size
; i
+= PAGE_SIZE
)
438 volatile char *buf
= trans
->request
.buffer
.virt
+ trans
->request
.offset
;
439 for (size_t i
= 0; i
< size
; i
+= PAGE_SIZE
)
446 void remote_usbhc_transfer(ddf_fun_t
*fun
, void *iface
, ipc_call_t
*call
)
452 const usbhc_iface_t
*usbhc_iface
= iface
;
454 if (!usbhc_iface
->transfer
) {
455 async_answer_0(call
, ENOTSUP
);
459 async_transaction_t
*trans
=
460 async_transaction_create(call
);
462 async_answer_0(call
, ENOMEM
);
466 errno_t err
= EPARTY
;
470 if (!async_data_write_receive(&data
, &len
) ||
471 len
!= sizeof(trans
->request
)) {
472 async_answer_0(&data
, EINVAL
);
476 if ((err
= async_data_write_finalize(&data
,
477 &trans
->request
, sizeof(trans
->request
))))
480 if (trans
->request
.size
> 0) {
481 if ((err
= receive_memory_buffer(trans
)))
484 /* The value was valid on the other side, for us, its garbage. */
485 trans
->request
.buffer
.virt
= NULL
;
488 if ((err
= usbhc_iface
->transfer(fun
, &trans
->request
,
489 &transfer_finished
, trans
)))
492 /* The call will be answered asynchronously by the callback. */
496 async_answer_0(call
, err
);
497 async_transaction_destroy(trans
);