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

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

Fix unanswered IPC calls (#216)

  • Property mode set to 100644
File size: 14.4 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#include <assert.h>
38
39#include "usbhc_iface.h"
40#include "ddf/driver.h"
41
42#define USB_MAX_PAYLOAD_SIZE 1020
43#define HACK_MAX_PACKET_SIZE 8
44#define HACK_MAX_PACKET_SIZE_INTERRUPT_IN 4
45
46static void remote_usbhc_interrupt_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
47static void remote_usbhc_interrupt_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
48static void remote_usbhc_bulk_out(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
49static void remote_usbhc_bulk_in(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
50static void remote_usbhc_control_write(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
51static void remote_usbhc_control_read(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
52static void remote_usbhc_request_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
53static void remote_usbhc_bind_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
54static void remote_usbhc_release_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
55static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
56static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
57//static void remote_usbhc(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
58
59/** Remote USB host controller interface operations. */
60static remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
61 remote_usbhc_request_address,
62 remote_usbhc_bind_address,
63 remote_usbhc_release_address,
64
65 remote_usbhc_interrupt_out,
66 remote_usbhc_interrupt_in,
67
68 remote_usbhc_bulk_out,
69 remote_usbhc_bulk_in,
70
71 remote_usbhc_control_write,
72 remote_usbhc_control_read,
73
74 remote_usbhc_register_endpoint,
75 remote_usbhc_unregister_endpoint
76};
77
78/** Remote USB host controller interface structure.
79 */
80remote_iface_t remote_usbhc_iface = {
81 .method_count = sizeof(remote_usbhc_iface_ops) /
82 sizeof(remote_usbhc_iface_ops[0]),
83 .methods = remote_usbhc_iface_ops
84};
85
86typedef struct {
87 ipc_callid_t caller;
88 ipc_callid_t data_caller;
89 void *buffer;
90 void *setup_packet;
91 size_t size;
92} async_transaction_t;
93
94static void async_transaction_destroy(async_transaction_t *trans)
95{
96 if (trans == NULL) {
97 return;
98 }
99
100 if (trans->setup_packet != NULL) {
101 free(trans->setup_packet);
102 }
103 if (trans->buffer != NULL) {
104 free(trans->buffer);
105 }
106
107 free(trans);
108}
109
110static async_transaction_t *async_transaction_create(ipc_callid_t caller)
111{
112 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
113 if (trans == NULL) {
114 return NULL;
115 }
116
117 trans->caller = caller;
118 trans->data_caller = 0;
119 trans->buffer = NULL;
120 trans->setup_packet = NULL;
121 trans->size = 0;
122
123 return trans;
124}
125
126void remote_usbhc_request_address(ddf_fun_t *fun, void *iface,
127 ipc_callid_t callid, ipc_call_t *call)
128{
129 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
130
131 if (!usb_iface->request_address) {
132 async_answer_0(callid, ENOTSUP);
133 return;
134 }
135
136 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
137
138 usb_address_t address;
139 int rc = usb_iface->request_address(fun, speed, &address);
140 if (rc != EOK) {
141 async_answer_0(callid, rc);
142 } else {
143 async_answer_1(callid, EOK, (sysarg_t) address);
144 }
145}
146
147void remote_usbhc_bind_address(ddf_fun_t *fun, void *iface,
148 ipc_callid_t callid, ipc_call_t *call)
149{
150 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
151
152 if (!usb_iface->bind_address) {
153 async_answer_0(callid, ENOTSUP);
154 return;
155 }
156
157 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
158 devman_handle_t handle = (devman_handle_t) DEV_IPC_GET_ARG2(*call);
159
160 int rc = usb_iface->bind_address(fun, address, handle);
161
162 async_answer_0(callid, rc);
163}
164
165void remote_usbhc_release_address(ddf_fun_t *fun, void *iface,
166 ipc_callid_t callid, ipc_call_t *call)
167{
168 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
169
170 if (!usb_iface->release_address) {
171 async_answer_0(callid, ENOTSUP);
172 return;
173 }
174
175 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
176
177 int rc = usb_iface->release_address(fun, address);
178
179 async_answer_0(callid, rc);
180}
181
182
183static void callback_out(ddf_fun_t *fun,
184 int outcome, void *arg)
185{
186 async_transaction_t *trans = (async_transaction_t *)arg;
187
188 async_answer_0(trans->caller, outcome);
189
190 async_transaction_destroy(trans);
191}
192
193static void callback_in(ddf_fun_t *fun,
194 int outcome, size_t actual_size, void *arg)
195{
196 async_transaction_t *trans = (async_transaction_t *)arg;
197
198 if (outcome != EOK) {
199 async_answer_0(trans->caller, outcome);
200 if (trans->data_caller) {
201 async_answer_0(trans->data_caller, EINTR);
202 }
203 async_transaction_destroy(trans);
204 return;
205 }
206
207 trans->size = actual_size;
208
209 if (trans->data_caller) {
210 async_data_read_finalize(trans->data_caller,
211 trans->buffer, actual_size);
212 }
213
214 async_answer_0(trans->caller, EOK);
215
216 async_transaction_destroy(trans);
217}
218
219/** Process an outgoing transfer (both OUT and SETUP).
220 *
221 * @param device Target device.
222 * @param callid Initiating caller.
223 * @param call Initiating call.
224 * @param transfer_func Transfer function (might be NULL).
225 */
226static void remote_usbhc_out_transfer(ddf_fun_t *fun,
227 ipc_callid_t callid, ipc_call_t *call,
228 usbhc_iface_transfer_out_t transfer_func)
229{
230 if (!transfer_func) {
231 async_answer_0(callid, ENOTSUP);
232 return;
233 }
234
235 usb_target_t target = {
236 .address = DEV_IPC_GET_ARG1(*call),
237 .endpoint = DEV_IPC_GET_ARG2(*call)
238 };
239
240 size_t len = 0;
241 void *buffer = NULL;
242
243 int rc = async_data_write_accept(&buffer, false,
244 1, USB_MAX_PAYLOAD_SIZE,
245 0, &len);
246
247 if (rc != EOK) {
248 async_answer_0(callid, rc);
249 return;
250 }
251
252 async_transaction_t *trans = async_transaction_create(callid);
253 if (trans == NULL) {
254 if (buffer != NULL) {
255 free(buffer);
256 }
257 async_answer_0(callid, ENOMEM);
258 return;
259 }
260
261 trans->buffer = buffer;
262 trans->size = len;
263
264 rc = transfer_func(fun, target,
265 buffer, len,
266 callback_out, trans);
267
268 if (rc != EOK) {
269 async_answer_0(callid, rc);
270 async_transaction_destroy(trans);
271 }
272}
273
274/** Process an incoming transfer.
275 *
276 * @param device Target device.
277 * @param callid Initiating caller.
278 * @param call Initiating call.
279 * @param transfer_func Transfer function (might be NULL).
280 */
281static void remote_usbhc_in_transfer(ddf_fun_t *fun,
282 ipc_callid_t callid, ipc_call_t *call,
283 usbhc_iface_transfer_in_t transfer_func)
284{
285 if (!transfer_func) {
286 async_answer_0(callid, ENOTSUP);
287 return;
288 }
289
290 usb_target_t target = {
291 .address = DEV_IPC_GET_ARG1(*call),
292 .endpoint = DEV_IPC_GET_ARG2(*call)
293 };
294
295 size_t len;
296 ipc_callid_t data_callid;
297 if (!async_data_read_receive(&data_callid, &len)) {
298 async_answer_0(callid, EPARTY);
299 return;
300 }
301
302 async_transaction_t *trans = async_transaction_create(callid);
303 if (trans == NULL) {
304 async_answer_0(data_callid, ENOMEM);
305 async_answer_0(callid, ENOMEM);
306 return;
307 }
308 trans->data_caller = data_callid;
309 trans->buffer = malloc(len);
310 trans->size = len;
311
312 int rc = transfer_func(fun, target,
313 trans->buffer, len,
314 callback_in, trans);
315
316 if (rc != EOK) {
317 async_answer_0(data_callid, rc);
318 async_answer_0(callid, rc);
319 async_transaction_destroy(trans);
320 }
321}
322
323void remote_usbhc_interrupt_out(ddf_fun_t *fun, void *iface,
324 ipc_callid_t callid, ipc_call_t *call)
325{
326 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
327 assert(usb_iface != NULL);
328
329 return remote_usbhc_out_transfer(fun, callid, call,
330 usb_iface->interrupt_out);
331}
332
333void remote_usbhc_interrupt_in(ddf_fun_t *fun, void *iface,
334 ipc_callid_t callid, ipc_call_t *call)
335{
336 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
337 assert(usb_iface != NULL);
338
339 return remote_usbhc_in_transfer(fun, callid, call,
340 usb_iface->interrupt_in);
341}
342
343void remote_usbhc_bulk_out(ddf_fun_t *fun, void *iface,
344 ipc_callid_t callid, ipc_call_t *call)
345{
346 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
347 assert(usb_iface != NULL);
348
349 return remote_usbhc_out_transfer(fun, callid, call,
350 usb_iface->bulk_out);
351}
352
353void remote_usbhc_bulk_in(ddf_fun_t *fun, void *iface,
354 ipc_callid_t callid, ipc_call_t *call)
355{
356 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
357 assert(usb_iface != NULL);
358
359 return remote_usbhc_in_transfer(fun, callid, call,
360 usb_iface->bulk_in);
361}
362
363void remote_usbhc_control_write(ddf_fun_t *fun, void *iface,
364ipc_callid_t callid, ipc_call_t *call)
365{
366 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
367 assert(usb_iface != NULL);
368
369 if (!usb_iface->control_write) {
370 async_answer_0(callid, ENOTSUP);
371 return;
372 }
373
374 usb_target_t target = {
375 .address = DEV_IPC_GET_ARG1(*call),
376 .endpoint = DEV_IPC_GET_ARG2(*call)
377 };
378 size_t data_buffer_len = DEV_IPC_GET_ARG3(*call);
379
380 int rc;
381
382 void *setup_packet = NULL;
383 void *data_buffer = NULL;
384 size_t setup_packet_len = 0;
385
386 rc = async_data_write_accept(&setup_packet, false,
387 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
388 if (rc != EOK) {
389 async_answer_0(callid, rc);
390 return;
391 }
392
393 if (data_buffer_len > 0) {
394 rc = async_data_write_accept(&data_buffer, false,
395 1, USB_MAX_PAYLOAD_SIZE, 0, &data_buffer_len);
396 if (rc != EOK) {
397 async_answer_0(callid, rc);
398 free(setup_packet);
399 return;
400 }
401 }
402
403 async_transaction_t *trans = async_transaction_create(callid);
404 if (trans == NULL) {
405 async_answer_0(callid, ENOMEM);
406 free(setup_packet);
407 free(data_buffer);
408 return;
409 }
410 trans->setup_packet = setup_packet;
411 trans->buffer = data_buffer;
412 trans->size = data_buffer_len;
413
414 rc = usb_iface->control_write(fun, target,
415 setup_packet, setup_packet_len,
416 data_buffer, data_buffer_len,
417 callback_out, trans);
418
419 if (rc != EOK) {
420 async_answer_0(callid, rc);
421 async_transaction_destroy(trans);
422 }
423}
424
425
426void remote_usbhc_control_read(ddf_fun_t *fun, void *iface,
427ipc_callid_t callid, ipc_call_t *call)
428{
429 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
430 assert(usb_iface != NULL);
431
432 if (!usb_iface->control_read) {
433 async_answer_0(callid, ENOTSUP);
434 return;
435 }
436
437 usb_target_t target = {
438 .address = DEV_IPC_GET_ARG1(*call),
439 .endpoint = DEV_IPC_GET_ARG2(*call)
440 };
441
442 int rc;
443
444 void *setup_packet = NULL;
445 size_t setup_packet_len = 0;
446 size_t data_len = 0;
447
448 rc = async_data_write_accept(&setup_packet, false,
449 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
450 if (rc != EOK) {
451 async_answer_0(callid, rc);
452 return;
453 }
454
455 ipc_callid_t data_callid;
456 if (!async_data_read_receive(&data_callid, &data_len)) {
457 async_answer_0(callid, EPARTY);
458 free(setup_packet);
459 return;
460 }
461
462 async_transaction_t *trans = async_transaction_create(callid);
463 if (trans == NULL) {
464 async_answer_0(data_callid, ENOMEM);
465 async_answer_0(callid, ENOMEM);
466 free(setup_packet);
467 return;
468 }
469 trans->data_caller = data_callid;
470 trans->setup_packet = setup_packet;
471 trans->size = data_len;
472 trans->buffer = malloc(data_len);
473 if (trans->buffer == NULL) {
474 async_answer_0(data_callid, ENOMEM);
475 async_answer_0(callid, ENOMEM);
476 async_transaction_destroy(trans);
477 return;
478 }
479
480 rc = usb_iface->control_read(fun, target,
481 setup_packet, setup_packet_len,
482 trans->buffer, trans->size,
483 callback_in, trans);
484
485 if (rc != EOK) {
486 async_answer_0(data_callid, rc);
487 async_answer_0(callid, rc);
488 async_transaction_destroy(trans);
489 }
490}
491
492
493void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
494 ipc_callid_t callid, ipc_call_t *call)
495{
496 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
497
498 if (!usb_iface->register_endpoint) {
499 async_answer_0(callid, ENOTSUP);
500 return;
501 }
502
503#define _INIT_FROM_HIGH_DATA2(type, var, arg_no) \
504 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) / (1 << 16)
505#define _INIT_FROM_LOW_DATA2(type, var, arg_no) \
506 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) % (1 << 16)
507#define _INIT_FROM_HIGH_DATA3(type, var, arg_no) \
508 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) / (1 << 16)
509#define _INIT_FROM_MIDDLE_DATA3(type, var, arg_no) \
510 type var = (type) (DEV_IPC_GET_ARG##arg_no(*call) / (1 << 8)) % (1 << 8)
511#define _INIT_FROM_LOW_DATA3(type, var, arg_no) \
512 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) % (1 << 8)
513
514 _INIT_FROM_HIGH_DATA2(usb_address_t, address, 1);
515 _INIT_FROM_LOW_DATA2(usb_endpoint_t, endpoint, 1);
516
517 _INIT_FROM_HIGH_DATA3(usb_speed_t, speed, 2);
518 _INIT_FROM_MIDDLE_DATA3(usb_transfer_type_t, transfer_type, 2);
519 _INIT_FROM_LOW_DATA3(usb_direction_t, direction, 2);
520
521 _INIT_FROM_HIGH_DATA2(size_t, max_packet_size, 3);
522 _INIT_FROM_LOW_DATA2(unsigned int, interval, 3);
523
524#undef _INIT_FROM_HIGH_DATA2
525#undef _INIT_FROM_LOW_DATA2
526#undef _INIT_FROM_HIGH_DATA3
527#undef _INIT_FROM_MIDDLE_DATA3
528#undef _INIT_FROM_LOW_DATA3
529
530 int rc = usb_iface->register_endpoint(fun, address, speed, endpoint,
531 transfer_type, direction, max_packet_size, interval);
532
533 async_answer_0(callid, rc);
534}
535
536
537void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
538 ipc_callid_t callid, ipc_call_t *call)
539{
540 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
541
542 if (!usb_iface->unregister_endpoint) {
543 async_answer_0(callid, ENOTSUP);
544 return;
545 }
546
547 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
548 usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG2(*call);
549 usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG3(*call);
550
551 int rc = usb_iface->unregister_endpoint(fun,
552 address, endpoint, direction);
553
554 async_answer_0(callid, rc);
555}
556
557
558/**
559 * @}
560 */
Note: See TracBrowser for help on using the repository browser.