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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since abfd36b was 6cb58e6, checked in by Vojtech Horky <vojtechhorky@…>, 15 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
RevLine 
[b371844]1/*
[6cb58e6]2 * Copyright (c) 2011 Vojtech Horky
[b371844]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
[bd8c753d]29/** @addtogroup drvusbvhc
[b371844]30 * @{
31 */
32/** @file
[6cb58e6]33 * Host controller interface implementation.
[b371844]34 */
35#include <assert.h>
36#include <errno.h>
[4b4c797]37#include <usb/usb.h>
[8555112]38#include <usb/addrkeep.h>
[357a302]39#include <usb/ddfiface.h>
[6cb58e6]40#include <usb/debug.h>
41#include <usbhc_iface.h>
[b371844]42#include "vhcd.h"
[6cb58e6]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)
[c4ba29c7]62{
[6cb58e6]63 VHC_DATA(vhc, fun);
[c4ba29c7]64
[6cb58e6]65 usb_address_t addr = device_keeper_get_free_address(&vhc->dev_keeper,
66 USB_SPEED_HIGH);
67 if (addr < 0) {
68 return addr;
[284c629]69 }
70
[6cb58e6]71 if (address != NULL) {
72 *address = addr;
[c4ba29c7]73 }
74
[6cb58e6]75 return EOK;
[c4ba29c7]76}
77
[6cb58e6]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)
[c4ba29c7]87{
[6cb58e6]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);
[284c629]92
[6cb58e6]93 return EOK;
[284c629]94}
95
[6cb58e6]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)
[284c629]103{
[6cb58e6]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;
[284c629]109}
110
[6cb58e6]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)
[284c629]127{
[6cb58e6]128 VHC_DATA(vhc, fun);
[284c629]129
[6cb58e6]130 endpoint_t *ep = malloc(sizeof(endpoint_t));
131 if (ep == NULL) {
132 return ENOMEM;
[284c629]133 }
134
[6cb58e6]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;
[284c629]140 }
141
[6cb58e6]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;
[284c629]149}
150
[6cb58e6]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)
[284c629]161{
[6cb58e6]162 VHC_DATA(vhc, fun);
[284c629]163
[6cb58e6]164 endpoint_t *ep = usb_endpoint_manager_get_ep(&vhc->ep_manager,
165 address, endpoint, direction, NULL);
166 if (ep == NULL) {
167 return ENOENT;
[284c629]168 }
169
[6cb58e6]170 int rc = usb_endpoint_manager_unregister_ep(&vhc->ep_manager,
171 address, endpoint, direction);
[c4ba29c7]172
[6cb58e6]173 return rc;
[c4ba29c7]174}
175
[6cb58e6]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,
[4317827]194 usbhc_iface_transfer_out_callback_t callback, void *arg)
[63b4f90]195{
[6cb58e6]196 VHC_DATA(vhc, fun);
[c4ba29c7]197
[6cb58e6]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;
[c4ba29c7]208
[6cb58e6]209 int rc = vhc_virtdev_add_transfer(vhc, transfer);
210 if (rc != EOK) {
211 free(transfer);
212 return rc;
213 }
[c4ba29c7]214
215 return EOK;
[63b4f90]216}
217
[6cb58e6]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,
[4317827]236 usbhc_iface_transfer_in_callback_t callback, void *arg)
[63b4f90]237{
[6cb58e6]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 }
[c4ba29c7]246
[6cb58e6]247 transfer->data_buffer = data;
248 transfer->data_buffer_size = size;
249 transfer->callback_in = callback;
[c4ba29c7]250
[6cb58e6]251 int rc = vhc_virtdev_add_transfer(vhc, transfer);
252 if (rc != EOK) {
253 free(transfer);
254 return rc;
255 }
[c4ba29c7]256
257 return EOK;
[63b4f90]258}
259
[6cb58e6]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,
[4317827]277 void *data, size_t size,
278 usbhc_iface_transfer_out_callback_t callback, void *arg)
279{
[6cb58e6]280 UNSUPPORTED("bulk_out");
281
282 return ENOTSUP;
[4317827]283}
284
[6cb58e6]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,
[4317827]302 void *data, size_t size,
303 usbhc_iface_transfer_in_callback_t callback, void *arg)
304{
[6cb58e6]305 UNSUPPORTED("bulk_in");
306
307 return ENOTSUP;
[4317827]308}
309
[6cb58e6]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 */
[eb1a2f4]329static int control_write(ddf_fun_t *fun, usb_target_t target,
[284c629]330 void *setup_packet, size_t setup_packet_size,
[6cb58e6]331 void *data_buffer, size_t data_buffer_size,
[284c629]332 usbhc_iface_transfer_out_callback_t callback, void *arg)
333{
[6cb58e6]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;
[284c629]348
[6cb58e6]349 int rc = vhc_virtdev_add_transfer(vhc, transfer);
350 if (rc != EOK) {
351 free(transfer);
352 return rc;
353 }
[284c629]354
355 return EOK;
356}
357
[6cb58e6]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 */
[eb1a2f4]377static int control_read(ddf_fun_t *fun, usb_target_t target,
[284c629]378 void *setup_packet, size_t setup_packet_size,
[6cb58e6]379 void *data_buffer, size_t data_buffer_size,
[284c629]380 usbhc_iface_transfer_in_callback_t callback, void *arg)
381{
[6cb58e6]382 VHC_DATA(vhc, fun);
[284c629]383
[6cb58e6]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;
[357a302]389 }
390
[6cb58e6]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;
[be9cbec]396
[6cb58e6]397 int rc = vhc_virtdev_add_transfer(vhc, transfer);
398 if (rc != EOK) {
399 free(transfer);
400 return rc;
[be9cbec]401 }
402
403 return EOK;
404}
405
[6cb58e6]406static int tell_address(ddf_fun_t *fun, devman_handle_t handle,
407 usb_address_t *address)
[be9cbec]408{
[6cb58e6]409 UNSUPPORTED("tell_address");
[be9cbec]410
[6cb58e6]411 return ENOTSUP;
[be9cbec]412}
413
[eb1a2f4]414static int usb_iface_get_hc_handle_rh_impl(ddf_fun_t *root_hub_fun,
415 devman_handle_t *handle)
416{
[6cb58e6]417 VHC_DATA(vhc, root_hub_fun);
[eb1a2f4]418
[6cb58e6]419 *handle = vhc->hc_fun->handle;
[eb1a2f4]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{
[6cb58e6]427 VHC_DATA(vhc, root_hub_fun);
[eb1a2f4]428
[6cb58e6]429 if (handle == 0) {
430 handle = root_hub_fun->handle;
431 }
[eb1a2f4]432
[6cb58e6]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 }
[be9cbec]441}
[4317827]442
443usbhc_iface_t vhc_iface = {
[ad104e0]444 .request_address = request_address,
[ce687bbe]445 .bind_address = bind_address,
[ad104e0]446 .release_address = release_address,
447
[6cb58e6]448 .register_endpoint = register_endpoint,
449 .unregister_endpoint = unregister_endpoint,
450
[4317827]451 .interrupt_out = interrupt_out,
452 .interrupt_in = interrupt_in,
[ad104e0]453
[6cb58e6]454 .bulk_in = bulk_in,
455 .bulk_out = bulk_out,
456
[284c629]457 .control_write = control_write,
458 .control_read = control_read
[63b4f90]459};
460
[357a302]461usb_iface_t vhc_usb_iface = {
462 .get_hc_handle = usb_iface_get_hc_handle_hc_impl,
463 .get_address = tell_address
464};
465
[eb1a2f4]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
[357a302]471
[b371844]472/**
473 * @}
474 */
Note: See TracBrowser for help on using the repository browser.