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

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

usbhost: manage endpoints by library + get/set_toggle → reset_toggle

That simplifies things A LOT. Now you can find endpoints for device in
an array inside device. This array is managed automatically in
register/unregister endpoint. HC drivers still needs to write to it when
setting up/tearing down the device.

Sorry for these two changes being in one commit, but splitting them
would be simply more work for no benefit.

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