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
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
38#include "usbhc_iface.h"
39#include "driver.h"
40
41#define USB_MAX_PAYLOAD_SIZE 1020
42#define HACK_MAX_PACKET_SIZE 8
43#define HACK_MAX_PACKET_SIZE_INTERRUPT_IN 4
44
45static void remote_usbhc_get_address(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_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 *);
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 *);
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 *);
55static void remote_usbhc_bind_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
56static void remote_usbhc_release_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
57//static void remote_usbhc(device_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_get_address,
62
63 remote_usbhc_reserve_default_address,
64 remote_usbhc_release_default_address,
65
66 remote_usbhc_request_address,
67 remote_usbhc_bind_address,
68 remote_usbhc_release_address,
69
70 remote_usbhc_interrupt_out,
71 remote_usbhc_interrupt_in,
72
73 remote_usbhc_bulk_out,
74 remote_usbhc_bulk_in,
75
76 remote_usbhc_control_write,
77 remote_usbhc_control_read
78};
79
80/** Remote USB host controller interface structure.
81 */
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
86};
87
88typedef struct {
89 ipc_callid_t caller;
90 ipc_callid_t data_caller;
91 void *buffer;
92 void *setup_packet;
93 size_t size;
94} async_transaction_t;
95
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;
120 trans->data_caller = 0;
121 trans->buffer = NULL;
122 trans->setup_packet = NULL;
123 trans->size = 0;
124
125 return trans;
126}
127
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) {
134 async_answer_0(callid, ENOTSUP);
135 return;
136 }
137
138 devman_handle_t handle = DEV_IPC_GET_ARG1(*call);
139
140 usb_address_t address;
141 int rc = usb_iface->tell_address(device, handle, &address);
142 if (rc != EOK) {
143 async_answer_0(callid, rc);
144 } else {
145 async_answer_1(callid, EOK, address);
146 }
147}
148
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) {
155 async_answer_0(callid, ENOTSUP);
156 return;
157 }
158
159 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
160
161 int rc = usb_iface->reserve_default_address(device, speed);
162
163 async_answer_0(callid, rc);
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) {
172 async_answer_0(callid, ENOTSUP);
173 return;
174 }
175
176 int rc = usb_iface->release_default_address(device);
177
178 async_answer_0(callid, rc);
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) {
187 async_answer_0(callid, ENOTSUP);
188 return;
189 }
190
191 usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
192
193 usb_address_t address;
194 int rc = usb_iface->request_address(device, speed, &address);
195 if (rc != EOK) {
196 async_answer_0(callid, rc);
197 } else {
198 async_answer_1(callid, EOK, (sysarg_t) address);
199 }
200}
201
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) {
208 async_answer_0(callid, ENOTSUP);
209 return;
210 }
211
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);
214
215 int rc = usb_iface->bind_address(device, address, handle);
216
217 async_answer_0(callid, rc);
218}
219
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) {
226 async_answer_0(callid, ENOTSUP);
227 return;
228 }
229
230 usb_address_t address = (usb_address_t) DEV_IPC_GET_ARG1(*call);
231
232 int rc = usb_iface->release_address(device, address);
233
234 async_answer_0(callid, rc);
235}
236
237
238static void callback_out(device_t *device,
239 int outcome, void *arg)
240{
241 async_transaction_t *trans = (async_transaction_t *)arg;
242
243 async_answer_0(trans->caller, outcome);
244
245 async_transaction_destroy(trans);
246}
247
248static void callback_in(device_t *device,
249 int outcome, size_t actual_size, void *arg)
250{
251 async_transaction_t *trans = (async_transaction_t *)arg;
252
253 if (outcome != EOK) {
254 async_answer_0(trans->caller, outcome);
255 if (trans->data_caller) {
256 async_answer_0(trans->data_caller, EINTR);
257 }
258 async_transaction_destroy(trans);
259 return;
260 }
261
262 trans->size = actual_size;
263
264 if (trans->data_caller) {
265 async_data_read_finalize(trans->data_caller,
266 trans->buffer, actual_size);
267 }
268
269 async_answer_0(trans->caller, EOK);
270
271 async_transaction_destroy(trans);
272}
273
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)
284{
285 if (!transfer_func) {
286 async_answer_0(callid, ENOTSUP);
287 return;
288 }
289
290 size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
291 usb_target_t target = {
292 .address = DEV_IPC_GET_ARG1(*call),
293 .endpoint = DEV_IPC_GET_ARG2(*call)
294 };
295
296 size_t len = 0;
297 void *buffer = NULL;
298
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;
306 }
307
308 async_transaction_t *trans = async_transaction_create(callid);
309 if (trans == NULL) {
310 if (buffer != NULL) {
311 free(buffer);
312 }
313 async_answer_0(callid, ENOMEM);
314 return;
315 }
316
317 trans->buffer = buffer;
318 trans->size = len;
319
320 rc = transfer_func(device, target, max_packet_size,
321 buffer, len,
322 callback_out, trans);
323
324 if (rc != EOK) {
325 async_answer_0(callid, rc);
326 async_transaction_destroy(trans);
327 }
328}
329
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)
340{
341 if (!transfer_func) {
342 async_answer_0(callid, ENOTSUP);
343 return;
344 }
345
346 size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
347 usb_target_t target = {
348 .address = DEV_IPC_GET_ARG1(*call),
349 .endpoint = DEV_IPC_GET_ARG2(*call)
350 };
351
352 size_t len;
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
359 async_transaction_t *trans = async_transaction_create(callid);
360 if (trans == NULL) {
361 async_answer_0(callid, ENOMEM);
362 return;
363 }
364 trans->data_caller = data_callid;
365 trans->buffer = malloc(len);
366 trans->size = len;
367
368 int rc = transfer_func(device, target, max_packet_size,
369 trans->buffer, len,
370 callback_in, trans);
371
372 if (rc != EOK) {
373 async_answer_0(callid, rc);
374 async_transaction_destroy(trans);
375 }
376}
377
378void remote_usbhc_interrupt_out(device_t *device, void *iface,
379 ipc_callid_t callid, ipc_call_t *call)
380{
381 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
382 assert(usb_iface != NULL);
383
384 return remote_usbhc_out_transfer(device, callid, call,
385 usb_iface->interrupt_out);
386}
387
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);
393
394 return remote_usbhc_in_transfer(device, callid, call,
395 usb_iface->interrupt_in);
396}
397
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
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) {
425 async_answer_0(callid, ENOTSUP);
426 return;
427 }
428
429 usb_target_t target = {
430 .address = DEV_IPC_GET_ARG1(*call),
431 .endpoint = DEV_IPC_GET_ARG2(*call)
432 };
433 size_t data_buffer_len = DEV_IPC_GET_ARG3(*call);
434 size_t max_packet_size = DEV_IPC_GET_ARG4(*call);
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) {
445 async_answer_0(callid, rc);
446 return;
447 }
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 }
457 }
458
459 async_transaction_t *trans = async_transaction_create(callid);
460 if (trans == NULL) {
461 async_answer_0(callid, ENOMEM);
462 free(setup_packet);
463 free(data_buffer);
464 return;
465 }
466 trans->setup_packet = setup_packet;
467 trans->buffer = data_buffer;
468 trans->size = data_buffer_len;
469
470 rc = usb_iface->control_write(device, target, max_packet_size,
471 setup_packet, setup_packet_len,
472 data_buffer, data_buffer_len,
473 callback_out, trans);
474
475 if (rc != EOK) {
476 async_answer_0(callid, rc);
477 async_transaction_destroy(trans);
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) {
489 async_answer_0(callid, ENOTSUP);
490 return;
491 }
492
493 usb_target_t target = {
494 .address = DEV_IPC_GET_ARG1(*call),
495 .endpoint = DEV_IPC_GET_ARG2(*call)
496 };
497 size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
498
499 int rc;
500
501 void *setup_packet = NULL;
502 size_t setup_packet_len = 0;
503 size_t data_len = 0;
504
505 rc = async_data_write_accept(&setup_packet, false,
506 1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
507 if (rc != EOK) {
508 async_answer_0(callid, rc);
509 return;
510 }
511
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
519 async_transaction_t *trans = async_transaction_create(callid);
520 if (trans == NULL) {
521 async_answer_0(callid, ENOMEM);
522 free(setup_packet);
523 return;
524 }
525 trans->data_caller = data_callid;
526 trans->setup_packet = setup_packet;
527 trans->size = data_len;
528 trans->buffer = malloc(data_len);
529 if (trans->buffer == NULL) {
530 async_answer_0(callid, ENOMEM);
531 async_transaction_destroy(trans);
532 return;
533 }
534
535 rc = usb_iface->control_read(device, target, max_packet_size,
536 setup_packet, setup_packet_len,
537 trans->buffer, trans->size,
538 callback_in, trans);
539
540 if (rc != EOK) {
541 async_answer_0(callid, rc);
542 async_transaction_destroy(trans);
543 }
544}
545
546
547
548/**
549 * @}
550 */
Note: See TracBrowser for help on using the repository browser.