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

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