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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 681f24b3 was b7d8fd9, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Add IPC layer for endpoint (bandwidth) reservation

No HC driver currently implements this and no device driver uses this.
Some adjustments might be needed later.

  • Property mode set to 100644
File size: 15.0 KB
Line 
1/*
2 * Copyright (c) 2010-2011 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libdrv
30 * @{
31 */
32/** @file
33 */
34
35#include <async.h>
36#include <errno.h>
37#include <assert.h>
38
39#include "usbhc_iface.h"
40#include "ddf/driver.h"
41
42#define USB_MAX_PAYLOAD_SIZE 1020
43#define HACK_MAX_PACKET_SIZE 8
44#define HACK_MAX_PACKET_SIZE_INTERRUPT_IN 4
45
46static void remote_usbhc_interrupt_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
47static void remote_usbhc_interrupt_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
48static void remote_usbhc_bulk_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
49static void remote_usbhc_bulk_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
50static void remote_usbhc_control_write(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
51static void remote_usbhc_control_read(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
52static void remote_usbhc_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
53static void remote_usbhc_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
54static void remote_usbhc_request_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
55static void remote_usbhc_bind_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
56static void remote_usbhc_release_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
57static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
58static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
59//static void remote_usbhc(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
60
61/** Remote USB host controller interface operations. */
62static remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
63 remote_usbhc_reserve_default_address,
64 remote_usbhc_release_default_address,
65
66 remote_usbhc_request_address,
67 remote_usbhc_bind_address,
68 remote_usbhc_release_address,
69
70 remote_usbhc_interrupt_out,
71 remote_usbhc_interrupt_in,
72
73 remote_usbhc_bulk_out,
74 remote_usbhc_bulk_in,
75
76 remote_usbhc_control_write,
77 remote_usbhc_control_read,
78
79 remote_usbhc_register_endpoint,
80 remote_usbhc_unregister_endpoint
81};
82
83/** Remote USB host controller interface structure.
84 */
85remote_iface_t remote_usbhc_iface = {
86 .method_count = sizeof(remote_usbhc_iface_ops) /
87 sizeof(remote_usbhc_iface_ops[0]),
88 .methods = remote_usbhc_iface_ops
89};
90
91typedef struct {
92 ipc_callid_t caller;
93 ipc_callid_t data_caller;
94 void *buffer;
95 void *setup_packet;
96 size_t size;
97} async_transaction_t;
98
99static void async_transaction_destroy(async_transaction_t *trans)
100{
101 if (trans == NULL) {
102 return;
103 }
104
105 if (trans->setup_packet != NULL) {
106 free(trans->setup_packet);
107 }
108 if (trans->buffer != NULL) {
109 free(trans->buffer);
110 }
111
112 free(trans);
113}
114
115static async_transaction_t *async_transaction_create(ipc_callid_t caller)
116{
117 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
118 if (trans == NULL) {
119 return NULL;
120 }
121
122 trans->caller = caller;
123 trans->data_caller = 0;
124 trans->buffer = NULL;
125 trans->setup_packet = NULL;
126 trans->size = 0;
127
128 return trans;
129}
130
131void remote_usbhc_reserve_default_address(ddf_fun_t *fun, void *iface,
132 ipc_callid_t callid, ipc_call_t *call)
133{
134 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
135
136 if (!usb_iface->reserve_default_address) {
137 async_answer_0(callid, ENOTSUP);
138 return;
139 }
140
141 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
142
143 int rc = usb_iface->reserve_default_address(fun, speed);
144
145 async_answer_0(callid, rc);
146}
147
148void remote_usbhc_release_default_address(ddf_fun_t *fun, void *iface,
149 ipc_callid_t callid, ipc_call_t *call)
150{
151 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
152
153 if (!usb_iface->release_default_address) {
154 async_answer_0(callid, ENOTSUP);
155 return;
156 }
157
158 int rc = usb_iface->release_default_address(fun);
159
160 async_answer_0(callid, rc);
161}
162
163void remote_usbhc_request_address(ddf_fun_t *fun, void *iface,
164 ipc_callid_t callid, ipc_call_t *call)
165{
166 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
167
168 if (!usb_iface->request_address) {
169 async_answer_0(callid, ENOTSUP);
170 return;
171 }
172
173 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
174
175 usb_address_t address;
176 int rc = usb_iface->request_address(fun, speed, &address);
177 if (rc != EOK) {
178 async_answer_0(callid, rc);
179 } else {
180 async_answer_1(callid, EOK, (sysarg_t) address);
181 }
182}
183
184void remote_usbhc_bind_address(ddf_fun_t *fun, void *iface,
185 ipc_callid_t callid, ipc_call_t *call)
186{
187 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
188
189 if (!usb_iface->bind_address) {
190 async_answer_0(callid, ENOTSUP);
191 return;
192 }
193
194 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
195 devman_handle_t handle = (devman_handle_t) DEV_IPC_GET_ARG2(*call);
196
197 int rc = usb_iface->bind_address(fun, address, handle);
198
199 async_answer_0(callid, rc);
200}
201
202void remote_usbhc_release_address(ddf_fun_t *fun, void *iface,
203 ipc_callid_t callid, ipc_call_t *call)
204{
205 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
206
207 if (!usb_iface->release_address) {
208 async_answer_0(callid, ENOTSUP);
209 return;
210 }
211
212 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
213
214 int rc = usb_iface->release_address(fun, address);
215
216 async_answer_0(callid, rc);
217}
218
219
220static void callback_out(ddf_fun_t *fun,
221 int outcome, void *arg)
222{
223 async_transaction_t *trans = (async_transaction_t *)arg;
224
225 async_answer_0(trans->caller, outcome);
226
227 async_transaction_destroy(trans);
228}
229
230static void callback_in(ddf_fun_t *fun,
231 int outcome, size_t actual_size, void *arg)
232{
233 async_transaction_t *trans = (async_transaction_t *)arg;
234
235 if (outcome != EOK) {
236 async_answer_0(trans->caller, outcome);
237 if (trans->data_caller) {
238 async_answer_0(trans->data_caller, EINTR);
239 }
240 async_transaction_destroy(trans);
241 return;
242 }
243
244 trans->size = actual_size;
245
246 if (trans->data_caller) {
247 async_data_read_finalize(trans->data_caller,
248 trans->buffer, actual_size);
249 }
250
251 async_answer_0(trans->caller, EOK);
252
253 async_transaction_destroy(trans);
254}
255
256/** Process an outgoing transfer (both OUT and SETUP).
257 *
258 * @param device Target device.
259 * @param callid Initiating caller.
260 * @param call Initiating call.
261 * @param transfer_func Transfer function (might be NULL).
262 */
263static void remote_usbhc_out_transfer(ddf_fun_t *fun,
264 ipc_callid_t callid, ipc_call_t *call,
265 usbhc_iface_transfer_out_t transfer_func)
266{
267 if (!transfer_func) {
268 async_answer_0(callid, ENOTSUP);
269 return;
270 }
271
272 size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
273 usb_target_t target = {
274 .address = DEV_IPC_GET_ARG1(*call),
275 .endpoint = DEV_IPC_GET_ARG2(*call)
276 };
277
278 size_t len = 0;
279 void *buffer = NULL;
280
281 int rc = async_data_write_accept(&buffer, false,
282 1, USB_MAX_PAYLOAD_SIZE,
283 0, &len);
284
285 if (rc != EOK) {
286 async_answer_0(callid, rc);
287 return;
288 }
289
290 async_transaction_t *trans = async_transaction_create(callid);
291 if (trans == NULL) {
292 if (buffer != NULL) {
293 free(buffer);
294 }
295 async_answer_0(callid, ENOMEM);
296 return;
297 }
298
299 trans->buffer = buffer;
300 trans->size = len;
301
302 rc = transfer_func(fun, target, max_packet_size,
303 buffer, len,
304 callback_out, trans);
305
306 if (rc != EOK) {
307 async_answer_0(callid, rc);
308 async_transaction_destroy(trans);
309 }
310}
311
312/** Process an incoming transfer.
313 *
314 * @param device Target device.
315 * @param callid Initiating caller.
316 * @param call Initiating call.
317 * @param transfer_func Transfer function (might be NULL).
318 */
319static void remote_usbhc_in_transfer(ddf_fun_t *fun,
320 ipc_callid_t callid, ipc_call_t *call,
321 usbhc_iface_transfer_in_t transfer_func)
322{
323 if (!transfer_func) {
324 async_answer_0(callid, ENOTSUP);
325 return;
326 }
327
328 size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
329 usb_target_t target = {
330 .address = DEV_IPC_GET_ARG1(*call),
331 .endpoint = DEV_IPC_GET_ARG2(*call)
332 };
333
334 size_t len;
335 ipc_callid_t data_callid;
336 if (!async_data_read_receive(&data_callid, &len)) {
337 async_answer_0(callid, EPARTY);
338 return;
339 }
340
341 async_transaction_t *trans = async_transaction_create(callid);
342 if (trans == NULL) {
343 async_answer_0(callid, ENOMEM);
344 return;
345 }
346 trans->data_caller = data_callid;
347 trans->buffer = malloc(len);
348 trans->size = len;
349
350 int rc = transfer_func(fun, target, max_packet_size,
351 trans->buffer, len,
352 callback_in, trans);
353
354 if (rc != EOK) {
355 async_answer_0(callid, rc);
356 async_transaction_destroy(trans);
357 }
358}
359
360void remote_usbhc_interrupt_out(ddf_fun_t *fun, void *iface,
361 ipc_callid_t callid, ipc_call_t *call)
362{
363 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
364 assert(usb_iface != NULL);
365
366 return remote_usbhc_out_transfer(fun, callid, call,
367 usb_iface->interrupt_out);
368}
369
370void remote_usbhc_interrupt_in(ddf_fun_t *fun, void *iface,
371 ipc_callid_t callid, ipc_call_t *call)
372{
373 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
374 assert(usb_iface != NULL);
375
376 return remote_usbhc_in_transfer(fun, callid, call,
377 usb_iface->interrupt_in);
378}
379
380void remote_usbhc_bulk_out(ddf_fun_t *fun, void *iface,
381 ipc_callid_t callid, ipc_call_t *call)
382{
383 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
384 assert(usb_iface != NULL);
385
386 return remote_usbhc_out_transfer(fun, callid, call,
387 usb_iface->bulk_out);
388}
389
390void remote_usbhc_bulk_in(ddf_fun_t *fun, void *iface,
391 ipc_callid_t callid, ipc_call_t *call)
392{
393 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
394 assert(usb_iface != NULL);
395
396 return remote_usbhc_in_transfer(fun, callid, call,
397 usb_iface->bulk_in);
398}
399
400void remote_usbhc_control_write(ddf_fun_t *fun, void *iface,
401ipc_callid_t callid, ipc_call_t *call)
402{
403 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
404 assert(usb_iface != NULL);
405
406 if (!usb_iface->control_write) {
407 async_answer_0(callid, ENOTSUP);
408 return;
409 }
410
411 usb_target_t target = {
412 .address = DEV_IPC_GET_ARG1(*call),
413 .endpoint = DEV_IPC_GET_ARG2(*call)
414 };
415 size_t data_buffer_len = DEV_IPC_GET_ARG3(*call);
416 size_t max_packet_size = DEV_IPC_GET_ARG4(*call);
417
418 int rc;
419
420 void *setup_packet = NULL;
421 void *data_buffer = NULL;
422 size_t setup_packet_len = 0;
423
424 rc = async_data_write_accept(&setup_packet, false,
425 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
426 if (rc != EOK) {
427 async_answer_0(callid, rc);
428 return;
429 }
430
431 if (data_buffer_len > 0) {
432 rc = async_data_write_accept(&data_buffer, false,
433 1, USB_MAX_PAYLOAD_SIZE, 0, &data_buffer_len);
434 if (rc != EOK) {
435 async_answer_0(callid, rc);
436 free(setup_packet);
437 return;
438 }
439 }
440
441 async_transaction_t *trans = async_transaction_create(callid);
442 if (trans == NULL) {
443 async_answer_0(callid, ENOMEM);
444 free(setup_packet);
445 free(data_buffer);
446 return;
447 }
448 trans->setup_packet = setup_packet;
449 trans->buffer = data_buffer;
450 trans->size = data_buffer_len;
451
452 rc = usb_iface->control_write(fun, target, max_packet_size,
453 setup_packet, setup_packet_len,
454 data_buffer, data_buffer_len,
455 callback_out, trans);
456
457 if (rc != EOK) {
458 async_answer_0(callid, rc);
459 async_transaction_destroy(trans);
460 }
461}
462
463
464void remote_usbhc_control_read(ddf_fun_t *fun, void *iface,
465ipc_callid_t callid, ipc_call_t *call)
466{
467 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
468 assert(usb_iface != NULL);
469
470 if (!usb_iface->control_read) {
471 async_answer_0(callid, ENOTSUP);
472 return;
473 }
474
475 usb_target_t target = {
476 .address = DEV_IPC_GET_ARG1(*call),
477 .endpoint = DEV_IPC_GET_ARG2(*call)
478 };
479 size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
480
481 int rc;
482
483 void *setup_packet = NULL;
484 size_t setup_packet_len = 0;
485 size_t data_len = 0;
486
487 rc = async_data_write_accept(&setup_packet, false,
488 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
489 if (rc != EOK) {
490 async_answer_0(callid, rc);
491 return;
492 }
493
494 ipc_callid_t data_callid;
495 if (!async_data_read_receive(&data_callid, &data_len)) {
496 async_answer_0(callid, EPARTY);
497 free(setup_packet);
498 return;
499 }
500
501 async_transaction_t *trans = async_transaction_create(callid);
502 if (trans == NULL) {
503 async_answer_0(callid, ENOMEM);
504 free(setup_packet);
505 return;
506 }
507 trans->data_caller = data_callid;
508 trans->setup_packet = setup_packet;
509 trans->size = data_len;
510 trans->buffer = malloc(data_len);
511 if (trans->buffer == NULL) {
512 async_answer_0(callid, ENOMEM);
513 async_transaction_destroy(trans);
514 return;
515 }
516
517 rc = usb_iface->control_read(fun, target, max_packet_size,
518 setup_packet, setup_packet_len,
519 trans->buffer, trans->size,
520 callback_in, trans);
521
522 if (rc != EOK) {
523 async_answer_0(callid, rc);
524 async_transaction_destroy(trans);
525 }
526}
527
528
529void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
530 ipc_callid_t callid, ipc_call_t *call)
531{
532 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
533
534 if (!usb_iface->register_endpoint) {
535 async_answer_0(callid, ENOTSUP);
536 return;
537 }
538
539#define INIT_FROM_HIGH_DATA(type, var, arg_no) \
540 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) / 256
541#define INIT_FROM_LOW_DATA(type, var, arg_no) \
542 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) % 256
543
544 INIT_FROM_HIGH_DATA(usb_address_t, address, 1);
545 INIT_FROM_LOW_DATA(usb_endpoint_t, endpoint, 1);
546 INIT_FROM_HIGH_DATA(usb_transfer_type_t, transfer_type, 2);
547 INIT_FROM_LOW_DATA(usb_direction_t, direction, 2);
548
549#undef INIT_FROM_HIGH_DATA
550#undef INIT_FROM_LOW_DATA
551
552 size_t max_packet_size = (size_t) DEV_IPC_GET_ARG3(*call);
553 unsigned int interval = (unsigned int) DEV_IPC_GET_ARG4(*call);
554
555 int rc = usb_iface->register_endpoint(fun, address, endpoint,
556 transfer_type, direction, max_packet_size, interval);
557
558 async_answer_0(callid, rc);
559}
560
561
562void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
563 ipc_callid_t callid, ipc_call_t *call)
564{
565 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
566
567 if (!usb_iface->unregister_endpoint) {
568 async_answer_0(callid, ENOTSUP);
569 return;
570 }
571
572 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
573 usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG2(*call);
574 usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG3(*call);
575
576 int rc = usb_iface->unregister_endpoint(fun,
577 address, endpoint, direction);
578
579 async_answer_0(callid, rc);
580}
581
582
583/**
584 * @}
585 */
Note: See TracBrowser for help on using the repository browser.