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

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

Add bulk transfers to libusb

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