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

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

usbhost: refactoring

Moved the "online" attribute from xhci_device_t to device_t. Changed
USB2 bus implementation to produce online devices (not to break
functionality on older buses).

  • Property mode set to 100644
File size: 13.6 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 // FIXME: Implement me!
370
371 return ENOTSUP;
372}
373
374static endpoint_t *usb2_bus_create_ep(device_t *dev, const usb_endpoint_desc_t *desc)
375{
376 endpoint_t *ep = malloc(sizeof(endpoint_t));
377 if (!ep)
378 return NULL;
379
380 endpoint_init(ep, dev, desc);
381 return ep;
382}
383
384static usb_target_t usb2_ep_to_target(endpoint_t *ep)
385{
386 assert(ep);
387 assert(ep->device);
388
389 return (usb_target_t) {{
390 .address = ep->device->address,
391 .endpoint = ep->endpoint,
392 }};
393}
394
395/** Register an endpoint to the bus. Reserves bandwidth.
396 * @param bus usb_bus structure, non-null.
397 * @param endpoint USB endpoint number.
398 */
399static int usb2_bus_register_ep(endpoint_t *ep)
400{
401 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
402 assert(fibril_mutex_is_locked(&bus->base.guard));
403 assert(ep);
404
405 /* Check for existence */
406 if (usb2_bus_find_ep(ep->device, usb2_ep_to_target(ep), ep->direction))
407 return EEXIST;
408
409 /* Check for available bandwidth */
410 if (ep->bandwidth > bus->free_bw)
411 return ENOSPC;
412
413 endpoint_add_ref(ep);
414 list_append(&ep->link, get_list(bus, ep->device->address));
415 bus->free_bw -= ep->bandwidth;
416
417 return EOK;
418}
419
420/** Release bandwidth reserved by the given endpoint.
421 */
422static int usb2_bus_unregister_ep(endpoint_t *ep)
423{
424 usb2_bus_t *bus = bus_to_usb2_bus(ep->device->bus);
425 assert(ep);
426
427 list_remove(&ep->link);
428
429 bus->free_bw += ep->bandwidth;
430 endpoint_del_ref(ep);
431
432 return EOK;
433}
434
435static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
436{
437 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
438
439 if (!usb_target_is_valid(target))
440 return EINVAL;
441
442 if (mode == RESET_NONE)
443 return EOK;
444
445 int ret = ENOENT;
446
447 list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
448 assert(ep->device->address == target.address);
449
450 if (mode == RESET_ALL || ep->endpoint == target.endpoint) {
451 endpoint_toggle_set(ep, 0);
452 ret = EOK;
453 }
454 }
455 return ret;
456}
457
458static int usb2_bus_register_default_address(bus_t *bus_base, usb_speed_t speed)
459{
460 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
461 usb_address_t addr = USB_ADDRESS_DEFAULT;
462 return request_address(bus, &addr, true, speed);
463}
464
465static int usb2_bus_release_default_address(bus_t *bus_base)
466{
467 usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
468 return release_address(bus, USB_ADDRESS_DEFAULT);
469}
470
471const bus_ops_t usb2_bus_ops = {
472 .reserve_default_address = usb2_bus_register_default_address,
473 .release_default_address = usb2_bus_release_default_address,
474 .reset_toggle = usb2_bus_reset_toggle,
475 .device_enumerate = usb2_bus_device_enumerate,
476 .device_find_endpoint = usb2_bus_find_ep,
477 .device_online = usb2_bus_device_online,
478 .device_offline = usb2_bus_device_offline,
479 .endpoint_create = usb2_bus_create_ep,
480 .endpoint_register = usb2_bus_register_ep,
481 .endpoint_unregister = usb2_bus_unregister_ep,
482};
483
484/** Initialize to default state.
485 *
486 * @param bus usb_bus structure, non-null.
487 * @param available_bandwidth Size of the bandwidth pool.
488 * @param bw_count function to use to calculate endpoint bw requirements.
489 * @return Error code.
490 */
491int usb2_bus_init(usb2_bus_t *bus, size_t available_bandwidth)
492{
493 assert(bus);
494
495 bus_init(&bus->base, sizeof(device_t));
496 bus->base.ops = &usb2_bus_ops;
497
498 bus->free_bw = available_bandwidth;
499 bus->last_address = 0;
500 for (unsigned i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
501 list_initialize(&bus->devices[i].endpoint_list);
502 bus->devices[i].speed = USB_SPEED_MAX;
503 bus->devices[i].occupied = false;
504 }
505 return EOK;
506}
507/**
508 * @}
509 */
Note: See TracBrowser for help on using the repository browser.