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

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

Endpoint registration sends address as well

This is the first step towards using endpoint registration instead
of reservation of default address.

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