source: mainline/uspace/lib/drv/generic/remote_usb.c@ b4b534ac

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b4b534ac was b4b534ac, checked in by Jakub Jermar <jakub@…>, 9 years ago

Merge from lp:~jan.vesely/helenos/usb

  • Property mode set to 100644
File size: 16.2 KB
Line 
1/*
2 * Copyright (c) 2010 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libdrv
31 * @{
32 */
33/** @file
34 */
35
36#include <async.h>
37#include <macros.h>
38#include <errno.h>
39#include <devman.h>
40
41#include "usb_iface.h"
42#include "ddf/driver.h"
43
44
45usb_dev_session_t *usb_dev_connect(devman_handle_t handle)
46{
47 return devman_device_connect(handle, IPC_FLAG_BLOCKING);
48}
49
50usb_dev_session_t *usb_dev_connect_to_self(ddf_dev_t *dev)
51{
52 return devman_parent_device_connect(ddf_dev_get_handle(dev), IPC_FLAG_BLOCKING);
53}
54
55void usb_dev_disconnect(usb_dev_session_t *sess)
56{
57 if (sess)
58 async_hangup(sess);
59}
60
61typedef enum {
62 IPC_M_USB_GET_MY_INTERFACE,
63 IPC_M_USB_GET_MY_DEVICE_HANDLE,
64 IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
65 IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
66 IPC_M_USB_DEVICE_ENUMERATE,
67 IPC_M_USB_DEVICE_REMOVE,
68 IPC_M_USB_REGISTER_ENDPOINT,
69 IPC_M_USB_UNREGISTER_ENDPOINT,
70 IPC_M_USB_READ,
71 IPC_M_USB_WRITE,
72} usb_iface_funcs_t;
73
74/** Tell interface number given device can use.
75 * @param[in] exch IPC communication exchange
76 * @param[in] handle Id of the device
77 * @param[out] usb_iface Assigned USB interface
78 * @return Error code.
79 */
80int usb_get_my_interface(async_exch_t *exch, int *usb_iface)
81{
82 if (!exch)
83 return EBADMEM;
84 sysarg_t iface_no;
85 const int ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
86 IPC_M_USB_GET_MY_INTERFACE, &iface_no);
87 if (ret == EOK && usb_iface)
88 *usb_iface = (int)iface_no;
89 return ret;
90}
91
92/** Tell devman handle of the usb device function.
93 * @param[in] exch IPC communication exchange
94 * @param[out] handle devman handle of the HC used by the target device.
95 * @return Error code.
96 */
97int usb_get_my_device_handle(async_exch_t *exch, devman_handle_t *handle)
98{
99 devman_handle_t h = 0;
100 const int ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
101 IPC_M_USB_GET_MY_DEVICE_HANDLE, &h);
102 if (ret == EOK && handle)
103 *handle = (devman_handle_t)h;
104 return ret;
105}
106
107/** Reserve default USB address.
108 * @param[in] exch IPC communication exchange
109 * @param[in] speed Communication speed of the newly attached device
110 * @return Error code.
111 */
112int usb_reserve_default_address(async_exch_t *exch, usb_speed_t speed)
113{
114 if (!exch)
115 return EBADMEM;
116 return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
117 IPC_M_USB_RESERVE_DEFAULT_ADDRESS, speed);
118}
119
120/** Release default USB address.
121 * @param[in] exch IPC communication exchange
122 * @return Error code.
123 */
124int usb_release_default_address(async_exch_t *exch)
125{
126 if (!exch)
127 return EBADMEM;
128 return async_req_1_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
129 IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
130}
131
132/** Trigger USB device enumeration
133 * @param[in] exch IPC communication exchange
134 * @param[out] handle Identifier of the newly added device (if successful)
135 * @return Error code.
136 */
137int usb_device_enumerate(async_exch_t *exch, unsigned port)
138{
139 if (!exch)
140 return EBADMEM;
141 const int ret = async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
142 IPC_M_USB_DEVICE_ENUMERATE, port);
143 return ret;
144}
145
146/** Trigger USB device enumeration
147 * @param[in] exch IPC communication exchange
148 * @param[in] handle Identifier of the device
149 * @return Error code.
150 */
151int usb_device_remove(async_exch_t *exch, unsigned port)
152{
153 if (!exch)
154 return EBADMEM;
155 return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
156 IPC_M_USB_DEVICE_REMOVE, port);
157}
158
159int static_assert[sizeof(sysarg_t) >= 4 ? 1 : -1];
160typedef union {
161 uint8_t arr[sizeof(sysarg_t)];
162 sysarg_t arg;
163} pack8_t;
164
165int usb_register_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
166 usb_transfer_type_t type, usb_direction_t direction,
167 size_t mps, unsigned packets, unsigned interval)
168{
169 if (!exch)
170 return EBADMEM;
171 pack8_t pack;
172 pack.arr[0] = type;
173 pack.arr[1] = direction;
174 pack.arr[2] = interval;
175 pack.arr[3] = packets;
176
177 return async_req_4_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
178 IPC_M_USB_REGISTER_ENDPOINT, endpoint, pack.arg, mps);
179
180}
181
182int usb_unregister_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
183 usb_direction_t direction)
184{
185 if (!exch)
186 return EBADMEM;
187 return async_req_3_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
188 IPC_M_USB_UNREGISTER_ENDPOINT, endpoint, direction);
189}
190
191int usb_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
192 void *data, size_t size, size_t *rec_size)
193{
194 if (!exch)
195 return EBADMEM;
196
197 if (size == 0 && setup == 0)
198 return EOK;
199
200 /* Make call identifying target USB device and type of transfer. */
201 aid_t opening_request = async_send_4(exch,
202 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_READ, endpoint,
203 (setup & UINT32_MAX), (setup >> 32), NULL);
204
205 if (opening_request == 0) {
206 return ENOMEM;
207 }
208
209 /* Retrieve the data. */
210 ipc_call_t data_request_call;
211 aid_t data_request =
212 async_data_read(exch, data, size, &data_request_call);
213
214 if (data_request == 0) {
215 // FIXME: How to let the other side know that we want to abort?
216 async_forget(opening_request);
217 return ENOMEM;
218 }
219
220 /* Wait for the answer. */
221 sysarg_t data_request_rc;
222 sysarg_t opening_request_rc;
223 async_wait_for(data_request, &data_request_rc);
224 async_wait_for(opening_request, &opening_request_rc);
225
226 if (data_request_rc != EOK) {
227 /* Prefer the return code of the opening request. */
228 if (opening_request_rc != EOK) {
229 return (int) opening_request_rc;
230 } else {
231 return (int) data_request_rc;
232 }
233 }
234 if (opening_request_rc != EOK) {
235 return (int) opening_request_rc;
236 }
237
238 *rec_size = IPC_GET_ARG2(data_request_call);
239 return EOK;
240}
241
242int usb_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
243 const void *data, size_t size)
244{
245 if (!exch)
246 return EBADMEM;
247
248 if (size == 0 && setup == 0)
249 return EOK;
250
251 aid_t opening_request = async_send_5(exch,
252 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
253 (setup & UINT32_MAX), (setup >> 32), NULL);
254
255 if (opening_request == 0) {
256 return ENOMEM;
257 }
258
259 /* Send the data if any. */
260 if (size > 0) {
261 const int ret = async_data_write_start(exch, data, size);
262 if (ret != EOK) {
263 async_forget(opening_request);
264 return ret;
265 }
266 }
267
268 /* Wait for the answer. */
269 sysarg_t opening_request_rc;
270 async_wait_for(opening_request, &opening_request_rc);
271
272 return (int) opening_request_rc;
273}
274
275static void remote_usb_get_my_interface(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
276static void remote_usb_get_my_device_handle(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
277static void remote_usb_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
278static void remote_usb_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
279static void remote_usb_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
280static void remote_usb_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
281static void remote_usb_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
282static void remote_usb_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
283static void remote_usb_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
284static void remote_usb_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
285
286/** Remote USB interface operations. */
287static const remote_iface_func_ptr_t remote_usb_iface_ops [] = {
288 [IPC_M_USB_GET_MY_INTERFACE] = remote_usb_get_my_interface,
289 [IPC_M_USB_GET_MY_DEVICE_HANDLE] = remote_usb_get_my_device_handle,
290 [IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usb_reserve_default_address,
291 [IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usb_release_default_address,
292 [IPC_M_USB_DEVICE_ENUMERATE] = remote_usb_device_enumerate,
293 [IPC_M_USB_DEVICE_REMOVE] = remote_usb_device_remove,
294 [IPC_M_USB_REGISTER_ENDPOINT] = remote_usb_register_endpoint,
295 [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usb_unregister_endpoint,
296 [IPC_M_USB_READ] = remote_usb_read,
297 [IPC_M_USB_WRITE] = remote_usb_write,
298};
299
300/** Remote USB interface structure.
301 */
302const remote_iface_t remote_usb_iface = {
303 .method_count = ARRAY_SIZE(remote_usb_iface_ops),
304 .methods = remote_usb_iface_ops,
305};
306
307void remote_usb_get_my_interface(ddf_fun_t *fun, void *iface,
308 ipc_callid_t callid, ipc_call_t *call)
309{
310 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
311
312 if (usb_iface->get_my_interface == NULL) {
313 async_answer_0(callid, ENOTSUP);
314 return;
315 }
316
317 int iface_no;
318 const int ret = usb_iface->get_my_interface(fun, &iface_no);
319 if (ret != EOK) {
320 async_answer_0(callid, ret);
321 } else {
322 async_answer_1(callid, EOK, iface_no);
323 }
324}
325
326void remote_usb_get_my_device_handle(ddf_fun_t *fun, void *iface,
327 ipc_callid_t callid, ipc_call_t *call)
328{
329 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
330
331 if (usb_iface->get_my_device_handle == NULL) {
332 async_answer_0(callid, ENOTSUP);
333 return;
334 }
335
336 devman_handle_t handle;
337 const int ret = usb_iface->get_my_device_handle(fun, &handle);
338 if (ret != EOK) {
339 async_answer_0(callid, ret);
340 }
341
342 async_answer_1(callid, EOK, (sysarg_t) handle);
343}
344
345void remote_usb_reserve_default_address(ddf_fun_t *fun, void *iface,
346 ipc_callid_t callid, ipc_call_t *call)
347{
348 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
349
350 if (usb_iface->reserve_default_address == NULL) {
351 async_answer_0(callid, ENOTSUP);
352 return;
353 }
354
355 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
356 const int ret = usb_iface->reserve_default_address(fun, speed);
357 async_answer_0(callid, ret);
358}
359
360void remote_usb_release_default_address(ddf_fun_t *fun, void *iface,
361 ipc_callid_t callid, ipc_call_t *call)
362{
363 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
364
365 if (usb_iface->release_default_address == NULL) {
366 async_answer_0(callid, ENOTSUP);
367 return;
368 }
369
370 const int ret = usb_iface->release_default_address(fun);
371 async_answer_0(callid, ret);
372}
373
374static void remote_usb_device_enumerate(ddf_fun_t *fun, void *iface,
375 ipc_callid_t callid, ipc_call_t *call)
376{
377 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
378
379 if (usb_iface->device_enumerate == NULL) {
380 async_answer_0(callid, ENOTSUP);
381 return;
382 }
383
384 const unsigned port = DEV_IPC_GET_ARG1(*call);
385 const int ret = usb_iface->device_enumerate(fun, port);
386 async_answer_0(callid, ret);
387}
388
389static void remote_usb_device_remove(ddf_fun_t *fun, void *iface,
390 ipc_callid_t callid, ipc_call_t *call)
391{
392 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
393
394 if (usb_iface->device_remove == NULL) {
395 async_answer_0(callid, ENOTSUP);
396 return;
397 }
398
399 const unsigned port = DEV_IPC_GET_ARG1(*call);
400 const int ret = usb_iface->device_remove(fun, port);
401 async_answer_0(callid, ret);
402}
403
404static void remote_usb_register_endpoint(ddf_fun_t *fun, void *iface,
405 ipc_callid_t callid, ipc_call_t *call)
406{
407 usb_iface_t *usb_iface = (usb_iface_t *) iface;
408
409 if (!usb_iface->register_endpoint) {
410 async_answer_0(callid, ENOTSUP);
411 return;
412 }
413
414 const usb_endpoint_t endpoint = DEV_IPC_GET_ARG1(*call);
415 const pack8_t pack = { .arg = DEV_IPC_GET_ARG2(*call)};
416 const size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
417
418 const usb_transfer_type_t transfer_type = pack.arr[0];
419 const usb_direction_t direction = pack.arr[1];
420 unsigned packets = pack.arr[2];
421 unsigned interval = pack.arr[3];
422
423 const int ret = usb_iface->register_endpoint(fun, endpoint,
424 transfer_type, direction, max_packet_size, packets, interval);
425
426 async_answer_0(callid, ret);
427}
428
429static void remote_usb_unregister_endpoint(ddf_fun_t *fun, void *iface,
430 ipc_callid_t callid, ipc_call_t *call)
431{
432 usb_iface_t *usb_iface = (usb_iface_t *) iface;
433
434 if (!usb_iface->unregister_endpoint) {
435 async_answer_0(callid, ENOTSUP);
436 return;
437 }
438
439 usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG1(*call);
440 usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG2(*call);
441
442 int rc = usb_iface->unregister_endpoint(fun, endpoint, direction);
443
444 async_answer_0(callid, rc);
445}
446
447typedef struct {
448 ipc_callid_t caller;
449 ipc_callid_t data_caller;
450 void *buffer;
451} async_transaction_t;
452
453static void async_transaction_destroy(async_transaction_t *trans)
454{
455 if (trans == NULL) {
456 return;
457 }
458 if (trans->buffer != NULL) {
459 free(trans->buffer);
460 }
461
462 free(trans);
463}
464
465static async_transaction_t *async_transaction_create(ipc_callid_t caller)
466{
467 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
468 if (trans == NULL) {
469 return NULL;
470 }
471
472 trans->caller = caller;
473 trans->data_caller = 0;
474 trans->buffer = NULL;
475
476 return trans;
477}
478
479static void callback_out(int outcome, void *arg)
480{
481 async_transaction_t *trans = arg;
482
483 async_answer_0(trans->caller, outcome);
484
485 async_transaction_destroy(trans);
486}
487
488static void callback_in(int outcome, size_t actual_size, void *arg)
489{
490 async_transaction_t *trans = (async_transaction_t *)arg;
491
492 if (outcome != EOK) {
493 async_answer_0(trans->caller, outcome);
494 if (trans->data_caller) {
495 async_answer_0(trans->data_caller, EINTR);
496 }
497 async_transaction_destroy(trans);
498 return;
499 }
500
501 if (trans->data_caller) {
502 async_data_read_finalize(trans->data_caller,
503 trans->buffer, actual_size);
504 }
505
506 async_answer_0(trans->caller, EOK);
507
508 async_transaction_destroy(trans);
509}
510
511void remote_usb_read(
512 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
513{
514 assert(fun);
515 assert(iface);
516 assert(call);
517
518 const usb_iface_t *usb_iface = iface;
519
520 if (!usb_iface->read) {
521 async_answer_0(callid, ENOTSUP);
522 return;
523 }
524
525 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
526 const uint64_t setup =
527 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
528 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
529
530 async_transaction_t *trans = async_transaction_create(callid);
531 if (trans == NULL) {
532 async_answer_0(callid, ENOMEM);
533 return;
534 }
535
536 size_t size = 0;
537 if (!async_data_read_receive(&trans->data_caller, &size)) {
538 async_answer_0(callid, EPARTY);
539 return;
540 }
541
542 trans->buffer = malloc(size);
543 if (trans->buffer == NULL) {
544 async_answer_0(trans->data_caller, ENOMEM);
545 async_answer_0(callid, ENOMEM);
546 async_transaction_destroy(trans);
547 }
548
549 const int rc = usb_iface->read(
550 fun, ep, setup, trans->buffer, size, callback_in, trans);
551
552 if (rc != EOK) {
553 async_answer_0(trans->data_caller, rc);
554 async_answer_0(callid, rc);
555 async_transaction_destroy(trans);
556 }
557}
558
559void remote_usb_write(
560 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
561{
562 assert(fun);
563 assert(iface);
564 assert(call);
565
566 const usb_iface_t *usb_iface = iface;
567
568 if (!usb_iface->write) {
569 async_answer_0(callid, ENOTSUP);
570 return;
571 }
572
573 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
574 const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
575 const uint64_t setup =
576 ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
577 (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
578
579 async_transaction_t *trans = async_transaction_create(callid);
580 if (trans == NULL) {
581 async_answer_0(callid, ENOMEM);
582 return;
583 }
584
585 size_t size = 0;
586 if (data_buffer_len > 0) {
587 const int rc = async_data_write_accept(&trans->buffer, false,
588 1, data_buffer_len, 0, &size);
589
590 if (rc != EOK) {
591 async_answer_0(callid, rc);
592 async_transaction_destroy(trans);
593 return;
594 }
595 }
596
597 const int rc = usb_iface->write(
598 fun, ep, setup, trans->buffer, size, callback_out, trans);
599
600 if (rc != EOK) {
601 async_answer_0(callid, rc);
602 async_transaction_destroy(trans);
603 }
604}
605/**
606 * @}
607 */
Note: See TracBrowser for help on using the repository browser.