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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 84de606d 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
RevLine 
[91db50ac]1/*
[9753220]2 * Copyright (c) 2010-2011 Vojtech Horky
[91db50ac]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>
[eb1a2f4]37#include <assert.h>
[91db50ac]38
[cb59f787]39#include "usbhc_iface.h"
[eb1a2f4]40#include "ddf/driver.h"
[91db50ac]41
[1b22bd4]42#define USB_MAX_PAYLOAD_SIZE 1020
[ec59693]43#define HACK_MAX_PACKET_SIZE 8
44#define HACK_MAX_PACKET_SIZE_INTERRUPT_IN 4
[1b22bd4]45
[eb1a2f4]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 *);
[b7d8fd9]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 *);
[eb1a2f4]57//static void remote_usbhc(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
[91db50ac]58
[6edd494]59/** Remote USB host controller interface operations. */
[cb59f787]60static remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
[6f04905]61 remote_usbhc_request_address,
[4689d40]62 remote_usbhc_bind_address,
[6f04905]63 remote_usbhc_release_address,
64
[aae339e9]65 remote_usbhc_interrupt_out,
[a3dfb2e]66 remote_usbhc_interrupt_in,
[6f04905]67
[0a46c41e]68 remote_usbhc_bulk_out,
69 remote_usbhc_bulk_in,
70
[9753220]71 remote_usbhc_control_write,
[b7d8fd9]72 remote_usbhc_control_read,
73
74 remote_usbhc_register_endpoint,
75 remote_usbhc_unregister_endpoint
[91db50ac]76};
77
[6edd494]78/** Remote USB host controller interface structure.
[91db50ac]79 */
[cb59f787]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
[91db50ac]84};
85
[1b22bd4]86typedef struct {
87 ipc_callid_t caller;
[0a6fa9f]88 ipc_callid_t data_caller;
[1b22bd4]89 void *buffer;
[9753220]90 void *setup_packet;
[1b22bd4]91 size_t size;
92} async_transaction_t;
[91db50ac]93
[93f8da1]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;
[0a6fa9f]118 trans->data_caller = 0;
[93f8da1]119 trans->buffer = NULL;
120 trans->setup_packet = NULL;
121 trans->size = 0;
122
123 return trans;
124}
125
[eb1a2f4]126void remote_usbhc_request_address(ddf_fun_t *fun, void *iface,
[6f04905]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) {
[17aca1c]132 async_answer_0(callid, ENOTSUP);
[6f04905]133 return;
134 }
[6427cf67]135
[fa48ebe]136 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
[6f04905]137
138 usb_address_t address;
[eb1a2f4]139 int rc = usb_iface->request_address(fun, speed, &address);
[6f04905]140 if (rc != EOK) {
[17aca1c]141 async_answer_0(callid, rc);
[6f04905]142 } else {
[17aca1c]143 async_answer_1(callid, EOK, (sysarg_t) address);
[6f04905]144 }
145}
146
[eb1a2f4]147void remote_usbhc_bind_address(ddf_fun_t *fun, void *iface,
[4689d40]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) {
[17aca1c]153 async_answer_0(callid, ENOTSUP);
[4689d40]154 return;
155 }
156
[eac610e]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);
[4689d40]159
[eb1a2f4]160 int rc = usb_iface->bind_address(fun, address, handle);
[4689d40]161
[17aca1c]162 async_answer_0(callid, rc);
[4689d40]163}
164
[eb1a2f4]165void remote_usbhc_release_address(ddf_fun_t *fun, void *iface,
[6f04905]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) {
[17aca1c]171 async_answer_0(callid, ENOTSUP);
[6f04905]172 return;
173 }
174
[eac610e]175 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
[6f04905]176
[eb1a2f4]177 int rc = usb_iface->release_address(fun, address);
[6f04905]178
[17aca1c]179 async_answer_0(callid, rc);
[6f04905]180}
181
[1b22bd4]182
[eb1a2f4]183static void callback_out(ddf_fun_t *fun,
[daec5e04]184 int outcome, void *arg)
[1b22bd4]185{
186 async_transaction_t *trans = (async_transaction_t *)arg;
187
[17aca1c]188 async_answer_0(trans->caller, outcome);
[1b22bd4]189
[93f8da1]190 async_transaction_destroy(trans);
[1b22bd4]191}
192
[eb1a2f4]193static void callback_in(ddf_fun_t *fun,
[daec5e04]194 int outcome, size_t actual_size, void *arg)
[1b22bd4]195{
196 async_transaction_t *trans = (async_transaction_t *)arg;
197
[daec5e04]198 if (outcome != EOK) {
[17aca1c]199 async_answer_0(trans->caller, outcome);
[5842493]200 if (trans->data_caller) {
201 async_answer_0(trans->data_caller, EINTR);
202 }
[93f8da1]203 async_transaction_destroy(trans);
204 return;
205 }
[1b22bd4]206
207 trans->size = actual_size;
[0a6fa9f]208
209 if (trans->data_caller) {
210 async_data_read_finalize(trans->data_caller,
211 trans->buffer, actual_size);
212 }
213
[daec5e04]214 async_answer_0(trans->caller, EOK);
[1e64b250]215
216 async_transaction_destroy(trans);
[91db50ac]217}
218
[fb1dca09]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 */
[eb1a2f4]226static void remote_usbhc_out_transfer(ddf_fun_t *fun,
[fb1dca09]227 ipc_callid_t callid, ipc_call_t *call,
228 usbhc_iface_transfer_out_t transfer_func)
[91db50ac]229{
[fb1dca09]230 if (!transfer_func) {
[17aca1c]231 async_answer_0(callid, ENOTSUP);
[fb1dca09]232 return;
233 }
[1b22bd4]234
235 usb_target_t target = {
[eac610e]236 .address = DEV_IPC_GET_ARG1(*call),
237 .endpoint = DEV_IPC_GET_ARG2(*call)
[1b22bd4]238 };
239
240 size_t len = 0;
241 void *buffer = NULL;
242
[228f251]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;
[1b22bd4]250 }
251
[93f8da1]252 async_transaction_t *trans = async_transaction_create(callid);
253 if (trans == NULL) {
254 if (buffer != NULL) {
255 free(buffer);
256 }
[17aca1c]257 async_answer_0(callid, ENOMEM);
[93f8da1]258 return;
259 }
260
[fb1dca09]261 trans->buffer = buffer;
262 trans->size = len;
[1b22bd4]263
[7dfc06fa]264 rc = transfer_func(fun, target,
[ec59693]265 buffer, len,
[1b22bd4]266 callback_out, trans);
267
268 if (rc != EOK) {
[17aca1c]269 async_answer_0(callid, rc);
[93f8da1]270 async_transaction_destroy(trans);
[1b22bd4]271 }
[91db50ac]272}
273
[fb1dca09]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 */
[eb1a2f4]281static void remote_usbhc_in_transfer(ddf_fun_t *fun,
[fb1dca09]282 ipc_callid_t callid, ipc_call_t *call,
283 usbhc_iface_transfer_in_t transfer_func)
[91db50ac]284{
[fb1dca09]285 if (!transfer_func) {
[17aca1c]286 async_answer_0(callid, ENOTSUP);
[fb1dca09]287 return;
288 }
[1b22bd4]289
290 usb_target_t target = {
[eac610e]291 .address = DEV_IPC_GET_ARG1(*call),
292 .endpoint = DEV_IPC_GET_ARG2(*call)
[1b22bd4]293 };
294
[228f251]295 size_t len;
[0a6fa9f]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
[93f8da1]302 async_transaction_t *trans = async_transaction_create(callid);
303 if (trans == NULL) {
[6985b4e]304 async_answer_0(data_callid, ENOMEM);
[17aca1c]305 async_answer_0(callid, ENOMEM);
[93f8da1]306 return;
307 }
[0a6fa9f]308 trans->data_caller = data_callid;
[1b22bd4]309 trans->buffer = malloc(len);
310 trans->size = len;
311
[7dfc06fa]312 int rc = transfer_func(fun, target,
[ec59693]313 trans->buffer, len,
[1b22bd4]314 callback_in, trans);
315
316 if (rc != EOK) {
[6985b4e]317 async_answer_0(data_callid, rc);
[17aca1c]318 async_answer_0(callid, rc);
[93f8da1]319 async_transaction_destroy(trans);
[1b22bd4]320 }
[91db50ac]321}
322
[eb1a2f4]323void remote_usbhc_interrupt_out(ddf_fun_t *fun, void *iface,
[fb1dca09]324 ipc_callid_t callid, ipc_call_t *call)
[a3dfb2e]325{
326 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
[fb1dca09]327 assert(usb_iface != NULL);
[a3dfb2e]328
[eb1a2f4]329 return remote_usbhc_out_transfer(fun, callid, call,
[fb1dca09]330 usb_iface->interrupt_out);
331}
[a3dfb2e]332
[eb1a2f4]333void remote_usbhc_interrupt_in(ddf_fun_t *fun, void *iface,
[fb1dca09]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);
[a3dfb2e]338
[eb1a2f4]339 return remote_usbhc_in_transfer(fun, callid, call,
[fb1dca09]340 usb_iface->interrupt_in);
341}
[a3dfb2e]342
[eb1a2f4]343void remote_usbhc_bulk_out(ddf_fun_t *fun, void *iface,
[0a46c41e]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
[eb1a2f4]349 return remote_usbhc_out_transfer(fun, callid, call,
[0a46c41e]350 usb_iface->bulk_out);
351}
352
[eb1a2f4]353void remote_usbhc_bulk_in(ddf_fun_t *fun, void *iface,
[0a46c41e]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
[eb1a2f4]359 return remote_usbhc_in_transfer(fun, callid, call,
[0a46c41e]360 usb_iface->bulk_in);
361}
362
[eb1a2f4]363void remote_usbhc_control_write(ddf_fun_t *fun, void *iface,
[9753220]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) {
[17aca1c]370 async_answer_0(callid, ENOTSUP);
[9753220]371 return;
372 }
373
374 usb_target_t target = {
375 .address = DEV_IPC_GET_ARG1(*call),
376 .endpoint = DEV_IPC_GET_ARG2(*call)
377 };
[3937bda]378 size_t data_buffer_len = DEV_IPC_GET_ARG3(*call);
[9753220]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) {
[17aca1c]389 async_answer_0(callid, rc);
[9753220]390 return;
391 }
[3937bda]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 }
[9753220]401 }
402
[93f8da1]403 async_transaction_t *trans = async_transaction_create(callid);
404 if (trans == NULL) {
[17aca1c]405 async_answer_0(callid, ENOMEM);
[93f8da1]406 free(setup_packet);
407 free(data_buffer);
408 return;
409 }
[9753220]410 trans->setup_packet = setup_packet;
411 trans->buffer = data_buffer;
412 trans->size = data_buffer_len;
413
[7dfc06fa]414 rc = usb_iface->control_write(fun, target,
[9753220]415 setup_packet, setup_packet_len,
416 data_buffer, data_buffer_len,
417 callback_out, trans);
418
419 if (rc != EOK) {
[17aca1c]420 async_answer_0(callid, rc);
[93f8da1]421 async_transaction_destroy(trans);
[9753220]422 }
423}
424
425
[eb1a2f4]426void remote_usbhc_control_read(ddf_fun_t *fun, void *iface,
[9753220]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) {
[17aca1c]433 async_answer_0(callid, ENOTSUP);
[9753220]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;
[3937bda]446 size_t data_len = 0;
[9753220]447
448 rc = async_data_write_accept(&setup_packet, false,
449 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
450 if (rc != EOK) {
[17aca1c]451 async_answer_0(callid, rc);
[9753220]452 return;
453 }
454
[0a6fa9f]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
[93f8da1]462 async_transaction_t *trans = async_transaction_create(callid);
463 if (trans == NULL) {
[6985b4e]464 async_answer_0(data_callid, ENOMEM);
[17aca1c]465 async_answer_0(callid, ENOMEM);
[93f8da1]466 free(setup_packet);
467 return;
468 }
[0a6fa9f]469 trans->data_caller = data_callid;
[9753220]470 trans->setup_packet = setup_packet;
471 trans->size = data_len;
[93f8da1]472 trans->buffer = malloc(data_len);
473 if (trans->buffer == NULL) {
[6985b4e]474 async_answer_0(data_callid, ENOMEM);
[17aca1c]475 async_answer_0(callid, ENOMEM);
[93f8da1]476 async_transaction_destroy(trans);
477 return;
478 }
[9753220]479
[7dfc06fa]480 rc = usb_iface->control_read(fun, target,
[9753220]481 setup_packet, setup_packet_len,
482 trans->buffer, trans->size,
483 callback_in, trans);
484
485 if (rc != EOK) {
[6985b4e]486 async_answer_0(data_callid, rc);
[17aca1c]487 async_answer_0(callid, rc);
[93f8da1]488 async_transaction_destroy(trans);
[9753220]489 }
490}
491
[a3dfb2e]492
[b7d8fd9]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
[1998bcd]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,
[b7d8fd9]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
[1b22bd4]557
[91db50ac]558/**
559 * @}
560 */
Note: See TracBrowser for help on using the repository browser.