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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0918382f was 0918382f, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

usb: Switch to using port number as device identifier.

Port number info will be needed for HS SPLIT transactions. Moreover,
it can be used to produce predictable, persistent device names.

Switch port_number from size_t to unsigned. There are max 256 ports so
even short would be enough.

  • Property mode set to 100644
File size: 16.4 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(EXCHANGE_PARALLEL, handle, IPC_FLAG_BLOCKING);
48}
49
50usb_dev_session_t *usb_dev_connect_to_self(ddf_dev_t *dev)
51{
52 // TODO All usb requests are atomic so this is safe,
53 // it will need to change once USING EXCHANGE PARALLEL is safe with
54 // devman_parent_device_connect
55 return devman_parent_device_connect(EXCHANGE_ATOMIC,
56 ddf_dev_get_handle(dev), IPC_FLAG_BLOCKING);
57}
58
59void usb_dev_disconnect(usb_dev_session_t *sess)
60{
61 if (sess)
62 async_hangup(sess);
63}
64
65typedef enum {
66 IPC_M_USB_GET_MY_INTERFACE,
67 IPC_M_USB_GET_MY_DEVICE_HANDLE,
68 IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
69 IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
70 IPC_M_USB_DEVICE_ENUMERATE,
71 IPC_M_USB_DEVICE_REMOVE,
72 IPC_M_USB_REGISTER_ENDPOINT,
73 IPC_M_USB_UNREGISTER_ENDPOINT,
74 IPC_M_USB_READ,
75 IPC_M_USB_WRITE,
76} usb_iface_funcs_t;
77
78/** Tell interface number given device can use.
79 * @param[in] exch IPC communication exchange
80 * @param[in] handle Id of the device
81 * @param[out] usb_iface Assigned USB interface
82 * @return Error code.
83 */
84int usb_get_my_interface(async_exch_t *exch, int *usb_iface)
85{
86 if (!exch)
87 return EBADMEM;
88 sysarg_t iface_no;
89 const int ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
90 IPC_M_USB_GET_MY_INTERFACE, &iface_no);
91 if (ret == EOK && usb_iface)
92 *usb_iface = (int)iface_no;
93 return ret;
94}
95
96/** Tell devman handle of the usb device function.
97 * @param[in] exch IPC communication exchange
98 * @param[out] handle devman handle of the HC used by the target device.
99 * @return Error code.
100 */
101int usb_get_my_device_handle(async_exch_t *exch, devman_handle_t *handle)
102{
103 devman_handle_t h = 0;
104 const int ret = async_req_1_1(exch, DEV_IFACE_ID(USB_DEV_IFACE),
105 IPC_M_USB_GET_MY_DEVICE_HANDLE, &h);
106 if (ret == EOK && handle)
107 *handle = (devman_handle_t)h;
108 return ret;
109}
110
111/** Reserve default USB address.
112 * @param[in] exch IPC communication exchange
113 * @param[in] speed Communication speed of the newly attached device
114 * @return Error code.
115 */
116int usb_reserve_default_address(async_exch_t *exch, usb_speed_t speed)
117{
118 if (!exch)
119 return EBADMEM;
120 return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
121 IPC_M_USB_RESERVE_DEFAULT_ADDRESS, speed);
122}
123
124/** Release default USB address.
125 * @param[in] exch IPC communication exchange
126 * @return Error code.
127 */
128int usb_release_default_address(async_exch_t *exch)
129{
130 if (!exch)
131 return EBADMEM;
132 return async_req_1_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
133 IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
134}
135
136/** Trigger USB device enumeration
137 * @param[in] exch IPC communication exchange
138 * @param[out] handle Identifier of the newly added device (if successful)
139 * @return Error code.
140 */
141int usb_device_enumerate(async_exch_t *exch, unsigned port)
142{
143 if (!exch)
144 return EBADMEM;
145 const int ret = async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
146 IPC_M_USB_DEVICE_ENUMERATE, port);
147 return ret;
148}
149
150/** Trigger USB device enumeration
151 * @param[in] exch IPC communication exchange
152 * @param[in] handle Identifier of the device
153 * @return Error code.
154 */
155int usb_device_remove(async_exch_t *exch, unsigned port)
156{
157 if (!exch)
158 return EBADMEM;
159 return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
160 IPC_M_USB_DEVICE_REMOVE, port);
161}
162
163int usb_register_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
164 usb_transfer_type_t type, usb_direction_t direction,
165 size_t mps, unsigned interval)
166{
167 if (!exch)
168 return EBADMEM;
169#define _PACK2(high, low) (((high & 0xffff) << 16) | (low & 0xffff))
170
171 return async_req_4_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
172 IPC_M_USB_REGISTER_ENDPOINT, endpoint,
173 _PACK2(type, direction), _PACK2(mps, interval));
174
175#undef _PACK2
176}
177
178int usb_unregister_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
179 usb_direction_t direction)
180{
181 if (!exch)
182 return EBADMEM;
183 return async_req_3_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
184 IPC_M_USB_UNREGISTER_ENDPOINT, endpoint, direction);
185}
186
187int usb_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
188 void *data, size_t size, size_t *rec_size)
189{
190 if (!exch)
191 return EBADMEM;
192
193 if (size == 0 && setup == 0)
194 return EOK;
195
196 /* Make call identifying target USB device and type of transfer. */
197 aid_t opening_request = async_send_4(exch,
198 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_READ, endpoint,
199 (setup & UINT32_MAX), (setup >> 32), NULL);
200
201 if (opening_request == 0) {
202 return ENOMEM;
203 }
204
205 /* Retrieve the data. */
206 ipc_call_t data_request_call;
207 aid_t data_request =
208 async_data_read(exch, data, size, &data_request_call);
209
210 if (data_request == 0) {
211 // FIXME: How to let the other side know that we want to abort?
212 async_forget(opening_request);
213 return ENOMEM;
214 }
215
216 /* Wait for the answer. */
217 sysarg_t data_request_rc;
218 sysarg_t opening_request_rc;
219 async_wait_for(data_request, &data_request_rc);
220 async_wait_for(opening_request, &opening_request_rc);
221
222 if (data_request_rc != EOK) {
223 /* Prefer the return code of the opening request. */
224 if (opening_request_rc != EOK) {
225 return (int) opening_request_rc;
226 } else {
227 return (int) data_request_rc;
228 }
229 }
230 if (opening_request_rc != EOK) {
231 return (int) opening_request_rc;
232 }
233
234 *rec_size = IPC_GET_ARG2(data_request_call);
235 return EOK;
236}
237
238int usb_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
239 const void *data, size_t size)
240{
241 if (!exch)
242 return EBADMEM;
243
244 if (size == 0 && setup == 0)
245 return EOK;
246
247 aid_t opening_request = async_send_5(exch,
248 DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
249 (setup & UINT32_MAX), (setup >> 32), NULL);
250
251 if (opening_request == 0) {
252 return ENOMEM;
253 }
254
255 /* Send the data if any. */
256 if (size > 0) {
257 const int ret = async_data_write_start(exch, data, size);
258 if (ret != EOK) {
259 async_forget(opening_request);
260 return ret;
261 }
262 }
263
264 /* Wait for the answer. */
265 sysarg_t opening_request_rc;
266 async_wait_for(opening_request, &opening_request_rc);
267
268 return (int) opening_request_rc;
269}
270
271static void remote_usb_get_my_interface(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
272static void remote_usb_get_my_device_handle(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
273static void remote_usb_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
274static void remote_usb_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
275static void remote_usb_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
276static void remote_usb_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
277static void remote_usb_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
278static void remote_usb_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
279static void remote_usb_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
280static void remote_usb_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
281
282/** Remote USB interface operations. */
283static remote_iface_func_ptr_t remote_usb_iface_ops [] = {
284 [IPC_M_USB_GET_MY_INTERFACE] = remote_usb_get_my_interface,
285 [IPC_M_USB_GET_MY_DEVICE_HANDLE] = remote_usb_get_my_device_handle,
286 [IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usb_reserve_default_address,
287 [IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usb_release_default_address,
288 [IPC_M_USB_DEVICE_ENUMERATE] = remote_usb_device_enumerate,
289 [IPC_M_USB_DEVICE_REMOVE] = remote_usb_device_remove,
290 [IPC_M_USB_REGISTER_ENDPOINT] = remote_usb_register_endpoint,
291 [IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usb_unregister_endpoint,
292 [IPC_M_USB_READ] = remote_usb_read,
293 [IPC_M_USB_WRITE] = remote_usb_write,
294};
295
296/** Remote USB interface structure.
297 */
298remote_iface_t remote_usb_iface = {
299 .method_count = ARRAY_SIZE(remote_usb_iface_ops),
300 .methods = remote_usb_iface_ops,
301};
302
303void remote_usb_get_my_interface(ddf_fun_t *fun, void *iface,
304 ipc_callid_t callid, ipc_call_t *call)
305{
306 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
307
308 if (usb_iface->get_my_interface == NULL) {
309 async_answer_0(callid, ENOTSUP);
310 return;
311 }
312
313 int iface_no;
314 const int ret = usb_iface->get_my_interface(fun, &iface_no);
315 if (ret != EOK) {
316 async_answer_0(callid, ret);
317 } else {
318 async_answer_1(callid, EOK, iface_no);
319 }
320}
321
322void remote_usb_get_my_device_handle(ddf_fun_t *fun, void *iface,
323 ipc_callid_t callid, ipc_call_t *call)
324{
325 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
326
327 if (usb_iface->get_my_device_handle == NULL) {
328 async_answer_0(callid, ENOTSUP);
329 return;
330 }
331
332 devman_handle_t handle;
333 const int ret = usb_iface->get_my_device_handle(fun, &handle);
334 if (ret != EOK) {
335 async_answer_0(callid, ret);
336 }
337
338 async_answer_1(callid, EOK, (sysarg_t) handle);
339}
340
341void remote_usb_reserve_default_address(ddf_fun_t *fun, void *iface,
342 ipc_callid_t callid, ipc_call_t *call)
343{
344 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
345
346 if (usb_iface->reserve_default_address == NULL) {
347 async_answer_0(callid, ENOTSUP);
348 return;
349 }
350
351 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
352 const int ret = usb_iface->reserve_default_address(fun, speed);
353 async_answer_0(callid, ret);
354}
355
356void remote_usb_release_default_address(ddf_fun_t *fun, void *iface,
357 ipc_callid_t callid, ipc_call_t *call)
358{
359 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
360
361 if (usb_iface->release_default_address == NULL) {
362 async_answer_0(callid, ENOTSUP);
363 return;
364 }
365
366 const int ret = usb_iface->release_default_address(fun);
367 async_answer_0(callid, ret);
368}
369
370static void remote_usb_device_enumerate(ddf_fun_t *fun, void *iface,
371 ipc_callid_t callid, ipc_call_t *call)
372{
373 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
374
375 if (usb_iface->device_enumerate == NULL) {
376 async_answer_0(callid, ENOTSUP);
377 return;
378 }
379
380 const unsigned port = DEV_IPC_GET_ARG1(*call);
381 const int ret = usb_iface->device_enumerate(fun, port);
382 async_answer_0(callid, ret);
383}
384
385static void remote_usb_device_remove(ddf_fun_t *fun, void *iface,
386 ipc_callid_t callid, ipc_call_t *call)
387{
388 const usb_iface_t *usb_iface = (usb_iface_t *) iface;
389
390 if (usb_iface->device_remove == NULL) {
391 async_answer_0(callid, ENOTSUP);
392 return;
393 }
394
395 const unsigned port = DEV_IPC_GET_ARG1(*call);
396 const int ret = usb_iface->device_remove(fun, port);
397 async_answer_0(callid, ret);
398}
399
400static void remote_usb_register_endpoint(ddf_fun_t *fun, void *iface,
401 ipc_callid_t callid, ipc_call_t *call)
402{
403 usb_iface_t *usb_iface = (usb_iface_t *) iface;
404
405 if (!usb_iface->register_endpoint) {
406 async_answer_0(callid, ENOTSUP);
407 return;
408 }
409
410#define _INIT_FROM_HIGH_DATA2(type, var, arg_no) \
411 type var = (type) (DEV_IPC_GET_ARG##arg_no(*call) >> 16)
412#define _INIT_FROM_LOW_DATA2(type, var, arg_no) \
413 type var = (type) (DEV_IPC_GET_ARG##arg_no(*call) & 0xffff)
414
415 const usb_endpoint_t endpoint = DEV_IPC_GET_ARG1(*call);
416
417 _INIT_FROM_HIGH_DATA2(usb_transfer_type_t, transfer_type, 2);
418 _INIT_FROM_LOW_DATA2(usb_direction_t, direction, 2);
419
420 _INIT_FROM_HIGH_DATA2(size_t, max_packet_size, 3);
421 _INIT_FROM_LOW_DATA2(unsigned int, interval, 3);
422
423#undef _INIT_FROM_HIGH_DATA2
424#undef _INIT_FROM_LOW_DATA2
425
426 const int ret = usb_iface->register_endpoint(fun, endpoint,
427 transfer_type, direction, max_packet_size, interval);
428
429 async_answer_0(callid, ret);
430}
431
432static void remote_usb_unregister_endpoint(ddf_fun_t *fun, void *iface,
433 ipc_callid_t callid, ipc_call_t *call)
434{
435 usb_iface_t *usb_iface = (usb_iface_t *) iface;
436
437 if (!usb_iface->unregister_endpoint) {
438 async_answer_0(callid, ENOTSUP);
439 return;
440 }
441
442 usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG1(*call);
443 usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG2(*call);
444
445 int rc = usb_iface->unregister_endpoint(fun, endpoint, direction);
446
447 async_answer_0(callid, rc);
448}
449
450typedef struct {
451 ipc_callid_t caller;
452 ipc_callid_t data_caller;
453 void *buffer;
454} async_transaction_t;
455
456static void async_transaction_destroy(async_transaction_t *trans)
457{
458 if (trans == NULL) {
459 return;
460 }
461 if (trans->buffer != NULL) {
462 free(trans->buffer);
463 }
464
465 free(trans);
466}
467
468static async_transaction_t *async_transaction_create(ipc_callid_t caller)
469{
470 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
471 if (trans == NULL) {
472 return NULL;
473 }
474
475 trans->caller = caller;
476 trans->data_caller = 0;
477 trans->buffer = NULL;
478
479 return trans;
480}
481
482static void callback_out(int outcome, void *arg)
483{
484 async_transaction_t *trans = arg;
485
486 async_answer_0(trans->caller, outcome);
487
488 async_transaction_destroy(trans);
489}
490
491static void callback_in(int outcome, size_t actual_size, void *arg)
492{
493 async_transaction_t *trans = (async_transaction_t *)arg;
494
495 if (outcome != EOK) {
496 async_answer_0(trans->caller, outcome);
497 if (trans->data_caller) {
498 async_answer_0(trans->data_caller, EINTR);
499 }
500 async_transaction_destroy(trans);
501 return;
502 }
503
504 if (trans->data_caller) {
505 async_data_read_finalize(trans->data_caller,
506 trans->buffer, actual_size);
507 }
508
509 async_answer_0(trans->caller, EOK);
510
511 async_transaction_destroy(trans);
512}
513
514void remote_usb_read(
515 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
516{
517 assert(fun);
518 assert(iface);
519 assert(call);
520
521 const usb_iface_t *usb_iface = iface;
522
523 if (!usb_iface->read) {
524 async_answer_0(callid, ENOTSUP);
525 return;
526 }
527
528 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
529 const uint64_t setup =
530 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
531 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
532
533 async_transaction_t *trans = async_transaction_create(callid);
534 if (trans == NULL) {
535 async_answer_0(callid, ENOMEM);
536 return;
537 }
538
539 size_t size = 0;
540 if (!async_data_read_receive(&trans->data_caller, &size)) {
541 async_answer_0(callid, EPARTY);
542 return;
543 }
544
545 trans->buffer = malloc(size);
546 if (trans->buffer == NULL) {
547 async_answer_0(trans->data_caller, ENOMEM);
548 async_answer_0(callid, ENOMEM);
549 async_transaction_destroy(trans);
550 }
551
552 const int rc = usb_iface->read(
553 fun, ep, setup, trans->buffer, size, callback_in, trans);
554
555 if (rc != EOK) {
556 async_answer_0(trans->data_caller, rc);
557 async_answer_0(callid, rc);
558 async_transaction_destroy(trans);
559 }
560}
561
562void remote_usb_write(
563 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
564{
565 assert(fun);
566 assert(iface);
567 assert(call);
568
569 const usb_iface_t *usb_iface = iface;
570
571 if (!usb_iface->write) {
572 async_answer_0(callid, ENOTSUP);
573 return;
574 }
575
576 const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
577 const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
578 const uint64_t setup =
579 ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
580 (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
581
582 async_transaction_t *trans = async_transaction_create(callid);
583 if (trans == NULL) {
584 async_answer_0(callid, ENOMEM);
585 return;
586 }
587
588 size_t size = 0;
589 if (data_buffer_len > 0) {
590 const int rc = async_data_write_accept(&trans->buffer, false,
591 1, data_buffer_len, 0, &size);
592
593 if (rc != EOK) {
594 async_answer_0(callid, rc);
595 async_transaction_destroy(trans);
596 return;
597 }
598 }
599
600 const int rc = usb_iface->write(
601 fun, ep, setup, trans->buffer, size, callback_out, trans);
602
603 if (rc != EOK) {
604 async_answer_0(callid, rc);
605 async_transaction_destroy(trans);
606 }
607}
608/**
609 * @}
610 */
Note: See TracBrowser for help on using the repository browser.