Add missing calls to refcount_init()
[helenos.git] / uspace / lib / drv / generic / remote_usbhc.c
blob44b3efb1886a0e80a83b19e33c13663a5c975d79
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.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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
32 * @{
34 /** @file
37 #include <async.h>
38 #include <macros.h>
39 #include <errno.h>
40 #include <devman.h>
41 #include <as.h>
43 #include "usbhc_iface.h"
44 #include "ddf/driver.h"
46 typedef 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;
55 /** Reserve default USB address.
56 * @param[in] exch IPC communication exchange
57 * @return Error code.
59 errno_t usbhc_reserve_default_address(async_exch_t *exch)
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);
66 /** Release default USB address.
68 * @param[in] exch IPC communication exchange
70 * @return Error code.
72 errno_t usbhc_release_default_address(async_exch_t *exch)
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);
79 /**
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
86 * @return Error code.
88 errno_t usbhc_device_enumerate(async_exch_t *exch, unsigned port, usb_speed_t speed)
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;
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)
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);
113 errno_t usbhc_register_endpoint(async_exch_t *exch, usb_pipe_desc_t *pipe_desc,
114 const usb_endpoint_descriptors_t *desc)
116 if (!exch)
117 return EBADMEM;
119 if (!desc)
120 return EINVAL;
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) {
126 return ENOMEM;
129 errno_t ret = async_data_write_start(exch, desc, sizeof(*desc));
130 if (ret != EOK) {
131 async_forget(opening_request);
132 return ret;
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));
144 if (ret != EOK) {
145 return ret;
148 if (pipe_desc)
149 *pipe_desc = dest;
151 return EOK;
154 errno_t usbhc_unregister_endpoint(async_exch_t *exch, const usb_pipe_desc_t *pipe_desc)
156 if (!exch)
157 return EBADMEM;
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) {
163 return ENOMEM;
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;
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)
186 if (transferred)
187 *transferred = 0;
189 if (!exch)
190 return EBADMEM;
192 ipc_call_t call;
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)
198 return ENOMEM;
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;
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;
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;
218 /* Wait for the answer. */
219 errno_t opening_request_rc;
220 async_wait_for(opening_request, &opening_request_rc);
222 if (transferred)
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,
252 typedef struct {
253 ipc_call_t call;
254 usbhc_iface_transfer_request_t request;
255 } async_transaction_t;
257 void remote_usbhc_default_address_reservation(ddf_fun_t *fun, void *iface,
258 ipc_call_t *call)
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);
264 return;
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,
273 ipc_call_t *call)
275 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
277 if (usbhc_iface->device_enumerate == NULL) {
278 async_answer_0(call, ENOTSUP);
279 return;
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,
289 ipc_call_t *call)
291 const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
293 if (usbhc_iface->device_remove == NULL) {
294 async_answer_0(call, ENOTSUP);
295 return;
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,
304 ipc_call_t *call)
306 assert(fun);
307 assert(iface);
308 assert(call);
310 const usbhc_iface_t *usbhc_iface = iface;
312 if (!usbhc_iface->register_endpoint) {
313 async_answer_0(call, ENOTSUP);
314 return;
317 usb_endpoint_descriptors_t ep_desc;
318 ipc_call_t data;
319 size_t len;
321 if (!async_data_write_receive(&data, &len) ||
322 len != sizeof(ep_desc)) {
323 async_answer_0(call, EINVAL);
324 return;
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)) {
336 return;
338 async_data_read_finalize(&data, &pipe_desc, sizeof(pipe_desc));
341 static void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
342 ipc_call_t *call)
344 assert(fun);
345 assert(iface);
346 assert(call);
348 const usbhc_iface_t *usbhc_iface = iface;
350 if (!usbhc_iface->unregister_endpoint) {
351 async_answer_0(call, ENOTSUP);
352 return;
355 usb_pipe_desc_t pipe_desc;
356 ipc_call_t data;
357 size_t len;
359 if (!async_data_write_receive(&data, &len) ||
360 len != sizeof(pipe_desc)) {
361 async_answer_0(call, EINVAL);
362 return;
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)
372 if (trans == NULL) {
373 return;
375 if (trans->request.buffer.virt != NULL) {
376 as_area_destroy(trans->request.buffer.virt);
379 free(trans);
382 static async_transaction_t *async_transaction_create(ipc_call_t *call)
384 async_transaction_t *trans = calloc(1, sizeof(async_transaction_t));
386 if (trans != NULL)
387 trans->call = *call;
389 return trans;
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);
397 return err;
400 static errno_t receive_memory_buffer(async_transaction_t *trans)
402 assert(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;
410 errno_t err;
411 ipc_call_t data;
412 size_t size;
413 unsigned flags;
415 if (!async_share_out_receive(&data, &size, &flags))
416 return EPARTY;
418 if (size < required_size || (flags & required_flags) != required_flags) {
419 async_answer_0(&data, EINVAL);
420 return EINVAL;
423 if ((err = async_share_out_finalize(&data, &trans->request.buffer.virt)))
424 return err;
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.
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;
443 return EOK;
446 void remote_usbhc_transfer(ddf_fun_t *fun, void *iface, ipc_call_t *call)
448 assert(fun);
449 assert(iface);
450 assert(call);
452 const usbhc_iface_t *usbhc_iface = iface;
454 if (!usbhc_iface->transfer) {
455 async_answer_0(call, ENOTSUP);
456 return;
459 async_transaction_t *trans =
460 async_transaction_create(call);
461 if (trans == NULL) {
462 async_answer_0(call, ENOMEM);
463 return;
466 errno_t err = EPARTY;
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;
476 if ((err = async_data_write_finalize(&data,
477 &trans->request, sizeof(trans->request))))
478 goto err;
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;
488 if ((err = usbhc_iface->transfer(fun, &trans->request,
489 &transfer_finished, trans)))
490 goto err;
492 /* The call will be answered asynchronously by the callback. */
493 return;
495 err:
496 async_answer_0(call, err);
497 async_transaction_destroy(trans);
501 * @}