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

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

libusb, libdrv, libusbhost: Remove searching for dev handle by usb address

  • Property mode set to 100644
File size: 13.4 KB
Line 
1/*
2 * Copyright (c) 2010-2011 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 <errno.h>
38#include <assert.h>
39
40#include "usbhc_iface.h"
41#include "ddf/driver.h"
42
43#define USB_MAX_PAYLOAD_SIZE 1020
44
45/** IPC methods for communication with HC through DDF interface.
46 *
47 * Notes for async methods:
48 *
49 * Methods for sending data to device (OUT transactions)
50 * - e.g. IPC_M_USBHC_INTERRUPT_OUT -
51 * always use the same semantics:
52 * - first, IPC call with given method is made
53 * - argument #1 is target address
54 * - argument #2 is target endpoint
55 * - argument #3 is max packet size of the endpoint
56 * - this call is immediately followed by IPC data write (from caller)
57 * - the initial call (and the whole transaction) is answer after the
58 * transaction is scheduled by the HC and acknowledged by the device
59 * or immediately after error is detected
60 * - the answer carries only the error code
61 *
62 * Methods for retrieving data from device (IN transactions)
63 * - e.g. IPC_M_USBHC_INTERRUPT_IN -
64 * also use the same semantics:
65 * - first, IPC call with given method is made
66 * - argument #1 is target address
67 * - argument #2 is target endpoint
68 * - this call is immediately followed by IPC data read (async version)
69 * - the call is not answered until the device returns some data (or until
70 * error occurs)
71 *
72 * Some special methods (NO-DATA transactions) do not send any data. These
73 * might behave as both OUT or IN transactions because communication parts
74 * where actual buffers are exchanged are omitted.
75 **
76 * For all these methods, wrap functions exists. Important rule: functions
77 * for IN transactions have (as parameters) buffers where retrieved data
78 * will be stored. These buffers must be already allocated and shall not be
79 * touch until the transaction is completed
80 * (e.g. not before calling usb_wait_for() with appropriate handle).
81 * OUT transactions buffers can be freed immediately after call is dispatched
82 * (i.e. after return from wrapping function).
83 *
84 */
85typedef enum {
86 /** Register endpoint attributes at host controller.
87 * This is used to reserve portion of USB bandwidth.
88 * When speed is invalid, speed of the device is used.
89 * Parameters:
90 * - USB address + endpoint number
91 * - packed as ADDR << 16 + EP
92 * - speed + transfer type + direction
93 * - packed as ( SPEED << 8 + TYPE ) << 8 + DIR
94 * - maximum packet size + interval (in milliseconds)
95 * - packed as MPS << 16 + INT
96 * Answer:
97 * - EOK - reservation successful
98 * - ELIMIT - not enough bandwidth to satisfy the request
99 */
100 IPC_M_USBHC_REGISTER_ENDPOINT,
101
102 /** Revert endpoint registration.
103 * Parameters:
104 * - USB address
105 * - endpoint number
106 * - data direction
107 * Answer:
108 * - EOK - endpoint unregistered
109 * - ENOENT - unknown endpoint
110 */
111 IPC_M_USBHC_UNREGISTER_ENDPOINT,
112
113 /** Get data from device.
114 * See explanation at usb_iface_funcs_t (IN transaction).
115 */
116 IPC_M_USBHC_READ,
117
118 /** Send data to device.
119 * See explanation at usb_iface_funcs_t (OUT transaction).
120 */
121 IPC_M_USBHC_WRITE,
122} usbhc_iface_funcs_t;
123
124int usbhc_register_endpoint(async_exch_t *exch, usb_address_t address,
125 usb_endpoint_t endpoint, usb_transfer_type_t type,
126 usb_direction_t direction, size_t mps, unsigned interval)
127{
128 if (!exch)
129 return EBADMEM;
130 const usb_target_t target =
131 {{ .address = address, .endpoint = endpoint }};
132#define _PACK2(high, low) (((high & 0xffff) << 16) | (low & 0xffff))
133
134 return async_req_4_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
135 IPC_M_USBHC_REGISTER_ENDPOINT, target.packed,
136 _PACK2(type, direction), _PACK2(mps, interval));
137
138#undef _PACK2
139}
140
141int usbhc_unregister_endpoint(async_exch_t *exch, usb_address_t address,
142 usb_endpoint_t endpoint, usb_direction_t direction)
143{
144 if (!exch)
145 return EBADMEM;
146 return async_req_4_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
147 IPC_M_USBHC_UNREGISTER_ENDPOINT, address, endpoint, direction);
148}
149
150int usbhc_read(async_exch_t *exch, usb_address_t address,
151 usb_endpoint_t endpoint, uint64_t setup, void *data, size_t size,
152 size_t *rec_size)
153{
154 if (!exch)
155 return EBADMEM;
156
157 if (size == 0 && setup == 0)
158 return EOK;
159
160 const usb_target_t target =
161 {{ .address = address, .endpoint = endpoint }};
162
163 /* Make call identifying target USB device and type of transfer. */
164 aid_t opening_request = async_send_4(exch,
165 DEV_IFACE_ID(USBHC_DEV_IFACE),
166 IPC_M_USBHC_READ, target.packed,
167 (setup & UINT32_MAX), (setup >> 32), NULL);
168
169 if (opening_request == 0) {
170 return ENOMEM;
171 }
172
173 /* Retrieve the data. */
174 ipc_call_t data_request_call;
175 aid_t data_request =
176 async_data_read(exch, data, size, &data_request_call);
177
178 if (data_request == 0) {
179 // FIXME: How to let the other side know that we want to abort?
180 async_forget(opening_request);
181 return ENOMEM;
182 }
183
184 /* Wait for the answer. */
185 sysarg_t data_request_rc;
186 sysarg_t opening_request_rc;
187 async_wait_for(data_request, &data_request_rc);
188 async_wait_for(opening_request, &opening_request_rc);
189
190 if (data_request_rc != EOK) {
191 /* Prefer the return code of the opening request. */
192 if (opening_request_rc != EOK) {
193 return (int) opening_request_rc;
194 } else {
195 return (int) data_request_rc;
196 }
197 }
198 if (opening_request_rc != EOK) {
199 return (int) opening_request_rc;
200 }
201
202 *rec_size = IPC_GET_ARG2(data_request_call);
203 return EOK;
204}
205
206int usbhc_write(async_exch_t *exch, usb_address_t address,
207 usb_endpoint_t endpoint, uint64_t setup, const void *data, size_t size)
208{
209 if (!exch)
210 return EBADMEM;
211
212 if (size == 0 && setup == 0)
213 return EOK;
214
215 const usb_target_t target =
216 {{ .address = address, .endpoint = endpoint }};
217
218 aid_t opening_request = async_send_5(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
219 IPC_M_USBHC_WRITE, target.packed, size,
220 (setup & UINT32_MAX), (setup >> 32), NULL);
221
222 if (opening_request == 0) {
223 return ENOMEM;
224 }
225
226 /* Send the data if any. */
227 if (size > 0) {
228 const int ret = async_data_write_start(exch, data, size);
229 if (ret != EOK) {
230 async_forget(opening_request);
231 return ret;
232 }
233 }
234
235 /* Wait for the answer. */
236 sysarg_t opening_request_rc;
237 async_wait_for(opening_request, &opening_request_rc);
238
239 return (int) opening_request_rc;
240}
241
242
243static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
244static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
245static void remote_usbhc_read(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
246static void remote_usbhc_write(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
247//static void remote_usbhc(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
248
249/** Remote USB host controller interface operations. */
250static remote_iface_func_ptr_t remote_usbhc_iface_ops[] = {
251 [IPC_M_USBHC_REGISTER_ENDPOINT] = remote_usbhc_register_endpoint,
252 [IPC_M_USBHC_UNREGISTER_ENDPOINT] = remote_usbhc_unregister_endpoint,
253
254 [IPC_M_USBHC_READ] = remote_usbhc_read,
255 [IPC_M_USBHC_WRITE] = remote_usbhc_write,
256};
257
258/** Remote USB host controller interface structure.
259 */
260remote_iface_t remote_usbhc_iface = {
261 .method_count = sizeof(remote_usbhc_iface_ops) /
262 sizeof(remote_usbhc_iface_ops[0]),
263 .methods = remote_usbhc_iface_ops
264};
265
266typedef struct {
267 ipc_callid_t caller;
268 ipc_callid_t data_caller;
269 void *buffer;
270} async_transaction_t;
271
272static void async_transaction_destroy(async_transaction_t *trans)
273{
274 if (trans == NULL) {
275 return;
276 }
277 if (trans->buffer != NULL) {
278 free(trans->buffer);
279 }
280
281 free(trans);
282}
283
284static async_transaction_t *async_transaction_create(ipc_callid_t caller)
285{
286 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
287 if (trans == NULL) {
288 return NULL;
289 }
290
291 trans->caller = caller;
292 trans->data_caller = 0;
293 trans->buffer = NULL;
294
295 return trans;
296}
297
298static void callback_out(int outcome, void *arg)
299{
300 async_transaction_t *trans = arg;
301
302 async_answer_0(trans->caller, outcome);
303
304 async_transaction_destroy(trans);
305}
306
307static void callback_in(int outcome, size_t actual_size, void *arg)
308{
309 async_transaction_t *trans = (async_transaction_t *)arg;
310
311 if (outcome != EOK) {
312 async_answer_0(trans->caller, outcome);
313 if (trans->data_caller) {
314 async_answer_0(trans->data_caller, EINTR);
315 }
316 async_transaction_destroy(trans);
317 return;
318 }
319
320 if (trans->data_caller) {
321 async_data_read_finalize(trans->data_caller,
322 trans->buffer, actual_size);
323 }
324
325 async_answer_0(trans->caller, EOK);
326
327 async_transaction_destroy(trans);
328}
329
330void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
331 ipc_callid_t callid, ipc_call_t *call)
332{
333 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
334
335 if (!usb_iface->register_endpoint) {
336 async_answer_0(callid, ENOTSUP);
337 return;
338 }
339
340#define _INIT_FROM_HIGH_DATA2(type, var, arg_no) \
341 type var = (type) (DEV_IPC_GET_ARG##arg_no(*call) >> 16)
342#define _INIT_FROM_LOW_DATA2(type, var, arg_no) \
343 type var = (type) (DEV_IPC_GET_ARG##arg_no(*call) & 0xffff)
344
345 const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
346
347 _INIT_FROM_HIGH_DATA2(usb_transfer_type_t, transfer_type, 2);
348 _INIT_FROM_LOW_DATA2(usb_direction_t, direction, 2);
349
350 _INIT_FROM_HIGH_DATA2(size_t, max_packet_size, 3);
351 _INIT_FROM_LOW_DATA2(unsigned int, interval, 3);
352
353#undef _INIT_FROM_HIGH_DATA2
354#undef _INIT_FROM_LOW_DATA2
355
356 int rc = usb_iface->register_endpoint(fun, target.address,
357 target.endpoint, transfer_type, direction, max_packet_size, interval);
358
359 async_answer_0(callid, rc);
360}
361
362void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
363 ipc_callid_t callid, ipc_call_t *call)
364{
365 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
366
367 if (!usb_iface->unregister_endpoint) {
368 async_answer_0(callid, ENOTSUP);
369 return;
370 }
371
372 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
373 usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG2(*call);
374 usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG3(*call);
375
376 int rc = usb_iface->unregister_endpoint(fun,
377 address, endpoint, direction);
378
379 async_answer_0(callid, rc);
380}
381
382void remote_usbhc_read(
383 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
384{
385 assert(fun);
386 assert(iface);
387 assert(call);
388
389 const usbhc_iface_t *hc_iface = iface;
390
391 if (!hc_iface->read) {
392 async_answer_0(callid, ENOTSUP);
393 return;
394 }
395
396 const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
397 const uint64_t setup =
398 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
399 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
400
401 async_transaction_t *trans = async_transaction_create(callid);
402 if (trans == NULL) {
403 async_answer_0(callid, ENOMEM);
404 return;
405 }
406
407 size_t size = 0;
408 if (!async_data_read_receive(&trans->data_caller, &size)) {
409 async_answer_0(callid, EPARTY);
410 return;
411 }
412
413 trans->buffer = malloc(size);
414 if (trans->buffer == NULL) {
415 async_answer_0(trans->data_caller, ENOMEM);
416 async_answer_0(callid, ENOMEM);
417 async_transaction_destroy(trans);
418 }
419
420 const int rc = hc_iface->read(
421 fun, target, setup, trans->buffer, size, callback_in, trans);
422
423 if (rc != EOK) {
424 async_answer_0(trans->data_caller, rc);
425 async_answer_0(callid, rc);
426 async_transaction_destroy(trans);
427 }
428}
429
430void remote_usbhc_write(
431 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
432{
433 assert(fun);
434 assert(iface);
435 assert(call);
436
437 const usbhc_iface_t *hc_iface = iface;
438
439 if (!hc_iface->write) {
440 async_answer_0(callid, ENOTSUP);
441 return;
442 }
443
444 const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
445 const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
446 const uint64_t setup =
447 ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
448 (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
449
450 async_transaction_t *trans = async_transaction_create(callid);
451 if (trans == NULL) {
452 async_answer_0(callid, ENOMEM);
453 return;
454 }
455
456 size_t size = 0;
457 if (data_buffer_len > 0) {
458 const int rc = async_data_write_accept(&trans->buffer, false,
459 1, USB_MAX_PAYLOAD_SIZE,
460 0, &size);
461
462 if (rc != EOK) {
463 async_answer_0(callid, rc);
464 async_transaction_destroy(trans);
465 return;
466 }
467 }
468
469 const int rc = hc_iface->write(
470 fun, target, setup, trans->buffer, size, callback_out, trans);
471
472 if (rc != EOK) {
473 async_answer_0(callid, rc);
474 async_transaction_destroy(trans);
475 }
476}
477/**
478 * @}
479 */
Note: See TracBrowser for help on using the repository browser.