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

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

Fix memory leaks in USBHC remote interface

Also added handling of "out of memory" cases.

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