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

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

USBHC iface: remove default address reservation

This functionality is now handled via endpoint registration.

Also removed references to this function from hub driver. Either they
were unreachable or would break things anyway.

  • Property mode set to 100644
File size: 14.2 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) {
[17aca1c]304 async_answer_0(callid, ENOMEM);
[93f8da1]305 return;
306 }
[0a6fa9f]307 trans->data_caller = data_callid;
[1b22bd4]308 trans->buffer = malloc(len);
309 trans->size = len;
310
[7dfc06fa]311 int rc = transfer_func(fun, target,
[ec59693]312 trans->buffer, len,
[1b22bd4]313 callback_in, trans);
314
315 if (rc != EOK) {
[17aca1c]316 async_answer_0(callid, rc);
[93f8da1]317 async_transaction_destroy(trans);
[1b22bd4]318 }
[91db50ac]319}
320
[eb1a2f4]321void remote_usbhc_interrupt_out(ddf_fun_t *fun, void *iface,
[fb1dca09]322 ipc_callid_t callid, ipc_call_t *call)
[a3dfb2e]323{
324 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
[fb1dca09]325 assert(usb_iface != NULL);
[a3dfb2e]326
[eb1a2f4]327 return remote_usbhc_out_transfer(fun, callid, call,
[fb1dca09]328 usb_iface->interrupt_out);
329}
[a3dfb2e]330
[eb1a2f4]331void remote_usbhc_interrupt_in(ddf_fun_t *fun, void *iface,
[fb1dca09]332 ipc_callid_t callid, ipc_call_t *call)
333{
334 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
335 assert(usb_iface != NULL);
[a3dfb2e]336
[eb1a2f4]337 return remote_usbhc_in_transfer(fun, callid, call,
[fb1dca09]338 usb_iface->interrupt_in);
339}
[a3dfb2e]340
[eb1a2f4]341void remote_usbhc_bulk_out(ddf_fun_t *fun, void *iface,
[0a46c41e]342 ipc_callid_t callid, ipc_call_t *call)
343{
344 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
345 assert(usb_iface != NULL);
346
[eb1a2f4]347 return remote_usbhc_out_transfer(fun, callid, call,
[0a46c41e]348 usb_iface->bulk_out);
349}
350
[eb1a2f4]351void remote_usbhc_bulk_in(ddf_fun_t *fun, void *iface,
[0a46c41e]352 ipc_callid_t callid, ipc_call_t *call)
353{
354 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
355 assert(usb_iface != NULL);
356
[eb1a2f4]357 return remote_usbhc_in_transfer(fun, callid, call,
[0a46c41e]358 usb_iface->bulk_in);
359}
360
[eb1a2f4]361void remote_usbhc_control_write(ddf_fun_t *fun, void *iface,
[9753220]362ipc_callid_t callid, ipc_call_t *call)
363{
364 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
365 assert(usb_iface != NULL);
366
367 if (!usb_iface->control_write) {
[17aca1c]368 async_answer_0(callid, ENOTSUP);
[9753220]369 return;
370 }
371
372 usb_target_t target = {
373 .address = DEV_IPC_GET_ARG1(*call),
374 .endpoint = DEV_IPC_GET_ARG2(*call)
375 };
[3937bda]376 size_t data_buffer_len = DEV_IPC_GET_ARG3(*call);
[9753220]377
378 int rc;
379
380 void *setup_packet = NULL;
381 void *data_buffer = NULL;
382 size_t setup_packet_len = 0;
383
384 rc = async_data_write_accept(&setup_packet, false,
385 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
386 if (rc != EOK) {
[17aca1c]387 async_answer_0(callid, rc);
[9753220]388 return;
389 }
[3937bda]390
391 if (data_buffer_len > 0) {
392 rc = async_data_write_accept(&data_buffer, false,
393 1, USB_MAX_PAYLOAD_SIZE, 0, &data_buffer_len);
394 if (rc != EOK) {
395 async_answer_0(callid, rc);
396 free(setup_packet);
397 return;
398 }
[9753220]399 }
400
[93f8da1]401 async_transaction_t *trans = async_transaction_create(callid);
402 if (trans == NULL) {
[17aca1c]403 async_answer_0(callid, ENOMEM);
[93f8da1]404 free(setup_packet);
405 free(data_buffer);
406 return;
407 }
[9753220]408 trans->setup_packet = setup_packet;
409 trans->buffer = data_buffer;
410 trans->size = data_buffer_len;
411
[7dfc06fa]412 rc = usb_iface->control_write(fun, target,
[9753220]413 setup_packet, setup_packet_len,
414 data_buffer, data_buffer_len,
415 callback_out, trans);
416
417 if (rc != EOK) {
[17aca1c]418 async_answer_0(callid, rc);
[93f8da1]419 async_transaction_destroy(trans);
[9753220]420 }
421}
422
423
[eb1a2f4]424void remote_usbhc_control_read(ddf_fun_t *fun, void *iface,
[9753220]425ipc_callid_t callid, ipc_call_t *call)
426{
427 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
428 assert(usb_iface != NULL);
429
430 if (!usb_iface->control_read) {
[17aca1c]431 async_answer_0(callid, ENOTSUP);
[9753220]432 return;
433 }
434
435 usb_target_t target = {
436 .address = DEV_IPC_GET_ARG1(*call),
437 .endpoint = DEV_IPC_GET_ARG2(*call)
438 };
439
440 int rc;
441
442 void *setup_packet = NULL;
443 size_t setup_packet_len = 0;
[3937bda]444 size_t data_len = 0;
[9753220]445
446 rc = async_data_write_accept(&setup_packet, false,
447 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
448 if (rc != EOK) {
[17aca1c]449 async_answer_0(callid, rc);
[9753220]450 return;
451 }
452
[0a6fa9f]453 ipc_callid_t data_callid;
454 if (!async_data_read_receive(&data_callid, &data_len)) {
455 async_answer_0(callid, EPARTY);
456 free(setup_packet);
457 return;
458 }
459
[93f8da1]460 async_transaction_t *trans = async_transaction_create(callid);
461 if (trans == NULL) {
[17aca1c]462 async_answer_0(callid, ENOMEM);
[93f8da1]463 free(setup_packet);
464 return;
465 }
[0a6fa9f]466 trans->data_caller = data_callid;
[9753220]467 trans->setup_packet = setup_packet;
468 trans->size = data_len;
[93f8da1]469 trans->buffer = malloc(data_len);
470 if (trans->buffer == NULL) {
[17aca1c]471 async_answer_0(callid, ENOMEM);
[93f8da1]472 async_transaction_destroy(trans);
473 return;
474 }
[9753220]475
[7dfc06fa]476 rc = usb_iface->control_read(fun, target,
[9753220]477 setup_packet, setup_packet_len,
478 trans->buffer, trans->size,
479 callback_in, trans);
480
481 if (rc != EOK) {
[17aca1c]482 async_answer_0(callid, rc);
[93f8da1]483 async_transaction_destroy(trans);
[9753220]484 }
485}
486
[a3dfb2e]487
[b7d8fd9]488void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
489 ipc_callid_t callid, ipc_call_t *call)
490{
491 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
492
493 if (!usb_iface->register_endpoint) {
494 async_answer_0(callid, ENOTSUP);
495 return;
496 }
497
[1998bcd]498#define _INIT_FROM_HIGH_DATA2(type, var, arg_no) \
499 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) / (1 << 16)
500#define _INIT_FROM_LOW_DATA2(type, var, arg_no) \
501 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) % (1 << 16)
502#define _INIT_FROM_HIGH_DATA3(type, var, arg_no) \
503 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) / (1 << 16)
504#define _INIT_FROM_MIDDLE_DATA3(type, var, arg_no) \
505 type var = (type) (DEV_IPC_GET_ARG##arg_no(*call) / (1 << 8)) % (1 << 8)
506#define _INIT_FROM_LOW_DATA3(type, var, arg_no) \
507 type var = (type) DEV_IPC_GET_ARG##arg_no(*call) % (1 << 8)
508
509 _INIT_FROM_HIGH_DATA2(usb_address_t, address, 1);
510 _INIT_FROM_LOW_DATA2(usb_endpoint_t, endpoint, 1);
511
512 _INIT_FROM_HIGH_DATA3(usb_speed_t, speed, 2);
513 _INIT_FROM_MIDDLE_DATA3(usb_transfer_type_t, transfer_type, 2);
514 _INIT_FROM_LOW_DATA3(usb_direction_t, direction, 2);
515
516 _INIT_FROM_HIGH_DATA2(size_t, max_packet_size, 3);
517 _INIT_FROM_LOW_DATA2(unsigned int, interval, 3);
518
519#undef _INIT_FROM_HIGH_DATA2
520#undef _INIT_FROM_LOW_DATA2
521#undef _INIT_FROM_HIGH_DATA3
522#undef _INIT_FROM_MIDDLE_DATA3
523#undef _INIT_FROM_LOW_DATA3
524
525 int rc = usb_iface->register_endpoint(fun, address, speed, endpoint,
[b7d8fd9]526 transfer_type, direction, max_packet_size, interval);
527
528 async_answer_0(callid, rc);
529}
530
531
532void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
533 ipc_callid_t callid, ipc_call_t *call)
534{
535 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
536
537 if (!usb_iface->unregister_endpoint) {
538 async_answer_0(callid, ENOTSUP);
539 return;
540 }
541
542 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
543 usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG2(*call);
544 usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG3(*call);
545
546 int rc = usb_iface->unregister_endpoint(fun,
547 address, endpoint, direction);
548
549 async_answer_0(callid, rc);
550}
551
[1b22bd4]552
[91db50ac]553/**
554 * @}
555 */
Note: See TracBrowser for help on using the repository browser.