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

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

Add getting handle by address over USBHC iface

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