source: mainline/uspace/lib/usbhost/src/usb2_bus.c@ 9efad54

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

usb: move endpoint descriptor parsing to HC

This better separates responsibilities. Now the device driver does not
care about the contents of an endpoint descriptor, and HC can parse the
values according to device's actual speed.

Currently, it is device driver's responsibility to fetch endpoint
descriptors, map them and register pipes - sending the endpoint
descriptor back to HC. HC then parses it, and fills the pipe
description, which then sends back to device driver. We shall probably
fetch the endpoint descriptor from inside the HC (also fixing the USB
spec violation of communication with EP0).

  • Property mode set to 100644
File size: 12.8 KB
Line 
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>
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"
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
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 */
78static int get_speed(usb2_bus_t *bus, usb_address_t address, usb_speed_t *speed)
79{
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;
88
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))
125 return EINVAL;
126
127 const int ret = bus->devices[address].occupied ? EOK : ENOENT;
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);
143 }
144
145 return ret;
146}
147
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
188static const usb_target_t usb2_default_target = {{
189 .address = USB_ADDRESS_DEFAULT,
190 .endpoint = 0,
191}};
192
193static int address_device(device_t *dev)
194{
195 int err;
196
197 usb2_bus_t *bus = (usb2_bus_t *) dev->bus;
198
199 /* The default address is currently reserved for this device */
200 dev->address = USB_ADDRESS_DEFAULT;
201
202 /** Reserve address early, we want pretty log messages */
203 usb_address_t address = USB_ADDRESS_DEFAULT;
204 if ((err = request_address(bus, &address, false, dev->speed))) {
205 usb_log_error("Failed to reserve new address: %s.",
206 str_error(err));
207 return err;
208 }
209 usb_log_debug("Device(%d): Reserved new address.", address);
210
211 /* Add default pipe on default address */
212 usb_log_debug("Device(%d): Adding default target (0:0)", address);
213
214 usb_endpoint_descriptors_t ep0_desc = {
215 .endpoint.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
216 };
217 endpoint_t *default_ep;
218 err = bus_endpoint_add(dev, &ep0_desc, &default_ep);
219 if (err != EOK) {
220 usb_log_error("Device(%d): Failed to add default target: %s.",
221 address, str_error(err));
222 goto err_address;
223 }
224
225 if ((err = hcd_get_ep0_max_packet_size(&ep0_desc.endpoint.max_packet_size, &bus->base, dev)))
226 goto err_address;
227
228 /* Set new address */
229 const usb_device_request_setup_packet_t set_address = SET_ADDRESS(address);
230
231 usb_log_debug("Device(%d): Setting USB address.", address);
232 err = bus_device_send_batch_sync(dev, usb2_default_target, USB_DIRECTION_OUT,
233 NULL, 0, *(uint64_t *)&set_address, "set address");
234 if (err != 0) {
235 usb_log_error("Device(%d): Failed to set new address: %s.",
236 address, str_error(err));
237 goto err_default_control_ep;
238 }
239
240 /* We need to remove ep before we change the address */
241 if ((err = bus_endpoint_remove(default_ep))) {
242 usb_log_error("Device(%d): Failed to unregister default target: %s", address, str_error(err));
243 goto err_address;
244 }
245 endpoint_del_ref(default_ep);
246
247 dev->address = address;
248
249 /* Register EP on the new address */
250 usb_log_debug("Device(%d): Registering control EP.", address);
251 err = bus_endpoint_add(dev, &ep0_desc, NULL);
252 if (err != EOK) {
253 usb_log_error("Device(%d): Failed to register EP0: %s",
254 address, str_error(err));
255 goto err_address;
256 }
257
258 /* From now on, the device is officially online, yay! */
259 fibril_mutex_lock(&dev->guard);
260 dev->online = true;
261 fibril_mutex_unlock(&dev->guard);
262
263 return EOK;
264
265err_default_control_ep:
266 bus_endpoint_remove(default_ep);
267 endpoint_del_ref(default_ep);
268err_address:
269 release_address(bus, address);
270 return err;
271}
272
273/** Enumerate a new USB device
274 */
275static int usb2_bus_device_enumerate(device_t *dev)
276{
277 int err;
278 usb2_bus_t *bus = bus_to_usb2_bus(dev->bus);
279
280 /* The speed of the new device was reported by the hub when reserving
281 * default address.
282 */
283 if ((err = get_speed(bus, USB_ADDRESS_DEFAULT, &dev->speed))) {
284 usb_log_error("Failed to verify speed: %s.", str_error(err));
285 return err;
286 }
287 usb_log_debug("Found new %s speed USB device.", usb_str_speed(dev->speed));
288
289 if (!dev->hub) {
290 /* The device is the roothub */
291 dev->tt = (usb_tt_address_t) {
292 .address = -1,
293 .port = 0,
294 };
295 } else {
296 hcd_setup_device_tt(dev);
297 }
298
299 /* Assign an address to the device */
300 if ((err = address_device(dev))) {
301 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
302 return err;
303 }
304
305 /* Read the device descriptor, derive the match ids */
306 if ((err = hcd_device_explore(dev))) {
307 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
308 release_address(bus, dev->address);
309 return err;
310 }
311
312 return EOK;
313}
314
315/** Find endpoint.
316 * @param bus usb_bus structure, non-null.
317 * @param target Endpoint address.
318 * @param direction Communication direction.
319 * @return Pointer to endpoint_t structure representing given communication
320 * target, NULL if there is no such endpoint registered.
321 * @note Assumes that the internal mutex is locked.
322 */
323static endpoint_t *usb2_bus_find_ep(device_t *device, usb_target_t target, usb_direction_t direction)
324{
325 usb2_bus_t *bus = bus_to_usb2_bus(device->bus);
326
327 assert(device->address == target.address);
328
329 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
330 if (((direction == ep->direction)
331 || (ep->direction == USB_DIRECTION_BOTH)
332 || (direction == USB_DIRECTION_BOTH))
333 && (target.endpoint == ep->endpoint))
334 return ep;
335 }
336 return NULL;
337}
338
339static endpoint_t *usb2_bus_create_ep(device_t *dev, const usb_endpoint_descriptors_t *desc)
340{
341 endpoint_t *ep = malloc(sizeof(endpoint_t));
342 if (!ep)
343 return NULL;
344
345 endpoint_init(ep, dev, desc);
346 return ep;
347}
348
349static usb_target_t usb2_ep_to_target(endpoint_t *ep)
350{
351 assert(ep);
352 assert(ep->device);
353
354 return (usb_target_t) {{
355 .address = ep->device->address,
356 .endpoint = ep->endpoint,
357 }};
358}
359
360/** Register an endpoint to the bus. Reserves bandwidth.
361 * @param bus usb_bus structure, non-null.
362 * @param endpoint USB endpoint number.
363 */
364static int usb2_bus_register_ep(endpoint_t *ep)
365{
366 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
367 assert(fibril_mutex_is_locked(&bus->base.guard));
368 assert(ep);
369
370 /* Check for existence */
371 if (usb2_bus_find_ep(ep->device, usb2_ep_to_target(ep), ep->direction))
372 return EEXIST;
373
374 /* Check for available bandwidth */
375 if (ep->bandwidth > bus->free_bw)
376 return ENOSPC;
377
378 endpoint_add_ref(ep);
379 list_append(&ep->link, get_list(bus, ep->device->address));
380 bus->free_bw -= ep->bandwidth;
381
382 return EOK;
383}
384
385/** Release bandwidth reserved by the given endpoint.
386 */
387static int usb2_bus_unregister_ep(endpoint_t *ep)
388{
389 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
390 assert(ep);
391
392 list_remove(&ep->link);
393
394 bus->free_bw += ep->bandwidth;
395 endpoint_del_ref(ep);
396
397 return EOK;
398}
399
400static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
401{
402 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
403
404 if (!usb_target_is_valid(target))
405 return EINVAL;
406
407 if (mode == RESET_NONE)
408 return EOK;
409
410 int ret = ENOENT;
411
412 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
413 assert(ep->device->address == target.address);
414
415 if (mode == RESET_ALL || ep->endpoint == target.endpoint) {
416 endpoint_toggle_set(ep, 0);
417 ret = EOK;
418 }
419 }
420 return ret;
421}
422
423static int usb2_bus_register_default_address(bus_t *bus_base, usb_speed_t speed)
424{
425 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
426 usb_address_t addr = USB_ADDRESS_DEFAULT;
427 return request_address(bus, &addr, true, speed);
428}
429
430static int usb2_bus_release_default_address(bus_t *bus_base)
431{
432 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
433 return release_address(bus, USB_ADDRESS_DEFAULT);
434}
435
436const bus_ops_t usb2_bus_ops = {
437 .reserve_default_address = usb2_bus_register_default_address,
438 .release_default_address = usb2_bus_release_default_address,
439 .reset_toggle = usb2_bus_reset_toggle,
440 .device_enumerate = usb2_bus_device_enumerate,
441 .device_find_endpoint = usb2_bus_find_ep,
442 .endpoint_create = usb2_bus_create_ep,
443 .endpoint_register = usb2_bus_register_ep,
444 .endpoint_unregister = usb2_bus_unregister_ep,
445};
446
447/** Initialize to default state.
448 *
449 * @param bus usb_bus structure, non-null.
450 * @param available_bandwidth Size of the bandwidth pool.
451 * @param bw_count function to use to calculate endpoint bw requirements.
452 * @return Error code.
453 */
454int usb2_bus_init(usb2_bus_t *bus, size_t available_bandwidth)
455{
456 assert(bus);
457
458 bus_init(&bus->base, sizeof(device_t));
459 bus->base.ops = &usb2_bus_ops;
460
461 bus->free_bw = available_bandwidth;
462 bus->last_address = 0;
463 for (unsigned i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
464 list_initialize(&bus->devices[i].endpoint_list);
465 bus->devices[i].speed = USB_SPEED_MAX;
466 bus->devices[i].occupied = false;
467 }
468 return EOK;
469}
470/**
471 * @}
472 */
Note: See TracBrowser for help on using the repository browser.