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

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

Add address reservation to USBHC interface in DDF

Any USB driver that needs to assign new USB address shall ask its HC
for it. When using default USB address (address 0), it must inform
HC about it as HC is the only element able to provide serialization.

  • Property mode set to 100644
File size: 12.9 KB
Line 
1/*
2 * Copyright (c) 2010 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 <ipc/ipc.h>
36#include <async.h>
37#include <errno.h>
38
39#include "usbhc_iface.h"
40#include "driver.h"
41
42#define USB_MAX_PAYLOAD_SIZE 1020
43
44static void remote_usbhc_get_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
45static void remote_usbhc_get_buffer(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_control_write_setup(device_t *, void *, ipc_callid_t, ipc_call_t *);
49static void remote_usbhc_control_write_data(device_t *, void *, ipc_callid_t, ipc_call_t *);
50static void remote_usbhc_control_write_status(device_t *, void *, ipc_callid_t, ipc_call_t *);
51static void remote_usbhc_control_read_setup(device_t *, void *, ipc_callid_t, ipc_call_t *);
52static void remote_usbhc_control_read_data(device_t *, void *, ipc_callid_t, ipc_call_t *);
53static void remote_usbhc_control_read_status(device_t *, void *, ipc_callid_t, ipc_call_t *);
54static void remote_usbhc_reserve_default_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
55static void remote_usbhc_release_default_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
56static void remote_usbhc_request_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
57static void remote_usbhc_release_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
58//static void remote_usbhc(device_t *, void *, ipc_callid_t, ipc_call_t *);
59
60/** Remote USB interface operations. */
61static remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
62 remote_usbhc_get_address,
63
64 remote_usbhc_get_buffer,
65
66 remote_usbhc_reserve_default_address,
67 remote_usbhc_release_default_address,
68
69 remote_usbhc_request_address,
70 remote_usbhc_release_address,
71
72 remote_usbhc_interrupt_out,
73 remote_usbhc_interrupt_in,
74
75 remote_usbhc_control_write_setup,
76 remote_usbhc_control_write_data,
77 remote_usbhc_control_write_status,
78
79 remote_usbhc_control_read_setup,
80 remote_usbhc_control_read_data,
81 remote_usbhc_control_read_status
82};
83
84/** Remote USB interface structure.
85 */
86remote_iface_t remote_usbhc_iface = {
87 .method_count = sizeof(remote_usbhc_iface_ops) /
88 sizeof(remote_usbhc_iface_ops[0]),
89 .methods = remote_usbhc_iface_ops
90};
91
92typedef struct {
93 ipc_callid_t caller;
94 void *buffer;
95 size_t size;
96} async_transaction_t;
97
98void remote_usbhc_get_address(device_t *device, void *iface,
99 ipc_callid_t callid, ipc_call_t *call)
100{
101 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
102
103 if (!usb_iface->tell_address) {
104 ipc_answer_0(callid, ENOTSUP);
105 return;
106 }
107
108 devman_handle_t handle = IPC_GET_ARG1(*call);
109
110 usb_address_t address;
111 int rc = usb_iface->tell_address(device, handle, &address);
112 if (rc != EOK) {
113 ipc_answer_0(callid, rc);
114 } else {
115 ipc_answer_1(callid, EOK, address);
116 }
117}
118
119void remote_usbhc_get_buffer(device_t *device, void *iface,
120 ipc_callid_t callid, ipc_call_t *call)
121{
122 ipcarg_t buffer_hash = IPC_GET_ARG1(*call);
123 async_transaction_t * trans = (async_transaction_t *)buffer_hash;
124 if (trans == NULL) {
125 ipc_answer_0(callid, ENOENT);
126 return;
127 }
128 if (trans->buffer == NULL) {
129 ipc_answer_0(callid, EINVAL);
130 free(trans);
131 return;
132 }
133
134 ipc_callid_t cid;
135 size_t accepted_size;
136 if (!async_data_read_receive(&cid, &accepted_size)) {
137 ipc_answer_0(callid, EINVAL);
138 return;
139 }
140
141 if (accepted_size > trans->size) {
142 accepted_size = trans->size;
143 }
144 async_data_read_finalize(callid, trans->buffer, accepted_size);
145
146 ipc_answer_1(callid, EOK, accepted_size);
147
148 free(trans->buffer);
149 free(trans);
150}
151
152void remote_usbhc_reserve_default_address(device_t *device, void *iface,
153 ipc_callid_t callid, ipc_call_t *call)
154{
155 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
156
157 if (!usb_iface->reserve_default_address) {
158 ipc_answer_0(callid, ENOTSUP);
159 return;
160 }
161
162 int rc = usb_iface->reserve_default_address(device);
163
164 ipc_answer_0(callid, rc);
165}
166
167void remote_usbhc_release_default_address(device_t *device, 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->release_default_address) {
173 ipc_answer_0(callid, ENOTSUP);
174 return;
175 }
176
177 int rc = usb_iface->release_default_address(device);
178
179 ipc_answer_0(callid, rc);
180}
181
182void remote_usbhc_request_address(device_t *device, void *iface,
183 ipc_callid_t callid, ipc_call_t *call)
184{
185 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
186
187 if (!usb_iface->request_address) {
188 ipc_answer_0(callid, ENOTSUP);
189 return;
190 }
191
192 usb_address_t address;
193 int rc = usb_iface->request_address(device, &address);
194 if (rc != EOK) {
195 ipc_answer_0(callid, rc);
196 } else {
197 ipc_answer_1(callid, EOK, (ipcarg_t) address);
198 }
199}
200
201void remote_usbhc_release_address(device_t *device, void *iface,
202 ipc_callid_t callid, ipc_call_t *call)
203{
204 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
205
206 if (!usb_iface->release_address) {
207 ipc_answer_0(callid, ENOTSUP);
208 return;
209 }
210
211 usb_address_t address = (usb_address_t) IPC_GET_ARG1(*call);
212
213 int rc = usb_iface->release_address(device, address);
214
215 ipc_answer_0(callid, rc);
216}
217
218
219static void callback_out(device_t *device,
220 usb_transaction_outcome_t outcome, void *arg)
221{
222 async_transaction_t *trans = (async_transaction_t *)arg;
223
224 // FIXME - answer according to outcome
225 ipc_answer_0(trans->caller, EOK);
226
227 free(trans);
228}
229
230static void callback_in(device_t *device,
231 usb_transaction_outcome_t outcome, size_t actual_size, void *arg)
232{
233 async_transaction_t *trans = (async_transaction_t *)arg;
234
235 // FIXME - answer according to outcome
236 ipc_answer_1(trans->caller, EOK, (ipcarg_t)trans);
237
238 trans->size = actual_size;
239}
240
241/** Process an outgoing transfer (both OUT and SETUP).
242 *
243 * @param device Target device.
244 * @param callid Initiating caller.
245 * @param call Initiating call.
246 * @param transfer_func Transfer function (might be NULL).
247 */
248static void remote_usbhc_out_transfer(device_t *device,
249 ipc_callid_t callid, ipc_call_t *call,
250 usbhc_iface_transfer_out_t transfer_func)
251{
252 if (!transfer_func) {
253 ipc_answer_0(callid, ENOTSUP);
254 return;
255 }
256
257 size_t expected_len = IPC_GET_ARG3(*call);
258 usb_target_t target = {
259 .address = IPC_GET_ARG1(*call),
260 .endpoint = IPC_GET_ARG2(*call)
261 };
262
263 size_t len = 0;
264 void *buffer = NULL;
265 if (expected_len > 0) {
266 int rc = async_data_write_accept(&buffer, false,
267 1, USB_MAX_PAYLOAD_SIZE,
268 0, &len);
269
270 if (rc != EOK) {
271 ipc_answer_0(callid, rc);
272 return;
273 }
274 }
275
276 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
277 trans->caller = callid;
278 trans->buffer = buffer;
279 trans->size = len;
280
281 int rc = transfer_func(device, target, buffer, len,
282 callback_out, trans);
283
284 if (rc != EOK) {
285 ipc_answer_0(callid, rc);
286 if (buffer != NULL) {
287 free(buffer);
288 }
289 free(trans);
290 }
291}
292
293/** Process an incoming transfer.
294 *
295 * @param device Target device.
296 * @param callid Initiating caller.
297 * @param call Initiating call.
298 * @param transfer_func Transfer function (might be NULL).
299 */
300static void remote_usbhc_in_transfer(device_t *device,
301 ipc_callid_t callid, ipc_call_t *call,
302 usbhc_iface_transfer_in_t transfer_func)
303{
304 if (!transfer_func) {
305 ipc_answer_0(callid, ENOTSUP);
306 return;
307 }
308
309 size_t len = IPC_GET_ARG3(*call);
310 usb_target_t target = {
311 .address = IPC_GET_ARG1(*call),
312 .endpoint = IPC_GET_ARG2(*call)
313 };
314
315 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
316 trans->caller = callid;
317 trans->buffer = malloc(len);
318 trans->size = len;
319
320 int rc = transfer_func(device, target, trans->buffer, len,
321 callback_in, trans);
322
323 if (rc != EOK) {
324 ipc_answer_0(callid, rc);
325 free(trans->buffer);
326 free(trans);
327 }
328}
329
330/** Process status part of control transfer.
331 *
332 * @param device Target device.
333 * @param callid Initiating caller.
334 * @param call Initiating call.
335 * @param direction Transfer direction (read ~ in, write ~ out).
336 * @param transfer_in_func Transfer function for control read (might be NULL).
337 * @param transfer_out_func Transfer function for control write (might be NULL).
338 */
339static void remote_usbhc_status_transfer(device_t *device,
340 ipc_callid_t callid, ipc_call_t *call,
341 usb_direction_t direction,
342 int (*transfer_in_func)(device_t *, usb_target_t,
343 usbhc_iface_transfer_in_callback_t, void *),
344 int (*transfer_out_func)(device_t *, usb_target_t,
345 usbhc_iface_transfer_out_callback_t, void *))
346{
347 switch (direction) {
348 case USB_DIRECTION_IN:
349 if (!transfer_in_func) {
350 ipc_answer_0(callid, ENOTSUP);
351 return;
352 }
353 break;
354 case USB_DIRECTION_OUT:
355 if (!transfer_out_func) {
356 ipc_answer_0(callid, ENOTSUP);
357 return;
358 }
359 break;
360 default:
361 assert(false && "unreachable code");
362 break;
363 }
364
365 usb_target_t target = {
366 .address = IPC_GET_ARG1(*call),
367 .endpoint = IPC_GET_ARG2(*call)
368 };
369
370 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
371 trans->caller = callid;
372 trans->buffer = NULL;
373 trans->size = 0;
374
375 int rc;
376 switch (direction) {
377 case USB_DIRECTION_IN:
378 rc = transfer_in_func(device, target,
379 callback_in, trans);
380 break;
381 case USB_DIRECTION_OUT:
382 rc = transfer_out_func(device, target,
383 callback_out, trans);
384 break;
385 default:
386 assert(false && "unreachable code");
387 break;
388 }
389
390 if (rc != EOK) {
391 ipc_answer_0(callid, rc);
392 free(trans);
393 }
394 return;
395}
396
397
398void remote_usbhc_interrupt_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->interrupt_out);
406}
407
408void remote_usbhc_interrupt_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->interrupt_in);
416}
417
418void remote_usbhc_control_write_setup(device_t *device, void *iface,
419 ipc_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 return remote_usbhc_out_transfer(device, callid, call,
425 usb_iface->control_write_setup);
426}
427
428void remote_usbhc_control_write_data(device_t *device, void *iface,
429 ipc_callid_t callid, ipc_call_t *call)
430{
431 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
432 assert(usb_iface != NULL);
433
434 return remote_usbhc_out_transfer(device, callid, call,
435 usb_iface->control_write_data);
436}
437
438void remote_usbhc_control_write_status(device_t *device, void *iface,
439 ipc_callid_t callid, ipc_call_t *call)
440{
441 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
442 assert(usb_iface != NULL);
443
444 return remote_usbhc_status_transfer(device, callid, call,
445 USB_DIRECTION_IN, usb_iface->control_write_status, NULL);
446}
447
448void remote_usbhc_control_read_setup(device_t *device, void *iface,
449 ipc_callid_t callid, ipc_call_t *call)
450{
451 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
452 assert(usb_iface != NULL);
453
454 return remote_usbhc_out_transfer(device, callid, call,
455 usb_iface->control_read_setup);
456}
457
458void remote_usbhc_control_read_data(device_t *device, void *iface,
459 ipc_callid_t callid, ipc_call_t *call)
460{
461 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
462 assert(usb_iface != NULL);
463
464 return remote_usbhc_in_transfer(device, callid, call,
465 usb_iface->control_read_data);
466}
467
468void remote_usbhc_control_read_status(device_t *device, void *iface,
469 ipc_callid_t callid, ipc_call_t *call)
470{
471 usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
472 assert(usb_iface != NULL);
473
474 return remote_usbhc_status_transfer(device, callid, call,
475 USB_DIRECTION_OUT, NULL, usb_iface->control_read_status);
476}
477
478
479
480/**
481 * @}
482 */
Note: See TracBrowser for help on using the repository browser.