source: mainline/uspace/lib/usbhost/src/usb2_bus.c@ 415c5116

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 415c5116 was 415c5116, checked in by Petr Manek <petr.manek@…>, 8 years ago

usbhost: first stab at usb2 disconnect (wip)

  • Property mode set to 100644
File size: 14.4 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_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
202static int address_device(device_t *dev)
203{
204 int err;
205
206 usb2_bus_t *bus = (usb2_bus_t *) dev->bus;
207
208 /* The default address is currently reserved for this device */
209 dev->address = USB_ADDRESS_DEFAULT;
210
211 /** Reserve address early, we want pretty log messages */
212 usb_address_t address = USB_ADDRESS_DEFAULT;
213 if ((err = request_address(bus, &address, false, dev->speed))) {
214 usb_log_error("Failed to reserve new address: %s.",
215 str_error(err));
216 return err;
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);
222
223 endpoint_t *default_ep;
224 err = bus_endpoint_add(dev, &usb2_default_control_ep, &default_ep);
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
231 uint16_t max_packet_size;
232 if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, &bus->base, dev)))
233 goto err_address;
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);
239 err = bus_device_send_batch_sync(dev, usb2_default_target, USB_DIRECTION_OUT,
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.",
243 address, str_error(err));
244 goto err_default_control_ep;
245 }
246
247 /* We need to remove ep before we change the address */
248 if ((err = bus_endpoint_remove(default_ep))) {
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
254 dev->address = address;
255
256 const usb_endpoint_desc_t control_ep = {
257 .endpoint_no = 0,
258 .transfer_type = USB_TRANSFER_CONTROL,
259 .direction = USB_DIRECTION_BOTH,
260 .max_packet_size = max_packet_size,
261 .packets = 1,
262 };
263
264 /* Register EP on the new address */
265 usb_log_debug("Device(%d): Registering control EP.", address);
266 err = bus_endpoint_add(dev, &control_ep, NULL);
267 if (err != EOK) {
268 usb_log_error("Device(%d): Failed to register EP0: %s",
269 address, str_error(err));
270 goto err_address;
271 }
272
273 /* From now on, the device is officially online, yay! */
274 fibril_mutex_lock(&dev->guard);
275 dev->online = true;
276 fibril_mutex_unlock(&dev->guard);
277
278 return EOK;
279
280err_default_control_ep:
281 bus_endpoint_remove(default_ep);
282 endpoint_del_ref(default_ep);
283err_address:
284 release_address(bus, address);
285 return err;
286}
287
288/** Enumerate a new USB device
289 */
290static int usb2_bus_device_enumerate(device_t *dev)
291{
292 int err;
293 usb2_bus_t *bus = bus_to_usb2_bus(dev->bus);
294
295 /* The speed of the new device was reported by the hub when reserving
296 * default address.
297 */
298 if ((err = get_speed(bus, USB_ADDRESS_DEFAULT, &dev->speed))) {
299 usb_log_error("Failed to verify speed: %s.", str_error(err));
300 return err;
301 }
302 usb_log_debug("Found new %s speed USB device.", usb_str_speed(dev->speed));
303
304 if (!dev->hub) {
305 /* The device is the roothub */
306 dev->tt = (usb_tt_address_t) {
307 .address = -1,
308 .port = 0,
309 };
310 } else {
311 hcd_setup_device_tt(dev);
312 }
313
314 /* Assign an address to the device */
315 if ((err = address_device(dev))) {
316 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
317 return err;
318 }
319
320 /* Read the device descriptor, derive the match ids */
321 if ((err = hcd_device_explore(dev))) {
322 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
323 release_address(bus, dev->address);
324 return err;
325 }
326
327 return EOK;
328}
329
330/** Find endpoint.
331 * @param bus usb_bus structure, non-null.
332 * @param target Endpoint address.
333 * @param direction Communication direction.
334 * @return Pointer to endpoint_t structure representing given communication
335 * target, NULL if there is no such endpoint registered.
336 * @note Assumes that the internal mutex is locked.
337 */
338static endpoint_t *usb2_bus_find_ep(device_t *device, usb_target_t target, usb_direction_t direction)
339{
340 usb2_bus_t *bus = bus_to_usb2_bus(device->bus);
341
342 assert(device->address == target.address);
343
344 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
345 if (((direction == ep->direction)
346 || (ep->direction == USB_DIRECTION_BOTH)
347 || (direction == USB_DIRECTION_BOTH))
348 && (target.endpoint == ep->endpoint))
349 return ep;
350 }
351 return NULL;
352}
353
354static int usb2_bus_device_online(device_t *device)
355{
356 usb2_bus_t *bus = bus_to_usb2_bus(device->bus);
357 assert(bus);
358
359 // FIXME: Implement me!
360
361 return ENOTSUP;
362}
363
364static int usb2_bus_device_offline(device_t *device)
365{
366 usb2_bus_t *bus = bus_to_usb2_bus(device->bus);
367 assert(bus);
368
369 int err;
370 /* Tear down all drivers working with the device. */
371 if ((err = ddf_fun_offline(device->fun))) {
372 return err;
373 }
374
375 /* Block creation of new endpoints and transfers. */
376 usb_log_info("Device(%d): Going offline.", device->address);
377 fibril_mutex_lock(&device->guard);
378 device->online = false;
379 fibril_mutex_unlock(&device->guard);
380
381 /* FIXME: This implementation leaves sleeping parts of drivers around.
382 * With XHCI bus, the HID driver disengages and completely deactivates
383 * when the DDF function is offlined. In USB2 bus, the driver receives
384 * dev_remove and disengages "on paper" but later when interrupt message arrives,
385 * some sleeping code is woken up and crashes the driver.
386 *
387 * The XHCI does 2 extra things that might prevent this behavior:
388 * (1) deconfigure the device,
389 * (2) deallocate all transfer TRB rings
390 */
391
392 return EOK;
393}
394
395static endpoint_t *usb2_bus_create_ep(device_t *dev, const usb_endpoint_desc_t *desc)
396{
397 endpoint_t *ep = malloc(sizeof(endpoint_t));
398 if (!ep)
399 return NULL;
400
401 endpoint_init(ep, dev, desc);
402 return ep;
403}
404
405static usb_target_t usb2_ep_to_target(endpoint_t *ep)
406{
407 assert(ep);
408 assert(ep->device);
409
410 return (usb_target_t) {{
411 .address = ep->device->address,
412 .endpoint = ep->endpoint,
413 }};
414}
415
416/** Register an endpoint to the bus. Reserves bandwidth.
417 * @param bus usb_bus structure, non-null.
418 * @param endpoint USB endpoint number.
419 */
420static int usb2_bus_register_ep(endpoint_t *ep)
421{
422 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
423 assert(fibril_mutex_is_locked(&bus->base.guard));
424 assert(ep);
425
426 /* Check for existence */
427 if (usb2_bus_find_ep(ep->device, usb2_ep_to_target(ep), ep->direction))
428 return EEXIST;
429
430 /* Check for available bandwidth */
431 if (ep->bandwidth > bus->free_bw)
432 return ENOSPC;
433
434 endpoint_add_ref(ep);
435 list_append(&ep->link, get_list(bus, ep->device->address));
436 bus->free_bw -= ep->bandwidth;
437
438 return EOK;
439}
440
441/** Release bandwidth reserved by the given endpoint.
442 */
443static int usb2_bus_unregister_ep(endpoint_t *ep)
444{
445 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
446 assert(ep);
447
448 list_remove(&ep->link);
449
450 bus->free_bw += ep->bandwidth;
451 endpoint_del_ref(ep);
452
453 return EOK;
454}
455
456static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
457{
458 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
459
460 if (!usb_target_is_valid(target))
461 return EINVAL;
462
463 if (mode == RESET_NONE)
464 return EOK;
465
466 int ret = ENOENT;
467
468 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
469 assert(ep->device->address == target.address);
470
471 if (mode == RESET_ALL || ep->endpoint == target.endpoint) {
472 endpoint_toggle_set(ep, 0);
473 ret = EOK;
474 }
475 }
476 return ret;
477}
478
479static int usb2_bus_register_default_address(bus_t *bus_base, usb_speed_t speed)
480{
481 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
482 usb_address_t addr = USB_ADDRESS_DEFAULT;
483 return request_address(bus, &addr, true, speed);
484}
485
486static int usb2_bus_release_default_address(bus_t *bus_base)
487{
488 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
489 return release_address(bus, USB_ADDRESS_DEFAULT);
490}
491
492const bus_ops_t usb2_bus_ops = {
493 .reserve_default_address = usb2_bus_register_default_address,
494 .release_default_address = usb2_bus_release_default_address,
495 .reset_toggle = usb2_bus_reset_toggle,
496 .device_enumerate = usb2_bus_device_enumerate,
497 .device_find_endpoint = usb2_bus_find_ep,
498 .device_online = usb2_bus_device_online,
499 .device_offline = usb2_bus_device_offline,
500 .endpoint_create = usb2_bus_create_ep,
501 .endpoint_register = usb2_bus_register_ep,
502 .endpoint_unregister = usb2_bus_unregister_ep,
503};
504
505/** Initialize to default state.
506 *
507 * @param bus usb_bus structure, non-null.
508 * @param available_bandwidth Size of the bandwidth pool.
509 * @param bw_count function to use to calculate endpoint bw requirements.
510 * @return Error code.
511 */
512int usb2_bus_init(usb2_bus_t *bus, size_t available_bandwidth)
513{
514 assert(bus);
515
516 bus_init(&bus->base, sizeof(device_t));
517 bus->base.ops = &usb2_bus_ops;
518
519 bus->free_bw = available_bandwidth;
520 bus->last_address = 0;
521 for (unsigned i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
522 list_initialize(&bus->devices[i].endpoint_list);
523 bus->devices[i].speed = USB_SPEED_MAX;
524 bus->devices[i].occupied = false;
525 }
526 return EOK;
527}
528/**
529 * @}
530 */
Note: See TracBrowser for help on using the repository browser.