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

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

USB HC interface has max packet size

Currently, it is only a hack to extend the interface as the values
are hard coded inside the remote part of the interface.

Sending of real values will be added once the old drivers API is
completely removed (no sense in changing the same thing twice).

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