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

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

Merge development/ changes

  • Property mode set to 100644
File size: 17.1 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
38#include "usbhc_iface.h"
39#include "driver.h"
40
41#define USB_MAX_PAYLOAD_SIZE 1020
42#define HACK_MAX_PACKET_SIZE 8
43#define HACK_MAX_PACKET_SIZE_INTERRUPT_IN 4
44
45static void remote_usbhc_get_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
46static void remote_usbhc_interrupt_out(device_t *, void *, ipc_callid_t, ipc_call_t *);
47static void remote_usbhc_interrupt_in(device_t *, void *, ipc_callid_t, ipc_call_t *);
48static void remote_usbhc_control_write_setup(device_t *, void *, ipc_callid_t, ipc_call_t *);
49static void remote_usbhc_control_write_data(device_t *, void *, ipc_callid_t, ipc_call_t *);
50static void remote_usbhc_control_write_status(device_t *, void *, ipc_callid_t, ipc_call_t *);
51static void remote_usbhc_control_read_setup(device_t *, void *, ipc_callid_t, ipc_call_t *);
52static void remote_usbhc_control_read_data(device_t *, void *, ipc_callid_t, ipc_call_t *);
53static void remote_usbhc_control_read_status(device_t *, void *, ipc_callid_t, ipc_call_t *);
54static void remote_usbhc_control_write(device_t *, void *, ipc_callid_t, ipc_call_t *);
55static void remote_usbhc_control_read(device_t *, void *, ipc_callid_t, ipc_call_t *);
56static void remote_usbhc_reserve_default_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
57static void remote_usbhc_release_default_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
58static void remote_usbhc_request_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
59static void remote_usbhc_bind_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
60static void remote_usbhc_release_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
61//static void remote_usbhc(device_t *, void *, ipc_callid_t, ipc_call_t *);
62
63/** Remote USB host controller interface operations. */
64static remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
65 remote_usbhc_get_address,
66
67 remote_usbhc_reserve_default_address,
68 remote_usbhc_release_default_address,
69
70 remote_usbhc_request_address,
71 remote_usbhc_bind_address,
72 remote_usbhc_release_address,
73
74 remote_usbhc_interrupt_out,
75 remote_usbhc_interrupt_in,
76
77 remote_usbhc_control_write_setup,
78 remote_usbhc_control_write_data,
79 remote_usbhc_control_write_status,
80
81 remote_usbhc_control_read_setup,
82 remote_usbhc_control_read_data,
83 remote_usbhc_control_read_status,
84
85 remote_usbhc_control_write,
86 remote_usbhc_control_read
87};
88
89/** Remote USB host controller interface structure.
90 */
91remote_iface_t remote_usbhc_iface = {
92 .method_count = sizeof(remote_usbhc_iface_ops) /
93 sizeof(remote_usbhc_iface_ops[0]),
94 .methods = remote_usbhc_iface_ops
95};
96
97typedef struct {
98 ipc_callid_t caller;
99 ipc_callid_t data_caller;
100 void *buffer;
101 void *setup_packet;
102 size_t size;
103} async_transaction_t;
104
105static void async_transaction_destroy(async_transaction_t *trans)
106{
107 if (trans == NULL) {
108 return;
109 }
110
111 if (trans->setup_packet != NULL) {
112 free(trans->setup_packet);
113 }
114 if (trans->buffer != NULL) {
115 free(trans->buffer);
116 }
117
118 free(trans);
119}
120
121static async_transaction_t *async_transaction_create(ipc_callid_t caller)
122{
123 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
124 if (trans == NULL) {
125 return NULL;
126 }
127
128 trans->caller = caller;
129 trans->data_caller = 0;
130 trans->buffer = NULL;
131 trans->setup_packet = NULL;
132 trans->size = 0;
133
134 return trans;
135}
136
137void remote_usbhc_get_address(device_t *device, void *iface,
138 ipc_callid_t callid, ipc_call_t *call)
139{
140 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
141
142 if (!usb_iface->tell_address) {
143 async_answer_0(callid, ENOTSUP);
144 return;
145 }
146
147 devman_handle_t handle = DEV_IPC_GET_ARG1(*call);
148
149 usb_address_t address;
150 int rc = usb_iface->tell_address(device, handle, &address);
151 if (rc != EOK) {
152 async_answer_0(callid, rc);
153 } else {
154 async_answer_1(callid, EOK, address);
155 }
156}
157
158void remote_usbhc_reserve_default_address(device_t *device, void *iface,
159 ipc_callid_t callid, ipc_call_t *call)
160{
161 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
162
163 if (!usb_iface->reserve_default_address) {
164 async_answer_0(callid, ENOTSUP);
165 return;
166 }
167
168 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
169
170 int rc = usb_iface->reserve_default_address(device, speed);
171
172 async_answer_0(callid, rc);
173}
174
175void remote_usbhc_release_default_address(device_t *device, void *iface,
176 ipc_callid_t callid, ipc_call_t *call)
177{
178 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
179
180 if (!usb_iface->release_default_address) {
181 async_answer_0(callid, ENOTSUP);
182 return;
183 }
184
185 int rc = usb_iface->release_default_address(device);
186
187 async_answer_0(callid, rc);
188}
189
190void remote_usbhc_request_address(device_t *device, void *iface,
191 ipc_callid_t callid, ipc_call_t *call)
192{
193 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
194
195 if (!usb_iface->request_address) {
196 async_answer_0(callid, ENOTSUP);
197 return;
198 }
199
200 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
201
202 usb_address_t address;
203 int rc = usb_iface->request_address(device, speed, &address);
204 if (rc != EOK) {
205 async_answer_0(callid, rc);
206 } else {
207 async_answer_1(callid, EOK, (sysarg_t) address);
208 }
209}
210
211void remote_usbhc_bind_address(device_t *device, void *iface,
212 ipc_callid_t callid, ipc_call_t *call)
213{
214 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
215
216 if (!usb_iface->bind_address) {
217 async_answer_0(callid, ENOTSUP);
218 return;
219 }
220
221 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
222 devman_handle_t handle = (devman_handle_t) DEV_IPC_GET_ARG2(*call);
223
224 int rc = usb_iface->bind_address(device, address, handle);
225
226 async_answer_0(callid, rc);
227}
228
229void remote_usbhc_release_address(device_t *device, void *iface,
230 ipc_callid_t callid, ipc_call_t *call)
231{
232 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
233
234 if (!usb_iface->release_address) {
235 async_answer_0(callid, ENOTSUP);
236 return;
237 }
238
239 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
240
241 int rc = usb_iface->release_address(device, address);
242
243 async_answer_0(callid, rc);
244}
245
246
247static void callback_out(device_t *device,
248 int outcome, void *arg)
249{
250 async_transaction_t *trans = (async_transaction_t *)arg;
251
252 async_answer_0(trans->caller, outcome);
253
254 async_transaction_destroy(trans);
255}
256
257static void callback_in(device_t *device,
258 int outcome, size_t actual_size, void *arg)
259{
260 async_transaction_t *trans = (async_transaction_t *)arg;
261
262 if (outcome != EOK) {
263 async_answer_0(trans->caller, outcome);
264 if (trans->data_caller) {
265 async_answer_0(trans->data_caller, EINTR);
266 }
267 async_transaction_destroy(trans);
268 return;
269 }
270
271 trans->size = actual_size;
272
273 if (trans->data_caller) {
274 async_data_read_finalize(trans->data_caller,
275 trans->buffer, actual_size);
276 }
277
278 async_answer_0(trans->caller, EOK);
279
280 async_transaction_destroy(trans);
281}
282
283/** Process an outgoing transfer (both OUT and SETUP).
284 *
285 * @param device Target device.
286 * @param callid Initiating caller.
287 * @param call Initiating call.
288 * @param transfer_func Transfer function (might be NULL).
289 */
290static void remote_usbhc_out_transfer(device_t *device,
291 ipc_callid_t callid, ipc_call_t *call,
292 usbhc_iface_transfer_out_t transfer_func)
293{
294 if (!transfer_func) {
295 async_answer_0(callid, ENOTSUP);
296 return;
297 }
298
299 size_t expected_len = DEV_IPC_GET_ARG3(*call);
300 usb_target_t target = {
301 .address = DEV_IPC_GET_ARG1(*call),
302 .endpoint = DEV_IPC_GET_ARG2(*call)
303 };
304
305 size_t len = 0;
306 void *buffer = NULL;
307 if (expected_len > 0) {
308 int rc = async_data_write_accept(&buffer, false,
309 1, USB_MAX_PAYLOAD_SIZE,
310 0, &len);
311
312 if (rc != EOK) {
313 async_answer_0(callid, rc);
314 return;
315 }
316 }
317
318 async_transaction_t *trans = async_transaction_create(callid);
319 if (trans == NULL) {
320 if (buffer != NULL) {
321 free(buffer);
322 }
323 async_answer_0(callid, ENOMEM);
324 return;
325 }
326
327 trans->buffer = buffer;
328 trans->size = len;
329
330 int rc = transfer_func(device, target, HACK_MAX_PACKET_SIZE,
331 buffer, len,
332 callback_out, trans);
333
334 if (rc != EOK) {
335 async_answer_0(callid, rc);
336 async_transaction_destroy(trans);
337 }
338}
339
340/** Process an incoming transfer.
341 *
342 * @param device Target device.
343 * @param callid Initiating caller.
344 * @param call Initiating call.
345 * @param transfer_func Transfer function (might be NULL).
346 */
347static void remote_usbhc_in_transfer(device_t *device,
348 ipc_callid_t callid, ipc_call_t *call,
349 usbhc_iface_transfer_in_t transfer_func)
350{
351 if (!transfer_func) {
352 async_answer_0(callid, ENOTSUP);
353 return;
354 }
355
356 size_t len = DEV_IPC_GET_ARG3(*call);
357 usb_target_t target = {
358 .address = DEV_IPC_GET_ARG1(*call),
359 .endpoint = DEV_IPC_GET_ARG2(*call)
360 };
361
362 ipc_callid_t data_callid;
363 if (!async_data_read_receive(&data_callid, &len)) {
364 async_answer_0(callid, EPARTY);
365 return;
366 }
367
368 async_transaction_t *trans = async_transaction_create(callid);
369 if (trans == NULL) {
370 async_answer_0(callid, ENOMEM);
371 return;
372 }
373 trans->data_caller = data_callid;
374 trans->buffer = malloc(len);
375 trans->size = len;
376
377 int rc = transfer_func(device, target, HACK_MAX_PACKET_SIZE_INTERRUPT_IN,
378 trans->buffer, len,
379 callback_in, trans);
380
381 if (rc != EOK) {
382 async_answer_0(callid, rc);
383 async_transaction_destroy(trans);
384 }
385}
386
387/** Process status part of control transfer.
388 *
389 * @param device Target device.
390 * @param callid Initiating caller.
391 * @param call Initiating call.
392 * @param direction Transfer direction (read ~ in, write ~ out).
393 * @param transfer_in_func Transfer function for control read (might be NULL).
394 * @param transfer_out_func Transfer function for control write (might be NULL).
395 */
396static void remote_usbhc_status_transfer(device_t *device,
397 ipc_callid_t callid, ipc_call_t *call,
398 usb_direction_t direction,
399 int (*transfer_in_func)(device_t *, usb_target_t,
400 usbhc_iface_transfer_in_callback_t, void *),
401 int (*transfer_out_func)(device_t *, usb_target_t,
402 usbhc_iface_transfer_out_callback_t, void *))
403{
404 switch (direction) {
405 case USB_DIRECTION_IN:
406 if (!transfer_in_func) {
407 async_answer_0(callid, ENOTSUP);
408 return;
409 }
410 break;
411 case USB_DIRECTION_OUT:
412 if (!transfer_out_func) {
413 async_answer_0(callid, ENOTSUP);
414 return;
415 }
416 break;
417 default:
418 assert(false && "unreachable code");
419 break;
420 }
421
422 usb_target_t target = {
423 .address = DEV_IPC_GET_ARG1(*call),
424 .endpoint = DEV_IPC_GET_ARG2(*call)
425 };
426
427 async_transaction_t *trans = async_transaction_create(callid);
428 if (trans == NULL) {
429 async_answer_0(callid, ENOMEM);
430 return;
431 }
432
433 int rc;
434 switch (direction) {
435 case USB_DIRECTION_IN:
436 rc = transfer_in_func(device, target,
437 callback_in, trans);
438 break;
439 case USB_DIRECTION_OUT:
440 rc = transfer_out_func(device, target,
441 callback_out, trans);
442 break;
443 default:
444 assert(false && "unreachable code");
445 break;
446 }
447
448 if (rc != EOK) {
449 async_answer_0(callid, rc);
450 async_transaction_destroy(trans);
451 }
452}
453
454
455void remote_usbhc_interrupt_out(device_t *device, void *iface,
456 ipc_callid_t callid, ipc_call_t *call)
457{
458 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
459 assert(usb_iface != NULL);
460
461 return remote_usbhc_out_transfer(device, callid, call,
462 usb_iface->interrupt_out);
463}
464
465void remote_usbhc_interrupt_in(device_t *device, void *iface,
466 ipc_callid_t callid, ipc_call_t *call)
467{
468 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
469 assert(usb_iface != NULL);
470
471 return remote_usbhc_in_transfer(device, callid, call,
472 usb_iface->interrupt_in);
473}
474
475void remote_usbhc_control_write_setup(device_t *device, void *iface,
476 ipc_callid_t callid, ipc_call_t *call)
477{
478 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
479 assert(usb_iface != NULL);
480
481 return remote_usbhc_out_transfer(device, callid, call,
482 usb_iface->control_write_setup);
483}
484
485void remote_usbhc_control_write_data(device_t *device, void *iface,
486 ipc_callid_t callid, ipc_call_t *call)
487{
488 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
489 assert(usb_iface != NULL);
490
491 return remote_usbhc_out_transfer(device, callid, call,
492 usb_iface->control_write_data);
493}
494
495void remote_usbhc_control_write_status(device_t *device, void *iface,
496 ipc_callid_t callid, ipc_call_t *call)
497{
498 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
499 assert(usb_iface != NULL);
500
501 return remote_usbhc_status_transfer(device, callid, call,
502 USB_DIRECTION_IN, usb_iface->control_write_status, NULL);
503}
504
505void remote_usbhc_control_read_setup(device_t *device, void *iface,
506 ipc_callid_t callid, ipc_call_t *call)
507{
508 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
509 assert(usb_iface != NULL);
510
511 return remote_usbhc_out_transfer(device, callid, call,
512 usb_iface->control_read_setup);
513}
514
515void remote_usbhc_control_read_data(device_t *device, void *iface,
516 ipc_callid_t callid, ipc_call_t *call)
517{
518 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
519 assert(usb_iface != NULL);
520
521 return remote_usbhc_in_transfer(device, callid, call,
522 usb_iface->control_read_data);
523}
524
525void remote_usbhc_control_read_status(device_t *device, void *iface,
526 ipc_callid_t callid, ipc_call_t *call)
527{
528 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
529 assert(usb_iface != NULL);
530
531 return remote_usbhc_status_transfer(device, callid, call,
532 USB_DIRECTION_OUT, NULL, usb_iface->control_read_status);
533}
534
535void remote_usbhc_control_write(device_t *device, void *iface,
536ipc_callid_t callid, ipc_call_t *call)
537{
538 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
539 assert(usb_iface != NULL);
540
541 if (!usb_iface->control_write) {
542 async_answer_0(callid, ENOTSUP);
543 return;
544 }
545
546 usb_target_t target = {
547 .address = DEV_IPC_GET_ARG1(*call),
548 .endpoint = DEV_IPC_GET_ARG2(*call)
549 };
550 size_t data_buffer_len = DEV_IPC_GET_ARG3(*call);
551
552 int rc;
553
554 void *setup_packet = NULL;
555 void *data_buffer = NULL;
556 size_t setup_packet_len = 0;
557
558 rc = async_data_write_accept(&setup_packet, false,
559 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
560 if (rc != EOK) {
561 async_answer_0(callid, rc);
562 return;
563 }
564
565 if (data_buffer_len > 0) {
566 rc = async_data_write_accept(&data_buffer, false,
567 1, USB_MAX_PAYLOAD_SIZE, 0, &data_buffer_len);
568 if (rc != EOK) {
569 async_answer_0(callid, rc);
570 free(setup_packet);
571 return;
572 }
573 }
574
575 async_transaction_t *trans = async_transaction_create(callid);
576 if (trans == NULL) {
577 async_answer_0(callid, ENOMEM);
578 free(setup_packet);
579 free(data_buffer);
580 return;
581 }
582 trans->setup_packet = setup_packet;
583 trans->buffer = data_buffer;
584 trans->size = data_buffer_len;
585
586 rc = usb_iface->control_write(device, target, HACK_MAX_PACKET_SIZE,
587 setup_packet, setup_packet_len,
588 data_buffer, data_buffer_len,
589 callback_out, trans);
590
591 if (rc != EOK) {
592 async_answer_0(callid, rc);
593 async_transaction_destroy(trans);
594 }
595}
596
597
598void remote_usbhc_control_read(device_t *device, void *iface,
599ipc_callid_t callid, ipc_call_t *call)
600{
601 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
602 assert(usb_iface != NULL);
603
604 if (!usb_iface->control_read) {
605 async_answer_0(callid, ENOTSUP);
606 return;
607 }
608
609 usb_target_t target = {
610 .address = DEV_IPC_GET_ARG1(*call),
611 .endpoint = DEV_IPC_GET_ARG2(*call)
612 };
613
614 int rc;
615
616 void *setup_packet = NULL;
617 size_t setup_packet_len = 0;
618 size_t data_len = 0;
619
620 rc = async_data_write_accept(&setup_packet, false,
621 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
622 if (rc != EOK) {
623 async_answer_0(callid, rc);
624 return;
625 }
626
627 ipc_callid_t data_callid;
628 if (!async_data_read_receive(&data_callid, &data_len)) {
629 async_answer_0(callid, EPARTY);
630 free(setup_packet);
631 return;
632 }
633
634 async_transaction_t *trans = async_transaction_create(callid);
635 if (trans == NULL) {
636 async_answer_0(callid, ENOMEM);
637 free(setup_packet);
638 return;
639 }
640 trans->data_caller = data_callid;
641 trans->setup_packet = setup_packet;
642 trans->size = data_len;
643 trans->buffer = malloc(data_len);
644 if (trans->buffer == NULL) {
645 async_answer_0(callid, ENOMEM);
646 async_transaction_destroy(trans);
647 return;
648 }
649
650 rc = usb_iface->control_read(device, target, HACK_MAX_PACKET_SIZE,
651 setup_packet, setup_packet_len,
652 trans->buffer, trans->size,
653 callback_in, trans);
654
655 if (rc != EOK) {
656 async_answer_0(callid, rc);
657 async_transaction_destroy(trans);
658 }
659}
660
661
662
663/**
664 * @}
665 */
Note: See TracBrowser for help on using the repository browser.