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

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

Fix call leakage

Data read call is always answered.

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