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

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

Merge mainline changes

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