source: mainline/uspace/drv/vhc/connhost.c@ cc5908e

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

Virtual USB layer rewritten

Major changes include

  • IPC sends whole transfers (not transactions)
  • separate transfer queues for each device in host controller
  • possibility to return NAK from virtual device (handled by HC)
  • better implementation of callbacks for non-zero endpoints

Still missing

  • communication for some transfer types (bulk)
  • face-lift ;-)
  • documentation
  • Property mode set to 100644
File size: 14.3 KB
Line 
1/*
2 * Copyright (c) 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 drvusbvhc
30 * @{
31 */
32/** @file
33 * Host controller interface implementation.
34 */
35#include <assert.h>
36#include <errno.h>
37#include <usb/usb.h>
38#include <usb/addrkeep.h>
39#include <usb/ddfiface.h>
40#include <usb/debug.h>
41#include <usbhc_iface.h>
42#include "vhcd.h"
43
44#define GET_VHC_DATA(fun) \
45 ((vhc_data_t *)fun->dev->driver_data)
46#define VHC_DATA(vhc, fun) \
47 vhc_data_t *vhc = GET_VHC_DATA(fun); assert(vhc->magic == 0xdeadbeef)
48
49#define UNSUPPORTED(methodname) \
50 usb_log_warning("Unsupported interface method `%s()' in %s:%d.\n", \
51 methodname, __FILE__, __LINE__)
52
53/** Found free USB address.
54 *
55 * @param[in] fun Device function the action was invoked on.
56 * @param[in] speed Speed of the device that will get this address.
57 * @param[out] address Non-null pointer where to store the free address.
58 * @return Error code.
59 */
60static int request_address(ddf_fun_t *fun, usb_speed_t speed,
61 usb_address_t *address)
62{
63 VHC_DATA(vhc, fun);
64
65 usb_address_t addr = device_keeper_get_free_address(&vhc->dev_keeper,
66 USB_SPEED_HIGH);
67 if (addr < 0) {
68 return addr;
69 }
70
71 if (address != NULL) {
72 *address = addr;
73 }
74
75 return EOK;
76}
77
78/** Bind USB address with device devman handle.
79 *
80 * @param[in] fun Device function the action was invoked on.
81 * @param[in] address USB address of the device.
82 * @param[in] handle Devman handle of the device.
83 * @return Error code.
84 */
85static int bind_address(ddf_fun_t *fun,
86 usb_address_t address, devman_handle_t handle)
87{
88 VHC_DATA(vhc, fun);
89 usb_log_debug("Binding handle %" PRIun " to address %d.\n",
90 handle, address);
91 usb_device_keeper_bind(&vhc->dev_keeper, address, handle);
92
93 return EOK;
94}
95
96/** Release previously requested address.
97 *
98 * @param[in] fun Device function the action was invoked on.
99 * @param[in] address USB address to be released.
100 * @return Error code.
101 */
102static int release_address(ddf_fun_t *fun, usb_address_t address)
103{
104 VHC_DATA(vhc, fun);
105 usb_log_debug("Releasing address %d...\n", address);
106 usb_device_keeper_release(&vhc->dev_keeper, address);
107
108 return ENOTSUP;
109}
110
111/** Register endpoint for bandwidth reservation.
112 *
113 * @param[in] fun Device function the action was invoked on.
114 * @param[in] address USB address of the device.
115 * @param[in] speed Endpoint speed (invalid means to use device one).
116 * @param[in] endpoint Endpoint number.
117 * @param[in] transfer_type USB transfer type.
118 * @param[in] direction Endpoint data direction.
119 * @param[in] max_packet_size Max packet size of the endpoint.
120 * @param[in] interval Polling interval.
121 * @return Error code.
122 */
123static int register_endpoint(ddf_fun_t *fun,
124 usb_address_t address, usb_speed_t speed, usb_endpoint_t endpoint,
125 usb_transfer_type_t transfer_type, usb_direction_t direction,
126 size_t max_packet_size, unsigned int interval)
127{
128 VHC_DATA(vhc, fun);
129
130 endpoint_t *ep = malloc(sizeof(endpoint_t));
131 if (ep == NULL) {
132 return ENOMEM;
133 }
134
135 int rc = endpoint_init(ep, address, endpoint, direction, transfer_type,
136 USB_SPEED_FULL, 1);
137 if (rc != EOK) {
138 free(ep);
139 return rc;
140 }
141
142 rc = usb_endpoint_manager_register_ep(&vhc->ep_manager, ep, 1);
143 if (rc != EOK) {
144 endpoint_destroy(ep);
145 return rc;
146 }
147
148 return EOK;
149}
150
151/** Unregister endpoint (free some bandwidth reservation).
152 *
153 * @param[in] fun Device function the action was invoked on.
154 * @param[in] address USB address of the device.
155 * @param[in] endpoint Endpoint number.
156 * @param[in] direction Endpoint data direction.
157 * @return Error code.
158 */
159static int unregister_endpoint(ddf_fun_t *fun, usb_address_t address,
160 usb_endpoint_t endpoint, usb_direction_t direction)
161{
162 VHC_DATA(vhc, fun);
163
164 endpoint_t *ep = usb_endpoint_manager_get_ep(&vhc->ep_manager,
165 address, endpoint, direction, NULL);
166 if (ep == NULL) {
167 return ENOENT;
168 }
169
170 int rc = usb_endpoint_manager_unregister_ep(&vhc->ep_manager,
171 address, endpoint, direction);
172
173 return rc;
174}
175
176/** Schedule interrupt out transfer.
177 *
178 * The callback is supposed to be called once the transfer (on the wire) is
179 * complete regardless of the outcome.
180 * However, the callback could be called only when this function returns
181 * with success status (i.e. returns EOK).
182 *
183 * @param[in] fun Device function the action was invoked on.
184 * @param[in] target Target pipe (address and endpoint number) specification.
185 * @param[in] data Data to be sent (in USB endianess, allocated and deallocated
186 * by the caller).
187 * @param[in] size Size of the @p data buffer in bytes.
188 * @param[in] callback Callback to be issued once the transfer is complete.
189 * @param[in] arg Pass-through argument to the callback.
190 * @return Error code.
191 */
192static int interrupt_out(ddf_fun_t *fun, usb_target_t target,
193 void *data, size_t size,
194 usbhc_iface_transfer_out_callback_t callback, void *arg)
195{
196 VHC_DATA(vhc, fun);
197
198 vhc_transfer_t *transfer = vhc_transfer_create(target.address,
199 target.endpoint, USB_DIRECTION_OUT, USB_TRANSFER_INTERRUPT,
200 fun, arg);
201 if (transfer == NULL) {
202 return ENOMEM;
203 }
204
205 transfer->data_buffer = data;
206 transfer->data_buffer_size = size;
207 transfer->callback_out = callback;
208
209 int rc = vhc_virtdev_add_transfer(vhc, transfer);
210 if (rc != EOK) {
211 free(transfer);
212 return rc;
213 }
214
215 return EOK;
216}
217
218/** Schedule interrupt in transfer.
219 *
220 * The callback is supposed to be called once the transfer (on the wire) is
221 * complete regardless of the outcome.
222 * However, the callback could be called only when this function returns
223 * with success status (i.e. returns EOK).
224 *
225 * @param[in] fun Device function the action was invoked on.
226 * @param[in] target Target pipe (address and endpoint number) specification.
227 * @param[in] data Buffer where to store the data (in USB endianess,
228 * allocated and deallocated by the caller).
229 * @param[in] size Size of the @p data buffer in bytes.
230 * @param[in] callback Callback to be issued once the transfer is complete.
231 * @param[in] arg Pass-through argument to the callback.
232 * @return Error code.
233 */
234static int interrupt_in(ddf_fun_t *fun, usb_target_t target,
235 void *data, size_t size,
236 usbhc_iface_transfer_in_callback_t callback, void *arg)
237{
238 VHC_DATA(vhc, fun);
239
240 vhc_transfer_t *transfer = vhc_transfer_create(target.address,
241 target.endpoint, USB_DIRECTION_IN, USB_TRANSFER_INTERRUPT,
242 fun, arg);
243 if (transfer == NULL) {
244 return ENOMEM;
245 }
246
247 transfer->data_buffer = data;
248 transfer->data_buffer_size = size;
249 transfer->callback_in = callback;
250
251 int rc = vhc_virtdev_add_transfer(vhc, transfer);
252 if (rc != EOK) {
253 free(transfer);
254 return rc;
255 }
256
257 return EOK;
258}
259
260/** Schedule bulk out transfer.
261 *
262 * The callback is supposed to be called once the transfer (on the wire) is
263 * complete regardless of the outcome.
264 * However, the callback could be called only when this function returns
265 * with success status (i.e. returns EOK).
266 *
267 * @param[in] fun Device function the action was invoked on.
268 * @param[in] target Target pipe (address and endpoint number) specification.
269 * @param[in] data Data to be sent (in USB endianess, allocated and deallocated
270 * by the caller).
271 * @param[in] size Size of the @p data buffer in bytes.
272 * @param[in] callback Callback to be issued once the transfer is complete.
273 * @param[in] arg Pass-through argument to the callback.
274 * @return Error code.
275 */
276static int bulk_out(ddf_fun_t *fun, usb_target_t target,
277 void *data, size_t size,
278 usbhc_iface_transfer_out_callback_t callback, void *arg)
279{
280 UNSUPPORTED("bulk_out");
281
282 return ENOTSUP;
283}
284
285/** Schedule bulk in transfer.
286 *
287 * The callback is supposed to be called once the transfer (on the wire) is
288 * complete regardless of the outcome.
289 * However, the callback could be called only when this function returns
290 * with success status (i.e. returns EOK).
291 *
292 * @param[in] fun Device function the action was invoked on.
293 * @param[in] target Target pipe (address and endpoint number) specification.
294 * @param[in] data Buffer where to store the data (in USB endianess,
295 * allocated and deallocated by the caller).
296 * @param[in] size Size of the @p data buffer in bytes.
297 * @param[in] callback Callback to be issued once the transfer is complete.
298 * @param[in] arg Pass-through argument to the callback.
299 * @return Error code.
300 */
301static int bulk_in(ddf_fun_t *fun, usb_target_t target,
302 void *data, size_t size,
303 usbhc_iface_transfer_in_callback_t callback, void *arg)
304{
305 UNSUPPORTED("bulk_in");
306
307 return ENOTSUP;
308}
309
310/** Schedule control write transfer.
311 *
312 * The callback is supposed to be called once the transfer (on the wire) is
313 * complete regardless of the outcome.
314 * However, the callback could be called only when this function returns
315 * with success status (i.e. returns EOK).
316 *
317 * @param[in] fun Device function the action was invoked on.
318 * @param[in] target Target pipe (address and endpoint number) specification.
319 * @param[in] setup_packet Setup packet buffer (in USB endianess, allocated
320 * and deallocated by the caller).
321 * @param[in] setup_packet_size Size of @p setup_packet buffer in bytes.
322 * @param[in] data_buffer Data buffer (in USB endianess, allocated and
323 * deallocated by the caller).
324 * @param[in] data_buffer_size Size of @p data_buffer buffer in bytes.
325 * @param[in] callback Callback to be issued once the transfer is complete.
326 * @param[in] arg Pass-through argument to the callback.
327 * @return Error code.
328 */
329static int control_write(ddf_fun_t *fun, usb_target_t target,
330 void *setup_packet, size_t setup_packet_size,
331 void *data_buffer, size_t data_buffer_size,
332 usbhc_iface_transfer_out_callback_t callback, void *arg)
333{
334 VHC_DATA(vhc, fun);
335
336 vhc_transfer_t *transfer = vhc_transfer_create(target.address,
337 target.endpoint, USB_DIRECTION_OUT, USB_TRANSFER_CONTROL,
338 fun, arg);
339 if (transfer == NULL) {
340 return ENOMEM;
341 }
342
343 transfer->setup_buffer = setup_packet;
344 transfer->setup_buffer_size = setup_packet_size;
345 transfer->data_buffer = data_buffer;
346 transfer->data_buffer_size = data_buffer_size;
347 transfer->callback_out = callback;
348
349 int rc = vhc_virtdev_add_transfer(vhc, transfer);
350 if (rc != EOK) {
351 free(transfer);
352 return rc;
353 }
354
355 return EOK;
356}
357
358/** Schedule control read transfer.
359 *
360 * The callback is supposed to be called once the transfer (on the wire) is
361 * complete regardless of the outcome.
362 * However, the callback could be called only when this function returns
363 * with success status (i.e. returns EOK).
364 *
365 * @param[in] fun Device function the action was invoked on.
366 * @param[in] target Target pipe (address and endpoint number) specification.
367 * @param[in] setup_packet Setup packet buffer (in USB endianess, allocated
368 * and deallocated by the caller).
369 * @param[in] setup_packet_size Size of @p setup_packet buffer in bytes.
370 * @param[in] data_buffer Buffer where to store the data (in USB endianess,
371 * allocated and deallocated by the caller).
372 * @param[in] data_buffer_size Size of @p data_buffer buffer in bytes.
373 * @param[in] callback Callback to be issued once the transfer is complete.
374 * @param[in] arg Pass-through argument to the callback.
375 * @return Error code.
376 */
377static int control_read(ddf_fun_t *fun, usb_target_t target,
378 void *setup_packet, size_t setup_packet_size,
379 void *data_buffer, size_t data_buffer_size,
380 usbhc_iface_transfer_in_callback_t callback, void *arg)
381{
382 VHC_DATA(vhc, fun);
383
384 vhc_transfer_t *transfer = vhc_transfer_create(target.address,
385 target.endpoint, USB_DIRECTION_IN, USB_TRANSFER_CONTROL,
386 fun, arg);
387 if (transfer == NULL) {
388 return ENOMEM;
389 }
390
391 transfer->setup_buffer = setup_packet;
392 transfer->setup_buffer_size = setup_packet_size;
393 transfer->data_buffer = data_buffer;
394 transfer->data_buffer_size = data_buffer_size;
395 transfer->callback_in = callback;
396
397 int rc = vhc_virtdev_add_transfer(vhc, transfer);
398 if (rc != EOK) {
399 free(transfer);
400 return rc;
401 }
402
403 return EOK;
404}
405
406static int tell_address(ddf_fun_t *fun, devman_handle_t handle,
407 usb_address_t *address)
408{
409 UNSUPPORTED("tell_address");
410
411 return ENOTSUP;
412}
413
414static int usb_iface_get_hc_handle_rh_impl(ddf_fun_t *root_hub_fun,
415 devman_handle_t *handle)
416{
417 VHC_DATA(vhc, root_hub_fun);
418
419 *handle = vhc->hc_fun->handle;
420
421 return EOK;
422}
423
424static int tell_address_rh(ddf_fun_t *root_hub_fun, devman_handle_t handle,
425 usb_address_t *address)
426{
427 VHC_DATA(vhc, root_hub_fun);
428
429 if (handle == 0) {
430 handle = root_hub_fun->handle;
431 }
432
433 usb_log_debug("tell_address_rh(handle=%" PRIun ")\n", handle);
434 usb_address_t addr = usb_device_keeper_find(&vhc->dev_keeper, handle);
435 if (addr < 0) {
436 return addr;
437 } else {
438 *address = addr;
439 return EOK;
440 }
441}
442
443usbhc_iface_t vhc_iface = {
444 .request_address = request_address,
445 .bind_address = bind_address,
446 .release_address = release_address,
447
448 .register_endpoint = register_endpoint,
449 .unregister_endpoint = unregister_endpoint,
450
451 .interrupt_out = interrupt_out,
452 .interrupt_in = interrupt_in,
453
454 .bulk_in = bulk_in,
455 .bulk_out = bulk_out,
456
457 .control_write = control_write,
458 .control_read = control_read
459};
460
461usb_iface_t vhc_usb_iface = {
462 .get_hc_handle = usb_iface_get_hc_handle_hc_impl,
463 .get_address = tell_address
464};
465
466usb_iface_t rh_usb_iface = {
467 .get_hc_handle = usb_iface_get_hc_handle_rh_impl,
468 .get_address = tell_address_rh
469};
470
471
472/**
473 * @}
474 */
Note: See TracBrowser for help on using the repository browser.