source: mainline/uspace/drv/bus/usb/xhci/bus.c@ ef1a3a8

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

Small changes. Temporarily fixed no device problem for endpoint logging. Added similar macro for device logging. Changed log messages to adopt these macros. TRB rings can be freed again. Made ring finalizers noexcept. Upon detach, the entire slot is disabled prior to unregistering endpoints in order to prevent invalid HC commands. Removed active endpoints count from XHCI device. Device context is freed in HC, so DCBAA is not touched from anywhere else.

  • Property mode set to 100644
File size: 14.9 KB
Line 
1/*
2 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
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/** @addtogroup libusbhost
29 * @{
30 */
31/** @file
32 * HC Endpoint management.
33 */
34
35#include <usb/host/utils/malloc32.h>
36#include <usb/host/ddf_helpers.h>
37#include <usb/host/endpoint.h>
38#include <usb/host/hcd.h>
39#include <usb/debug.h>
40
41#include <assert.h>
42#include <errno.h>
43#include <str_error.h>
44#include <macros.h>
45#include <stdbool.h>
46
47#include "hc.h"
48#include "bus.h"
49#include "endpoint.h"
50#include "transfers.h"
51
52
53/* FIXME Are these really static? Older HCs fetch it from descriptor. */
54/* FIXME Add USB3 options, if applicable. */
55static const usb_endpoint_desc_t ep0_desc = {
56 .endpoint_no = 0,
57 .direction = USB_DIRECTION_BOTH,
58 .transfer_type = USB_TRANSFER_CONTROL,
59 .max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
60 .packets = 1,
61};
62
63static int prepare_endpoint(xhci_endpoint_t *ep, const usb_endpoint_desc_t *desc)
64{
65 /* Extract information from endpoint_desc */
66 ep->base.endpoint = desc->endpoint_no;
67 ep->base.direction = desc->direction;
68 ep->base.transfer_type = desc->transfer_type;
69 ep->base.max_packet_size = desc->max_packet_size;
70 ep->base.packets = desc->packets;
71 ep->max_streams = desc->usb3.max_streams;
72 ep->max_burst = desc->usb3.max_burst;
73 // TODO add this property to usb_endpoint_desc_t and fetch it from ss companion desc
74 ep->mult = 0;
75
76 return xhci_endpoint_alloc_transfer_ds(ep);
77}
78
79static endpoint_t *create_endpoint(bus_t *base);
80
81static int address_device(xhci_hc_t *hc, xhci_device_t *dev)
82{
83 int err;
84
85 /* Enable new slot. */
86 if ((err = hc_enable_slot(hc, &dev->slot_id)) != EOK)
87 return err;
88 usb_log_debug2("Obtained slot ID: %u.\n", dev->slot_id);
89
90 /* Create and configure control endpoint. */
91 endpoint_t *ep0_base = create_endpoint(&hc->bus.base);
92 if (!ep0_base)
93 goto err_slot;
94
95 /* Temporary reference */
96 endpoint_add_ref(ep0_base);
97
98 xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
99
100 if ((err = prepare_endpoint(ep0, &ep0_desc)))
101 goto err_ep;
102
103 /* Register EP0 */
104 if ((err = xhci_device_add_endpoint(dev, ep0)))
105 goto err_prepared;
106
107 /* Address device */
108 if ((err = hc_address_device(hc, dev, ep0)))
109 goto err_added;
110
111 /* Temporary reference */
112 endpoint_del_ref(ep0_base);
113 return EOK;
114
115err_added:
116 xhci_device_remove_endpoint(ep0);
117err_prepared:
118 xhci_endpoint_free_transfer_ds(ep0);
119err_ep:
120 /* Temporary reference */
121 endpoint_del_ref(ep0_base);
122err_slot:
123 hc_disable_slot(hc, dev);
124 return err;
125}
126
127int xhci_bus_enumerate_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
128{
129 int err;
130 xhci_device_t *xhci_dev = xhci_device_get(dev);
131
132 /* Manage TT */
133 if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
134 /* For LS devices under HS hub */
135 /* TODO: How about SS hubs? */
136 dev->tt.address = dev->hub->address;
137 dev->tt.port = dev->port;
138 }
139 else {
140 /* Inherit hub's TT */
141 dev->tt = dev->hub->tt;
142 }
143
144 /* Calculate route string */
145 xhci_device_t *xhci_hub = xhci_device_get(dev->hub);
146 xhci_dev->tier = xhci_hub->tier + 1;
147 xhci_dev->route_str = xhci_hub->route_str;
148
149 /* Roothub port is not part of the route string */
150 if (xhci_dev->tier >= 2) {
151 const unsigned offset = 4 * (xhci_dev->tier - 2);
152 xhci_dev->route_str |= (dev->port & 0xf) << offset;
153 xhci_dev->rh_port = xhci_hub->rh_port;
154 }
155
156 fibril_mutex_lock(&bus->base.guard);
157 /* Assign an address to the device */
158 if ((err = address_device(hc, xhci_dev))) {
159 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
160 return err;
161 }
162
163 // TODO: Fetch descriptor of EP0 and reconfigure it accordingly
164 assert(xhci_dev->endpoints[0]);
165
166 assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
167 bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
168 fibril_mutex_unlock(&bus->base.guard);
169
170 /* Read the device descriptor, derive the match ids */
171 if ((err = hcd_ddf_device_explore(hc->hcd, dev))) {
172 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
173 goto err_address;
174 }
175
176 return EOK;
177
178err_address:
179 bus_release_address(&bus->base, dev->address);
180 return err;
181}
182
183static int unregister_endpoint(bus_t *, endpoint_t *);
184
185int xhci_bus_remove_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
186{
187 int err;
188 xhci_device_t *xhci_dev = xhci_device_get(dev);
189
190 /* Block creation of new endpoints and transfers. */
191 usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*xhci_dev));
192 fibril_mutex_lock(&dev->guard);
193 xhci_dev->online = false;
194 fibril_mutex_unlock(&dev->guard);
195
196 /* Abort running transfers. */
197 usb_log_debug2("Aborting all active transfers to device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*xhci_dev));
198 for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
199 xhci_endpoint_t *ep = xhci_dev->endpoints[i];
200 if (!ep || !ep->base.active)
201 continue;
202
203 /* FIXME: This is racy. */
204 if ((err = xhci_transfer_abort(&ep->active_transfer))) {
205 usb_log_warning("Failed to abort active transfer to "
206 " endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep),
207 str_error(err));
208 }
209 }
210
211 /* TODO: Figure out how to handle errors here. So far, they are reported and skipped. */
212
213 /* Make DDF (and all drivers) forget about the device. */
214 if ((err = ddf_fun_unbind(dev->fun))) {
215 usb_log_warning("Failed to unbind DDF function of device " XHCI_DEV_FMT ": %s",
216 XHCI_DEV_ARGS(*xhci_dev), str_error(err));
217 }
218
219 /* Disable the slot, dropping all endpoints. */
220 const uint32_t slot_id = xhci_dev->slot_id;
221 if ((err = hc_disable_slot(hc, xhci_dev))) {
222 usb_log_warning("Failed to disable slot of device " XHCI_DEV_FMT ": %s",
223 XHCI_DEV_ARGS(*xhci_dev), str_error(err));
224 }
225
226 bus->devices_by_slot[slot_id] = NULL;
227
228 /* Unregister remaining endpoints, freeing memory. */
229 for (unsigned i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
230 if (!xhci_dev->endpoints[i])
231 continue;
232
233 if ((err = unregister_endpoint(&bus->base, &xhci_dev->endpoints[i]->base))) {
234 usb_log_warning("Failed to unregister endpoint " XHCI_EP_FMT ": %s",
235 XHCI_EP_ARGS(*xhci_dev->endpoints[i]), str_error(err));
236 }
237 }
238
239 /* Destroy DDF device. */
240 /* XXX: Not a good idea, this method should not destroy devices. */
241 hcd_ddf_device_destroy(dev);
242
243 return EOK;
244}
245
246/** Ops receive generic bus_t pointer. */
247static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
248{
249 assert(bus_base);
250 return (xhci_bus_t *) bus_base;
251}
252
253static int enumerate_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
254{
255 xhci_hc_t *hc = hcd_get_driver_data(hcd);
256 assert(hc);
257
258 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
259 assert(bus);
260
261 return xhci_bus_enumerate_device(bus, hc, dev);
262}
263
264static int remove_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
265{
266 xhci_hc_t *hc = hcd_get_driver_data(hcd);
267 assert(hc);
268
269 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
270 assert(bus);
271
272 return xhci_bus_remove_device(bus, hc, dev);
273}
274
275static int online_device(bus_t *bus_base, hcd_t *hcd, device_t *dev_base)
276{
277 int err;
278
279 xhci_hc_t *hc = hcd_get_driver_data(hcd);
280 assert(hc);
281
282 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
283 assert(bus);
284
285 xhci_device_t *dev = xhci_device_get(dev_base);
286 assert(dev);
287
288 /* Transition the device from the Addressed to the Configured state. */
289 if ((err = hc_configure_device(hc, dev->slot_id))) {
290 usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
291 }
292
293 /* Block creation of new endpoints and transfers. */
294 usb_log_debug2("Device " XHCI_DEV_FMT " going online.", XHCI_DEV_ARGS(*dev));
295 fibril_mutex_lock(&dev_base->guard);
296 dev->online = true;
297 fibril_mutex_unlock(&dev_base->guard);
298
299 if ((err = ddf_fun_online(dev_base->fun))) {
300 return err;
301 }
302
303 return EOK;
304}
305
306static int offline_device(bus_t *bus_base, hcd_t *hcd, device_t *dev_base)
307{
308 int err;
309
310 xhci_hc_t *hc = hcd_get_driver_data(hcd);
311 assert(hc);
312
313 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
314 assert(bus);
315
316 xhci_device_t *dev = xhci_device_get(dev_base);
317 assert(dev);
318
319 /* Tear down all drivers working with the device. */
320 if ((err = ddf_fun_offline(dev_base->fun))) {
321 return err;
322 }
323
324 /* Block creation of new endpoints and transfers. */
325 usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*dev));
326 fibril_mutex_lock(&dev_base->guard);
327 dev->online = false;
328 fibril_mutex_unlock(&dev_base->guard);
329
330 /* We will need the endpoint array later for DS deallocation. */
331 xhci_endpoint_t *endpoints[ARRAY_SIZE(dev->endpoints)];
332 memcpy(endpoints, dev->endpoints, sizeof(dev->endpoints));
333
334 /* Remove all endpoints except zero. */
335 for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
336 if (!endpoints[i])
337 continue;
338
339 /* FIXME: Asserting here that the endpoint is not active. If not, EBUSY? */
340
341 xhci_device_remove_endpoint(endpoints[i]);
342 }
343
344 /* Issue one HC command to simultaneously drop all endpoints except zero. */
345 if ((err = hc_deconfigure_device(hc, dev->slot_id))) {
346 usb_log_warning("Failed to deconfigure device " XHCI_DEV_FMT ".",
347 XHCI_DEV_ARGS(*dev));
348 }
349
350 /* Tear down TRB ring / PSA. */
351 for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
352 if (!endpoints[i])
353 continue;
354
355 xhci_endpoint_free_transfer_ds(endpoints[i]);
356 }
357
358 /* FIXME: What happens to unregistered endpoints now? Destroy them? */
359
360 return EOK;
361}
362
363static endpoint_t *create_endpoint(bus_t *base)
364{
365 xhci_bus_t *bus = bus_to_xhci_bus(base);
366
367 xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t));
368 if (!ep)
369 return NULL;
370
371 if (xhci_endpoint_init(ep, bus)) {
372 free(ep);
373 return NULL;
374 }
375
376 return &ep->base;
377}
378
379static void destroy_endpoint(endpoint_t *ep)
380{
381 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
382
383 xhci_endpoint_fini(xhci_ep);
384 free(xhci_ep);
385}
386
387static int register_endpoint(bus_t *bus_base, device_t *device, endpoint_t *ep_base, const usb_endpoint_desc_t *desc)
388{
389 int err;
390 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
391 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
392
393 xhci_device_t *dev = xhci_device_get(device);
394
395 if ((err = prepare_endpoint(ep, desc)))
396 return err;
397
398 if ((err = xhci_device_add_endpoint(dev, ep)))
399 goto err_prepared;
400
401 usb_log_info("Endpoint " XHCI_EP_FMT " registered to XHCI bus.", XHCI_EP_ARGS(*ep));
402
403 xhci_ep_ctx_t ep_ctx;
404 xhci_setup_endpoint_context(ep, &ep_ctx);
405
406 if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
407 goto err_added;
408
409 return EOK;
410
411err_added:
412 xhci_device_remove_endpoint(ep);
413err_prepared:
414 xhci_endpoint_free_transfer_ds(ep);
415 return err;
416}
417
418static int unregister_endpoint(bus_t *bus_base, endpoint_t *ep_base)
419{
420 int err;
421 xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
422 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
423 xhci_device_t *dev = xhci_device_get(ep_base->device);
424
425 usb_log_info("Endpoint " XHCI_EP_FMT " unregistered from XHCI bus.", XHCI_EP_ARGS(*ep));
426
427 xhci_device_remove_endpoint(ep);
428
429 /* If device slot is still available, drop the endpoint. */
430 if (dev->slot_id) {
431 if ((err = hc_drop_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep)))) {
432 usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep), str_error(err));
433 }
434 } else {
435 usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
436 " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
437 }
438
439 /* Tear down TRB ring / PSA. */
440 xhci_endpoint_free_transfer_ds(ep);
441
442 return EOK;
443}
444
445static endpoint_t* find_endpoint(bus_t *bus_base, device_t *dev_base, usb_target_t target, usb_direction_t direction)
446{
447 xhci_device_t *dev = xhci_device_get(dev_base);
448
449 xhci_endpoint_t *ep = xhci_device_get_endpoint(dev, target.endpoint);
450 if (!ep)
451 return NULL;
452
453 return &ep->base;
454}
455
456static int reset_toggle(bus_t *bus_base, usb_target_t target, bool all)
457{
458 // TODO: Implement me!
459 return ENOTSUP;
460}
461
462static size_t count_bw(endpoint_t *ep, size_t size)
463{
464 // TODO: Implement me!
465 return 0;
466}
467
468/* Endpoint ops, optional (have generic fallback) */
469static bool endpoint_get_toggle(endpoint_t *ep)
470{
471 // TODO: Implement me!
472 return ENOTSUP;
473}
474
475static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
476{
477 // TODO: Implement me!
478}
479
480static int request_address(bus_t *bus_base, usb_address_t *addr, bool strict, usb_speed_t speed)
481{
482 assert(addr);
483
484 if (*addr != USB_ADDRESS_DEFAULT)
485 /* xHCI does not allow software to assign addresses. */
486 return ENOTSUP;
487
488 assert(strict);
489
490 xhci_bus_t *xhci_bus = bus_to_xhci_bus(bus_base);
491
492 if (xhci_bus->default_address_speed != USB_SPEED_MAX)
493 /* Already allocated */
494 return ENOENT;
495
496 xhci_bus->default_address_speed = speed;
497 return EOK;
498}
499
500static int release_address(bus_t *bus_base, usb_address_t addr)
501{
502 if (addr != USB_ADDRESS_DEFAULT)
503 return ENOTSUP;
504
505 xhci_bus_t *xhci_bus = bus_to_xhci_bus(bus_base);
506
507 xhci_bus->default_address_speed = USB_SPEED_MAX;
508 return EOK;
509}
510
511static usb_transfer_batch_t *create_batch(bus_t *bus, endpoint_t *ep)
512{
513 xhci_transfer_t *transfer = xhci_transfer_create(ep);
514 return &transfer->batch;
515}
516
517static void destroy_batch(usb_transfer_batch_t *batch)
518{
519 xhci_transfer_destroy(xhci_transfer_from_batch(batch));
520}
521
522static const bus_ops_t xhci_bus_ops = {
523#define BIND_OP(op) .op = op,
524 BIND_OP(enumerate_device)
525 BIND_OP(remove_device)
526
527 BIND_OP(online_device)
528 BIND_OP(offline_device)
529
530 BIND_OP(create_endpoint)
531 BIND_OP(destroy_endpoint)
532
533 BIND_OP(register_endpoint)
534 BIND_OP(unregister_endpoint)
535 BIND_OP(find_endpoint)
536
537 BIND_OP(request_address)
538 BIND_OP(release_address)
539 BIND_OP(reset_toggle)
540
541 BIND_OP(count_bw)
542
543 BIND_OP(endpoint_get_toggle)
544 BIND_OP(endpoint_set_toggle)
545
546 BIND_OP(create_batch)
547 BIND_OP(destroy_batch)
548#undef BIND_OP
549};
550
551int xhci_bus_init(xhci_bus_t *bus, xhci_hc_t *hc)
552{
553 assert(bus);
554
555 bus_init(&bus->base, sizeof(xhci_device_t));
556
557 bus->devices_by_slot = calloc(hc->max_slots, sizeof(xhci_device_t *));
558 if (!bus->devices_by_slot)
559 return ENOMEM;
560
561 bus->hc = hc;
562 bus->base.ops = xhci_bus_ops;
563 bus->default_address_speed = USB_SPEED_MAX;
564 return EOK;
565}
566
567void xhci_bus_fini(xhci_bus_t *bus)
568{
569
570}
571
572/**
573 * @}
574 */
Note: See TracBrowser for help on using the repository browser.