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

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

xhci: rewritten isochronous transfers

There was a fundamental problem with relying on hardware to send
RING_OVERRUN/UNDERRUN events, which QEMU (and possibly others) do not
send. That resulted in not knowing if the transfer is still on schedule,
and having to ring the doorbell every time. That is not feasible,
because then the transfer can be more frequent than it should be.
Furthermore, it ignored the fact that isochronous TRBs are to be
scheduled not too late, but also not too soon (see 4.11.2.5 of the xHCI
spec).

Now, scheduling the TRBs to hardware is called feeding, and can be
delayed by setting a timer. Ring overruns/underruns are detected also at
the end of handling an event.

  • Property mode set to 100644
File size: 13.8 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/ddf_helpers.h>
36#include <usb/host/endpoint.h>
37#include <usb/host/hcd.h>
38#include <usb/descriptor.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/** Initial descriptor used for control endpoint 0 before more configuration is retrieved. */
54static const usb_endpoint_descriptors_t ep0_initial_desc = {
55 .endpoint.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
56};
57
58static endpoint_t *endpoint_create(device_t *, const usb_endpoint_descriptors_t *);
59
60/**
61 * Assign address and control endpoint to a new XHCI device. Once this function
62 * successfully returns, the device is online.
63 *
64 * @param[in] bus XHCI bus, in which the address is assigned.
65 * @param[in] dev New device to address and configure./e
66 * @return Error code.
67 */
68static int address_device(xhci_bus_t *bus, xhci_device_t *dev)
69{
70 int err;
71
72 /* Enable new slot. */
73 if ((err = hc_enable_slot(bus->hc, &dev->slot_id)) != EOK)
74 return err;
75 usb_log_debug2("Obtained slot ID: %u.\n", dev->slot_id);
76
77 /* Create and configure control endpoint. */
78 endpoint_t *ep0_base = endpoint_create(&dev->base, &ep0_initial_desc);
79 if (!ep0_base)
80 goto err_slot;
81
82 /* Bus reference */
83 endpoint_add_ref(ep0_base);
84 dev->base.endpoints[0] = ep0_base;
85
86 xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
87
88 /* Address device */
89 if ((err = hc_address_device(bus->hc, dev, ep0)))
90 goto err_added;
91
92 return EOK;
93
94err_added:
95 /* Bus reference */
96 endpoint_del_ref(ep0_base);
97 dev->base.endpoints[0] = NULL;
98err_slot:
99 hc_disable_slot(bus->hc, dev);
100 return err;
101}
102
103/**
104 * Retrieve and set maximum packet size for endpoint zero of a XHCI device.
105 *
106 * @param[in] hc Host controller, which manages the device.
107 * @param[in] dev Device with operational endpoint zero.
108 * @return Error code.
109 */
110static int setup_ep0_packet_size(xhci_hc_t *hc, xhci_device_t *dev)
111{
112 int err;
113
114 uint16_t max_packet_size;
115 if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, (bus_t *) &hc->bus, &dev->base)))
116 return err;
117
118 xhci_endpoint_t *ep0 = xhci_device_get_endpoint(dev, 0);
119 assert(ep0);
120
121 if (ep0->base.max_packet_size == max_packet_size)
122 return EOK;
123
124 ep0->base.max_packet_size = max_packet_size;
125 ep0->base.max_transfer_size = max_packet_size * ep0->base.packets_per_uframe;
126
127 xhci_ep_ctx_t ep_ctx;
128 xhci_setup_endpoint_context(ep0, &ep_ctx);
129
130 if ((err = hc_update_endpoint(hc, dev->slot_id, 0, &ep_ctx)))
131 return err;
132
133 return EOK;
134}
135
136/**
137 * Respond to a new device on the XHCI bus. Address it, negotiate packet size
138 * and retrieve USB descriptors.
139 *
140 * @param[in] bus XHCI bus, where the new device emerged.
141 * @param[in] dev XHCI device, which has appeared on the bus.
142 *
143 * @return Error code.
144 */
145static int device_enumerate(device_t *dev)
146{
147 int err;
148 xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
149 xhci_device_t *xhci_dev = xhci_device_get(dev);
150
151 hcd_setup_device_tt(dev);
152
153 /* Calculate route string */
154 xhci_device_t *xhci_hub = xhci_device_get(dev->hub);
155 xhci_dev->tier = xhci_hub->tier + 1;
156 xhci_dev->route_str = xhci_hub->route_str;
157
158 /* Roothub port is not part of the route string */
159 if (xhci_dev->tier >= 2) {
160 const unsigned offset = 4 * (xhci_dev->tier - 2);
161 xhci_dev->route_str |= (dev->port & 0xf) << offset;
162 xhci_dev->rh_port = xhci_hub->rh_port;
163 }
164
165 /* Assign an address to the device */
166 if ((err = address_device(bus, xhci_dev))) {
167 usb_log_error("Failed to setup address of the new device: %s", str_error(err));
168 return err;
169 }
170
171 /* Setup EP0 might already need to issue a transfer. */
172 fibril_mutex_lock(&bus->base.guard);
173 assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
174 bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
175 fibril_mutex_unlock(&bus->base.guard);
176
177 if ((err = setup_ep0_packet_size(bus->hc, xhci_dev))) {
178 usb_log_error("Failed to setup control endpoint of the new device: %s", str_error(err));
179 goto err_address;
180 }
181
182 /* Read the device descriptor, derive the match ids */
183 if ((err = hcd_device_explore(dev))) {
184 usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
185 goto err_address;
186 }
187
188 return EOK;
189
190err_address:
191 // TODO: deaddress device
192 return err;
193}
194
195static int endpoint_unregister(endpoint_t *);
196
197/**
198 * Remove device from XHCI bus. Transition it to the offline state, abort all
199 * ongoing transfers and unregister all of its endpoints.
200 *
201 * Bus callback.
202 *
203 * @param[in] bus XHCI bus, from which the device is removed.
204 * @param[in] dev XHCI device, which is removed from the bus.
205 * @return Error code.
206 */
207static int device_remove(device_t *dev)
208{
209 int err;
210 xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
211 xhci_device_t *xhci_dev = xhci_device_get(dev);
212
213 /* Block creation of new endpoints and transfers. */
214 usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*xhci_dev));
215 fibril_mutex_lock(&dev->guard);
216 dev->online = false;
217 fibril_mutex_unlock(&dev->guard);
218
219 /* Abort running transfers. */
220 usb_log_debug2("Aborting all active transfers to device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*xhci_dev));
221 for (usb_endpoint_t i = 0; i < USB_ENDPOINT_MAX; ++i) {
222 xhci_endpoint_t *ep = xhci_device_get_endpoint(xhci_dev, i);
223 if (!ep)
224 continue;
225
226 endpoint_abort(&ep->base);
227 }
228
229 /* TODO: Figure out how to handle errors here. So far, they are reported and skipped. */
230
231 /* Make DDF (and all drivers) forget about the device. */
232 if ((err = ddf_fun_unbind(dev->fun))) {
233 usb_log_warning("Failed to unbind DDF function of device " XHCI_DEV_FMT ": %s",
234 XHCI_DEV_ARGS(*xhci_dev), str_error(err));
235 }
236
237 /* Disable the slot, dropping all endpoints. */
238 const uint32_t slot_id = xhci_dev->slot_id;
239 if ((err = hc_disable_slot(bus->hc, xhci_dev))) {
240 usb_log_warning("Failed to disable slot of device " XHCI_DEV_FMT ": %s",
241 XHCI_DEV_ARGS(*xhci_dev), str_error(err));
242 }
243
244 bus->devices_by_slot[slot_id] = NULL;
245
246 /* Unregister remaining endpoints, freeing memory. */
247 for (usb_endpoint_t i = 0; i < USB_ENDPOINT_MAX; ++i) {
248 if (!dev->endpoints[i])
249 continue;
250
251 if ((err = endpoint_unregister(dev->endpoints[i]))) {
252 usb_log_warning("Failed to unregister endpoint " XHCI_EP_FMT ": %s",
253 XHCI_EP_ARGS(*xhci_device_get_endpoint(xhci_dev, i)), str_error(err));
254 }
255 }
256
257 /* Destroy DDF device. */
258 /* XXX: Not a good idea, this method should not destroy devices. */
259 hcd_ddf_fun_destroy(dev);
260
261 return EOK;
262}
263
264/**
265 * Reverts things device_offline did, getting the device back up.
266 *
267 * Bus callback.
268 */
269static int device_online(device_t *dev_base)
270{
271 int err;
272
273 xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
274 assert(bus);
275
276 xhci_device_t *dev = xhci_device_get(dev_base);
277 assert(dev);
278
279 /* Transition the device from the Addressed to the Configured state. */
280 if ((err = hc_configure_device(bus->hc, dev->slot_id))) {
281 usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
282 }
283
284 /* Allow creation of new endpoints and transfers. */
285 usb_log_debug2("Device " XHCI_DEV_FMT " going online.", XHCI_DEV_ARGS(*dev));
286 fibril_mutex_lock(&dev_base->guard);
287 dev_base->online = true;
288 fibril_mutex_unlock(&dev_base->guard);
289
290 if ((err = ddf_fun_online(dev_base->fun))) {
291 return err;
292 }
293
294 return EOK;
295}
296
297/**
298 * Make given device offline. Offline the DDF function, tear down all
299 * endpoints, issue Deconfigure Device command to xHC.
300 *
301 * Bus callback.
302 */
303static int device_offline(device_t *dev_base)
304{
305 int err;
306
307 xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
308 assert(bus);
309
310 xhci_device_t *dev = xhci_device_get(dev_base);
311 assert(dev);
312
313 /* Tear down all drivers working with the device. */
314 if ((err = ddf_fun_offline(dev_base->fun))) {
315 return err;
316 }
317
318 /* Block creation of new endpoints and transfers. */
319 usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*dev));
320 fibril_mutex_lock(&dev_base->guard);
321 dev_base->online = false;
322 fibril_mutex_unlock(&dev_base->guard);
323
324 /* We will need the endpoint array later for DS deallocation. */
325 endpoint_t *endpoints[USB_ENDPOINT_MAX];
326 memcpy(endpoints, dev->base.endpoints, sizeof(endpoints));
327
328 for (usb_endpoint_t i = 1; i < USB_ENDPOINT_MAX; ++i) {
329 /* FIXME: Asserting here that the endpoint is not active. If not, EBUSY? */
330 dev->base.endpoints[i] = NULL;
331 }
332
333 /* Issue one HC command to simultaneously drop all endpoints except zero. */
334 if ((err = hc_deconfigure_device(bus->hc, dev->slot_id))) {
335 usb_log_warning("Failed to deconfigure device " XHCI_DEV_FMT ".",
336 XHCI_DEV_ARGS(*dev));
337 }
338
339 /* Tear down TRB ring / PSA. */
340 for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
341 if (!endpoints[i])
342 continue;
343
344 /* Bus reference */
345 endpoint_del_ref(endpoints[i]);
346 }
347
348 return EOK;
349}
350
351/**
352 * Create a new xHCI endpoint structure.
353 *
354 * Bus callback.
355 */
356static endpoint_t *endpoint_create(device_t *dev, const usb_endpoint_descriptors_t *desc)
357{
358 const usb_transfer_type_t type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
359
360 xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t)
361 + (type == USB_TRANSFER_ISOCHRONOUS) * sizeof(*ep->isoch));
362 if (!ep)
363 return NULL;
364
365 if (xhci_endpoint_init(ep, dev, desc)) {
366 free(ep);
367 return NULL;
368 }
369
370 return &ep->base;
371}
372
373/**
374 * Destroy given xHCI endpoint structure.
375 *
376 * Bus callback.
377 */
378static void endpoint_destroy(endpoint_t *ep)
379{
380 xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
381
382 xhci_endpoint_fini(xhci_ep);
383 free(xhci_ep);
384}
385
386/**
387 * Register an andpoint to the bus. Allocate its transfer ring(s) and inform
388 * xHC about it.
389 *
390 * Bus callback.
391 */
392static int endpoint_register(endpoint_t *ep_base)
393{
394 int err;
395 xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
396 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
397
398 xhci_device_t *dev = xhci_device_get(ep_base->device);
399
400 usb_log_info("Endpoint " XHCI_EP_FMT " registered to XHCI bus.", XHCI_EP_ARGS(*ep));
401
402 xhci_ep_ctx_t ep_ctx;
403 xhci_setup_endpoint_context(ep, &ep_ctx);
404
405 if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
406 return err;
407
408 return EOK;
409}
410
411/**
412 * Unregister an endpoint. If the device is still available, inform the xHC
413 * about it. Destroy resources allocated when registering.
414 *
415 * Bus callback.
416 */
417static int endpoint_unregister(endpoint_t *ep_base)
418{
419 int err;
420 xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
421 xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
422 xhci_device_t *dev = xhci_device_get(ep_base->device);
423
424 usb_log_info("Endpoint " XHCI_EP_FMT " unregistered from XHCI bus.", XHCI_EP_ARGS(*ep));
425
426 /* If device slot is still available, drop the endpoint. */
427 if (dev->slot_id) {
428 if ((err = hc_drop_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep)))) {
429 usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep), str_error(err));
430 }
431 } else {
432 usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
433 " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
434 }
435
436 return EOK;
437}
438
439/**
440 * Schedule a batch for xHC.
441 *
442 * Bus callback.
443 */
444static int batch_schedule(usb_transfer_batch_t *batch)
445{
446 assert(batch);
447 xhci_hc_t *hc = bus_to_hc(endpoint_get_bus(batch->ep));
448
449 if (!batch->target.address) {
450 usb_log_error("Attempted to schedule transfer to address 0.");
451 return EINVAL;
452 }
453
454 return xhci_transfer_schedule(hc, batch);
455}
456
457static const bus_ops_t xhci_bus_ops = {
458 .interrupt = hc_interrupt,
459 .status = hc_status,
460
461 .device_enumerate = device_enumerate,
462 .device_remove = device_remove,
463 .device_online = device_online,
464 .device_offline = device_offline,
465
466 .endpoint_create = endpoint_create,
467 .endpoint_destroy = endpoint_destroy,
468 .endpoint_register = endpoint_register,
469 .endpoint_unregister = endpoint_unregister,
470
471 .batch_schedule = batch_schedule,
472 .batch_create = xhci_transfer_create,
473 .batch_destroy = xhci_transfer_destroy,
474};
475
476/** Initialize XHCI bus.
477 * @param[in] bus Allocated XHCI bus to initialize.
478 * @param[in] hc Associated host controller, which manages the bus.
479 *
480 * @return Error code.
481 */
482int xhci_bus_init(xhci_bus_t *bus, xhci_hc_t *hc)
483{
484 assert(bus);
485
486 bus_init(&bus->base, sizeof(xhci_device_t));
487
488 bus->devices_by_slot = calloc(hc->max_slots, sizeof(xhci_device_t *));
489 if (!bus->devices_by_slot)
490 return ENOMEM;
491
492 bus->hc = hc;
493 bus->base.ops = &xhci_bus_ops;
494 return EOK;
495}
496
497/** Finalize XHCI bus.
498 * @param[in] bus XHCI bus to finalize.
499 */
500void xhci_bus_fini(xhci_bus_t *bus)
501{
502 // FIXME: Ensure there are no more devices?
503 free(bus->devices_by_slot);
504 // FIXME: Something else we forgot?
505}
506
507/**
508 * @}
509 */
Note: See TracBrowser for help on using the repository browser.