source: mainline/uspace/lib/usbhost/src/usb2_bus.c@ 00d23a2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 00d23a2 was 32fb6bce, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usbhost: refactoring

This commit moves interrupt, status and schedule to bus
operations. Then the purpose of hcd_t is better defined, and split into
hc_driver_t and hc_device_t. hc_driver_t is used to wrap driver
implementation by the library (similar to how usb_driver_t is used to
wrap usb device drivers). hc_device_t is used as a parent for hc_t
inside drivers, and is allocated inside the DDF device node.

To support these changes, some local identifiers were renamed, some
functions were moved and/or renamed and their arguments changed. The
most notable one being hcd_send_batch → bus_device_send_batch.

  • Property mode set to 100644
File size: 13.0 KB
RevLine 
[41924f30]1/*
2 * Copyright (c) 2011 Jan Vesely
3 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup libusbhost
30 * @{
31 */
32/** @file
33 * HC Endpoint management.
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <macros.h>
39#include <stdbool.h>
[64fea02]40#include <stdlib.h>
41#include <str_error.h>
42#include <usb/debug.h>
43#include <usb/descriptor.h>
44#include <usb/request.h>
45#include <usb/usb.h>
46
47#include "endpoint.h"
48#include "ddf_helpers.h"
49
50#include "usb2_bus.h"
[41924f30]51
52/** Ops receive generic bus_t pointer. */
53static inline usb2_bus_t *bus_to_usb2_bus(bus_t *bus_base)
54{
55 assert(bus_base);
56 return (usb2_bus_t *) bus_base;
57}
58
59/** Get list that holds endpoints for given address.
60 * @param bus usb2_bus structure, non-null.
61 * @param addr USB address, must be >= 0.
62 * @return Pointer to the appropriate list.
63 */
64static list_t * get_list(usb2_bus_t *bus, usb_address_t addr)
65{
66 assert(bus);
67 assert(addr >= 0);
68 return &bus->devices[addr % ARRAY_SIZE(bus->devices)].endpoint_list;
69}
70
[20eaa82]71/** Get speed assigned to USB address.
72 *
73 * @param[in] bus Device manager structure to use.
74 * @param[in] address Address the caller wants to find.
75 * @param[out] speed Assigned speed.
76 * @return Error code.
77 */
[10cd715]78static int get_speed(usb2_bus_t *bus, usb_address_t address, usb_speed_t *speed)
[20eaa82]79{
[10cd715]80 if (!usb_address_is_valid(address))
81 return EINVAL;
82
83 if (!bus->devices[address].occupied)
84 return ENOENT;
85
86 if (speed)
87 *speed = bus->devices[address].speed;
[20eaa82]88
[10cd715]89 return EOK;
90}
91
92/** Get a free USB address
93 *
94 * @param[in] bus Device manager structure to use.
95 * @return Free address, or error code.
96 */
97static int get_free_address(usb2_bus_t *bus, usb_address_t *addr)
98{
99 usb_address_t new_address = bus->last_address;
100 do {
101 new_address = (new_address + 1) % USB_ADDRESS_COUNT;
102 if (new_address == USB_ADDRESS_DEFAULT)
103 new_address = 1;
104 if (new_address == bus->last_address)
105 return ENOSPC;
106 } while (bus->devices[new_address].occupied);
107
108 assert(new_address != USB_ADDRESS_DEFAULT);
109 bus->last_address = new_address;
110
111 *addr = new_address;
112 return EOK;
113}
114
115/** Unregister and destroy all endpoints using given address.
116 * @param bus usb_bus structure, non-null.
117 * @param address USB address.
118 * @param endpoint USB endpoint number.
119 * @param direction Communication direction.
120 * @return Error code.
121 */
122static int release_address(usb2_bus_t *bus, usb_address_t address)
123{
124 if (!usb_address_is_valid(address))
[20eaa82]125 return EINVAL;
126
127 const int ret = bus->devices[address].occupied ? EOK : ENOENT;
[10cd715]128 bus->devices[address].occupied = false;
129
130 list_t *list = get_list(bus, address);
131 for (link_t *link = list_first(list); link != NULL; ) {
132 endpoint_t *ep = list_get_instance(link, endpoint_t, link);
133 link = list_next(link, list);
134
135 assert(ep->device->address == address);
136 list_remove(&ep->link);
137
138 usb_log_warning("Endpoint %d:%d %s was left behind, removing.\n",
139 address, ep->endpoint, usb_str_direction(ep->direction));
140
141 /* Drop bus reference */
142 endpoint_del_ref(ep);
[20eaa82]143 }
144
145 return ret;
146}
147
[10cd715]148/** Request USB address.
149 * @param bus usb_device_manager
150 * @param addr Pointer to requested address value, place to store new address
151 * @parma strict Fail if the requested address is not available.
152 * @return Error code.
153 * @note Default address is only available in strict mode.
154 */
155static int request_address(usb2_bus_t *bus, usb_address_t *addr, bool strict, usb_speed_t speed)
156{
157 int err;
158
159 assert(bus);
160 assert(addr);
161
162 if (!usb_address_is_valid(*addr))
163 return EINVAL;
164
165 /* Only grant default address to strict requests */
166 if ((*addr == USB_ADDRESS_DEFAULT) && !strict) {
167 if ((err = get_free_address(bus, addr)))
168 return err;
169 }
170 else if (bus->devices[*addr].occupied) {
171 if (strict) {
172 return ENOENT;
173 }
174 if ((err = get_free_address(bus, addr)))
175 return err;
176 }
177
178 assert(usb_address_is_valid(*addr));
179 assert(bus->devices[*addr].occupied == false);
180 assert(*addr != USB_ADDRESS_DEFAULT || strict);
181
182 bus->devices[*addr].occupied = true;
183 bus->devices[*addr].speed = speed;
184
185 return EOK;
186}
187
[56db65d]188static const usb_endpoint_desc_t usb2_default_control_ep = {
189 .endpoint_no = 0,
190 .transfer_type = USB_TRANSFER_CONTROL,
191 .direction = USB_DIRECTION_BOTH,
192 .max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
193 .packets = 1,
194};
195
196
197static const usb_target_t usb2_default_target = {{
198 .address = USB_ADDRESS_DEFAULT,
199 .endpoint = 0,
200}};
201
[6832245]202static int address_device(device_t *dev)
[20eaa82]203{
204 int err;
205
[6832245]206 usb2_bus_t *bus = (usb2_bus_t *) dev->bus;
207
[58ac3ec]208 /* The default address is currently reserved for this device */
209 dev->address = USB_ADDRESS_DEFAULT;
210
[20eaa82]211 /** Reserve address early, we want pretty log messages */
[837d53d]212 usb_address_t address = USB_ADDRESS_DEFAULT;
[10cd715]213 if ((err = request_address(bus, &address, false, dev->speed))) {
[20eaa82]214 usb_log_error("Failed to reserve new address: %s.",
[8b8c164]215 str_error(err));
216 return err;
[20eaa82]217 }
218 usb_log_debug("Device(%d): Reserved new address.", address);
219
220 /* Add default pipe on default address */
221 usb_log_debug("Device(%d): Adding default target (0:0)", address);
[0206d35]222
223 endpoint_t *default_ep;
[6832245]224 err = bus_endpoint_add(dev, &usb2_default_control_ep, &default_ep);
[20eaa82]225 if (err != EOK) {
226 usb_log_error("Device(%d): Failed to add default target: %s.",
227 address, str_error(err));
228 goto err_address;
229 }
230
[306a36d]231 uint16_t max_packet_size;
[32fb6bce]232 if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, &bus->base, dev)))
[306a36d]233 goto err_address;
[20eaa82]234
235 /* Set new address */
236 const usb_device_request_setup_packet_t set_address = SET_ADDRESS(address);
237
238 usb_log_debug("Device(%d): Setting USB address.", address);
[32fb6bce]239 err = bus_device_send_batch_sync(dev, usb2_default_target, USB_DIRECTION_OUT,
[20eaa82]240 NULL, 0, *(uint64_t *)&set_address, "set address");
241 if (err != 0) {
242 usb_log_error("Device(%d): Failed to set new address: %s.",
[306a36d]243 address, str_error(err));
[56db65d]244 goto err_default_control_ep;
[20eaa82]245 }
246
[8b8c164]247 /* We need to remove ep before we change the address */
[6832245]248 if ((err = bus_endpoint_remove(default_ep))) {
[8b8c164]249 usb_log_error("Device(%d): Failed to unregister default target: %s", address, str_error(err));
250 goto err_address;
251 }
252 endpoint_del_ref(default_ep);
253
[20eaa82]254 dev->address = address;
255
[56db65d]256 const usb_endpoint_desc_t control_ep = {
257 .endpoint_no = 0,
258 .transfer_type = USB_TRANSFER_CONTROL,
259 .direction = USB_DIRECTION_BOTH,
[306a36d]260 .max_packet_size = max_packet_size,
261 .packets = 1,
[56db65d]262 };
263
[20eaa82]264 /* Register EP on the new address */
265 usb_log_debug("Device(%d): Registering control EP.", address);
[6832245]266 err = bus_endpoint_add(dev, &control_ep, NULL);
[20eaa82]267 if (err != EOK) {
268 usb_log_error("Device(%d): Failed to register EP0: %s",
269 address, str_error(err));
[8b8c164]270 goto err_address;
[20eaa82]271 }
272
273 return EOK;
274
[56db65d]275err_default_control_ep:
[6832245]276 bus_endpoint_remove(default_ep);
[0206d35]277 endpoint_del_ref(default_ep);
[20eaa82]278err_address:
[10cd715]279 release_address(bus, address);
[20eaa82]280 return err;
281}
282
283/** Enumerate a new USB device
284 */
[6832245]285static int usb2_bus_device_enumerate(device_t *dev)
[20eaa82]286{
287 int err;
[6832245]288 usb2_bus_t *bus = bus_to_usb2_bus(dev->bus);
[20eaa82]289
290 /* The speed of the new device was reported by the hub when reserving
291 * default address.
292 */
[10cd715]293 if ((err = get_speed(bus, USB_ADDRESS_DEFAULT, &dev->speed))) {
[20eaa82]294 usb_log_error("Failed to verify speed: %s.", str_error(err));
295 return err;
296 }
297 usb_log_debug("Found new %s speed USB device.", usb_str_speed(dev->speed));
298
[ff14aede]299 if (!dev->hub) {
300 /* The device is the roothub */
[8b8c164]301 dev->tt = (usb_tt_address_t) {
302 .address = -1,
303 .port = 0,
304 };
[ff14aede]305 } else {
306 hcd_setup_device_tt(dev);
[20eaa82]307 }
308
309 /* Assign an address to the device */
[6832245]310 if ((err = address_device(dev))) {
[20eaa82]311 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
312 return err;
313 }
314
315 /* Read the device descriptor, derive the match ids */
[32fb6bce]316 if ((err = hcd_device_explore(dev))) {
[20eaa82]317 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
[10cd715]318 release_address(bus, dev->address);
[20eaa82]319 return err;
320 }
321
322 return EOK;
323}
324
[41924f30]325/** Find endpoint.
326 * @param bus usb_bus structure, non-null.
327 * @param target Endpoint address.
328 * @param direction Communication direction.
329 * @return Pointer to endpoint_t structure representing given communication
330 * target, NULL if there is no such endpoint registered.
331 * @note Assumes that the internal mutex is locked.
332 */
[6832245]333static endpoint_t *usb2_bus_find_ep(device_t *device, usb_target_t target, usb_direction_t direction)
[41924f30]334{
[6832245]335 usb2_bus_t *bus = bus_to_usb2_bus(device->bus);
[41924f30]336
[a5b3de6]337 assert(device->address == target.address);
[41924f30]338
339 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
340 if (((direction == ep->direction)
341 || (ep->direction == USB_DIRECTION_BOTH)
342 || (direction == USB_DIRECTION_BOTH))
[a5b3de6]343 && (target.endpoint == ep->endpoint))
[41924f30]344 return ep;
345 }
346 return NULL;
347}
348
[6832245]349static endpoint_t *usb2_bus_create_ep(device_t *dev, const usb_endpoint_desc_t *desc)
[e6b9182]350{
351 endpoint_t *ep = malloc(sizeof(endpoint_t));
352 if (!ep)
353 return NULL;
354
[6832245]355 endpoint_init(ep, dev, desc);
[e6b9182]356 return ep;
357}
358
[a5b3de6]359static usb_target_t usb2_ep_to_target(endpoint_t *ep)
360{
361 assert(ep);
362 assert(ep->device);
363
364 return (usb_target_t) {{
365 .address = ep->device->address,
366 .endpoint = ep->endpoint,
367 }};
368}
369
[41924f30]370/** Register an endpoint to the bus. Reserves bandwidth.
371 * @param bus usb_bus structure, non-null.
372 * @param endpoint USB endpoint number.
373 */
[6832245]374static int usb2_bus_register_ep(endpoint_t *ep)
[41924f30]375{
[6832245]376 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
377 assert(fibril_mutex_is_locked(&bus->base.guard));
[41924f30]378 assert(ep);
379
380 /* Check for existence */
[6832245]381 if (usb2_bus_find_ep(ep->device, usb2_ep_to_target(ep), ep->direction))
[41924f30]382 return EEXIST;
383
384 /* Check for available bandwidth */
385 if (ep->bandwidth > bus->free_bw)
386 return ENOSPC;
387
[6832245]388 endpoint_add_ref(ep);
[56db65d]389 list_append(&ep->link, get_list(bus, ep->device->address));
[41924f30]390 bus->free_bw -= ep->bandwidth;
391
392 return EOK;
393}
394
395/** Release bandwidth reserved by the given endpoint.
396 */
[6832245]397static int usb2_bus_unregister_ep(endpoint_t *ep)
[41924f30]398{
[6832245]399 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
[41924f30]400 assert(ep);
401
[8b8c164]402 list_remove(&ep->link);
403
[41924f30]404 bus->free_bw += ep->bandwidth;
[6832245]405 endpoint_del_ref(ep);
[41924f30]406
407 return EOK;
408}
409
[17873ac7]410static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
[41924f30]411{
412 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
413
414 if (!usb_target_is_valid(target))
415 return EINVAL;
416
[17873ac7]417 if (mode == RESET_NONE)
418 return EOK;
419
[41924f30]420 int ret = ENOENT;
421
422 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
[a5b3de6]423 assert(ep->device->address == target.address);
424
[17873ac7]425 if (mode == RESET_ALL || ep->endpoint == target.endpoint) {
[41924f30]426 endpoint_toggle_set(ep, 0);
427 ret = EOK;
428 }
429 }
430 return ret;
431}
432
[10cd715]433static int usb2_bus_register_default_address(bus_t *bus_base, usb_speed_t speed)
[41924f30]434{
435 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
[10cd715]436 usb_address_t addr = USB_ADDRESS_DEFAULT;
437 return request_address(bus, &addr, true, speed);
[41924f30]438}
439
[10cd715]440static int usb2_bus_release_default_address(bus_t *bus_base)
[41924f30]441{
442 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
[10cd715]443 return release_address(bus, USB_ADDRESS_DEFAULT);
[41924f30]444}
445
[6832245]446const bus_ops_t usb2_bus_ops = {
[10cd715]447 .reserve_default_address = usb2_bus_register_default_address,
448 .release_default_address = usb2_bus_release_default_address,
[41924f30]449 .reset_toggle = usb2_bus_reset_toggle,
[6832245]450 .device_enumerate = usb2_bus_device_enumerate,
451 .device_find_endpoint = usb2_bus_find_ep,
452 .endpoint_create= usb2_bus_create_ep,
453 .endpoint_register= usb2_bus_register_ep,
454 .endpoint_unregister= usb2_bus_unregister_ep,
[41924f30]455};
456
457/** Initialize to default state.
458 *
459 * @param bus usb_bus structure, non-null.
460 * @param available_bandwidth Size of the bandwidth pool.
461 * @param bw_count function to use to calculate endpoint bw requirements.
462 * @return Error code.
463 */
[32fb6bce]464int usb2_bus_init(usb2_bus_t *bus, size_t available_bandwidth)
[41924f30]465{
466 assert(bus);
467
[32fb6bce]468 bus_init(&bus->base, sizeof(device_t));
[6832245]469 bus->base.ops = &usb2_bus_ops;
[41924f30]470
471 bus->free_bw = available_bandwidth;
472 bus->last_address = 0;
473 for (unsigned i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
474 list_initialize(&bus->devices[i].endpoint_list);
475 bus->devices[i].speed = USB_SPEED_MAX;
476 bus->devices[i].occupied = false;
477 }
478 return EOK;
479}
480/**
481 * @}
482 */
Note: See TracBrowser for help on using the repository browser.