source: mainline/uspace/drv/bus/usb/xhci/rh.c@ 8fe29a7c

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

xhci rh: try to detect hidden connected devices at startup

  • Property mode set to 100644
File size: 7.7 KB
RevLine 
[7bd99bf]1/*
[dcf0597]2 * Copyright (c) 2017 Michal Staruch
[7bd99bf]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
29/** @addtogroup drvusbxhci
30 * @{
31 */
32/** @file
33 * @brief The roothub structures abstraction.
34 */
[d967aa1]35
[7bd99bf]36#include <errno.h>
37#include <str_error.h>
[2770b66]38#include <usb/request.h>
[7bd99bf]39#include <usb/debug.h>
[20eaa82]40#include <usb/host/bus.h>
[867b375]41#include <usb/host/ddf_helpers.h>
[b80c1ab]42#include <usb/host/dma_buffer.h>
[2770b66]43#include <usb/host/hcd.h>
[a9fcd73]44#include <usb/port.h>
[867b375]45
[7bd99bf]46#include "debug.h"
[174788f]47#include "commands.h"
[370a1c8]48#include "endpoint.h"
[7bd99bf]49#include "hc.h"
50#include "hw_struct/trb.h"
[c8bb7090]51#include "rh.h"
[e9e24f2]52#include "transfers.h"
[7bd99bf]53
[9876e34]54/* This mask only lists registers, which imply port change. */
[9b56e528]55static const uint32_t port_events_mask =
[9876e34]56 XHCI_REG_MASK(XHCI_PORT_CSC) |
57 XHCI_REG_MASK(XHCI_PORT_PEC) |
58 XHCI_REG_MASK(XHCI_PORT_WRC) |
59 XHCI_REG_MASK(XHCI_PORT_OCC) |
60 XHCI_REG_MASK(XHCI_PORT_PRC) |
61 XHCI_REG_MASK(XHCI_PORT_PLC) |
62 XHCI_REG_MASK(XHCI_PORT_CEC);
63
[a9fcd73]64typedef struct rh_port {
65 usb_port_t base;
66 xhci_rh_t *rh;
67 uint8_t major;
68 xhci_port_regs_t *regs;
69 xhci_device_t *device;
70} rh_port_t;
71
[eb928c4]72/**
73 * Initialize the roothub subsystem.
74 */
[63431db2]75int xhci_rh_init(xhci_rh_t *rh, xhci_hc_t *hc)
[d32d51d]76{
[5c5c9407]77 assert(rh);
[816335c]78 assert(hc);
79
80 rh->hc = hc;
[9876e34]81 rh->max_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
[a9fcd73]82 rh->ports = calloc(rh->max_ports, sizeof(rh_port_t));
83 if (!rh->ports)
84 return ENOMEM;
[5c5c9407]85
[6832245]86 const int err = bus_device_init(&rh->device.base, &rh->hc->bus.base);
[a9fcd73]87 if (err) {
88 free(rh->ports);
[2cf28b9]89 return err;
[a9fcd73]90 }
91
92 for (unsigned i = 0; i < rh->max_ports; i++) {
93 usb_port_init(&rh->ports[i].base);
94 rh->ports[i].rh = rh;
95 rh->ports[i].regs = &rh->hc->op_regs->portrs[i];
96 }
[2cf28b9]97
98 /* Initialize route string */
99 rh->device.route_str = 0;
100 rh->device.tier = 0;
101
102 return EOK;
[d32d51d]103}
104
[eb928c4]105/**
106 * Finalize the RH subsystem.
107 */
108int xhci_rh_fini(xhci_rh_t *rh)
109{
110 assert(rh);
[a9fcd73]111 for (unsigned i = 0; i < rh->max_ports; i++)
112 usb_port_fini(&rh->ports[i].base);
[eb928c4]113 return EOK;
114}
115
[a9fcd73]116static rh_port_t *get_rh_port(usb_port_t *port)
[49e62998]117{
[a9fcd73]118 assert(port);
119 return (rh_port_t *) port;
[49e62998]120}
121
[eb928c4]122/**
123 * Create and setup a device directly connected to RH. As the xHCI is not using
124 * a virtual usbhub device for RH, this routine is called for devices directly.
[20eaa82]125 */
[a9fcd73]126static int rh_enumerate_device(usb_port_t *usb_port)
[867b375]127{
[20eaa82]128 int err;
[a9fcd73]129 rh_port_t *port = get_rh_port(usb_port);
[2cf28b9]130
[a9fcd73]131 if (port->major <= 2) {
132 /* USB ports for lower speeds needs their port reset first. */
133 XHCI_REG_SET(port->regs, XHCI_PORT_PR, 1);
134 if ((err = usb_port_wait_for_enabled(&port->base)))
135 return err;
[9b56e528]136 }
137
[0f79283b]138 /*
139 * We cannot know in advance, whether the speed in the status register
140 * is valid - it depends on the protocol. So we read it later, but then
141 * we have to check if the port is still enabled.
142 */
143 uint32_t status = XHCI_REG_RD_FIELD(&port->regs->portsc, 32);
144
145 bool enabled = !!(status & XHCI_REG_MASK(XHCI_PORT_PED));
146 if (!enabled)
147 return ENOENT;
148
149 unsigned psiv = (status & XHCI_REG_MASK(XHCI_PORT_PS)) >> XHCI_REG_SHIFT(XHCI_PORT_PS);
150 const usb_speed_t speed = port->rh->hc->speeds[psiv].usb_speed;
151
152 device_t *dev = hcd_ddf_fun_create(&port->rh->hc->base, speed);
[20eaa82]153 if (!dev) {
154 usb_log_error("Failed to create USB device function.");
155 return ENOMEM;
156 }
157
[a9fcd73]158 dev->hub = &port->rh->device.base;
159 dev->port = port - port->rh->ports + 1;
[eeca8a6]160
[a9fcd73]161 port->device = xhci_device_get(dev);
162 port->device->rh_port = dev->port;
[0206d35]163
[eb928c4]164 if ((err = bus_device_enumerate(dev))) {
[20eaa82]165 usb_log_error("Failed to enumerate USB device: %s", str_error(err));
166 return err;
167 }
168
169 if (!ddf_fun_get_name(dev->fun)) {
[6832245]170 bus_device_set_default_name(dev);
[20eaa82]171 }
172
173 if ((err = ddf_fun_bind(dev->fun))) {
[9620a54]174 usb_log_error("Failed to register device " XHCI_DEV_FMT " DDF function: %s.",
[a9fcd73]175 XHCI_DEV_ARGS(*port->device), str_error(err));
[20eaa82]176 goto err_usb_dev;
177 }
178
179 return EOK;
180
181err_usb_dev:
[32fb6bce]182 hcd_ddf_fun_destroy(dev);
[a9fcd73]183 port->device = NULL;
[20eaa82]184 return err;
[867b375]185}
186
[eb928c4]187/**
188 * Deal with a detached device.
[f45c78f]189 */
[a9fcd73]190static void rh_remove_device(usb_port_t *usb_port)
[f45c78f]191{
[a9fcd73]192 rh_port_t *port = get_rh_port(usb_port);
[a4e26882]193
[a9fcd73]194 assert(port->device);
195 usb_log_info("Device " XHCI_DEV_FMT " at port %zu has been disconnected.",
196 XHCI_DEV_ARGS(*port->device), port - port->rh->ports + 1);
[2cf28b9]197
[31cca4f3]198 /* Remove device from XHCI bus. */
[a9fcd73]199 bus_device_gone(&port->device->base);
[49e62998]200
[a9fcd73]201 /* Mark the device as detached. */
202 port->device = NULL;
[49e62998]203}
204
[eb928c4]205/**
[fb154e13]206 * Handle all changes on specified port.
[eb928c4]207 */
[fb154e13]208void xhci_rh_handle_port_change(xhci_rh_t *rh, uint8_t port_id)
[d32d51d]209{
[a9fcd73]210 rh_port_t * const port = &rh->ports[port_id - 1];
[07c08ea]211
[a9fcd73]212 uint32_t status = XHCI_REG_RD_FIELD(&port->regs->portsc, 32);
[5c5c9407]213
[a9fcd73]214 while (status & port_events_mask) {
[9b56e528]215 /*
216 * The PED bit in xHCI has RW1C semantics, which means that
217 * writing 1 to it will disable the port. Which means all
218 * standard mechanisms of register handling fails here.
219 */
[a9fcd73]220 XHCI_REG_WR_FIELD(&port->regs->portsc, status & ~XHCI_REG_MASK(XHCI_PORT_PED), 32);
[5c5c9407]221
[a9fcd73]222 if (status & XHCI_REG_MASK(XHCI_PORT_CSC)) {
[fb154e13]223 usb_log_info("Connected state changed on port %u.", port_id);
[a9fcd73]224 status &= ~XHCI_REG_MASK(XHCI_PORT_CSC);
[5c5c9407]225
[05770666]226 const bool connected = !!(status & XHCI_REG_MASK(XHCI_PORT_CCS));
[f45c78f]227 if (connected) {
[a9fcd73]228 usb_port_connected(&port->base, &rh_enumerate_device);
[f45c78f]229 } else {
[a9fcd73]230 usb_port_disabled(&port->base, &rh_remove_device);
[f45c78f]231 }
[dcf0597]232 }
[5c5c9407]233
[a9fcd73]234 if (status & XHCI_REG_MASK(XHCI_PORT_PRC)) {
235 status &= ~XHCI_REG_MASK(XHCI_PORT_PRC);
[fb154e13]236
[05770666]237 const bool enabled = !!(status & XHCI_REG_MASK(XHCI_PORT_PED));
[a9fcd73]238 if (enabled) {
[0f79283b]239 usb_port_enabled(&port->base);
[a9fcd73]240 } else {
241 usb_port_disabled(&port->base, &rh_remove_device);
242 }
243 }
244
245 status &= port_events_mask;
246 if (status != 0)
247 usb_log_debug("RH port %u change not handled: 0x%x", port_id, status);
[fb154e13]248
249 /* Make sure that PSCEG is 0 before exiting the loop. */
[a9fcd73]250 status = XHCI_REG_RD_FIELD(&port->regs->portsc, 32);
[916991b]251 }
[07c08ea]252}
253
[a9fcd73]254void xhci_rh_set_ports_protocol(xhci_rh_t *rh, unsigned offset, unsigned count, unsigned major)
[07c08ea]255{
[a9fcd73]256 for (unsigned i = offset; i < offset + count; i++)
257 rh->ports[i - 1].major = major;
[07c08ea]258}
259
[05770666]260void xhci_rh_startup(xhci_rh_t *rh)
261{
262 /* The reset changed status of all ports, and SW originated reason does
263 * not cause an interrupt.
264 */
265 for (uint8_t i = 1; i < rh->max_ports; ++i) {
266 xhci_rh_handle_port_change(rh, i + 1);
267
268 rh_port_t * const port = &rh->ports[i];
269
270 /*
271 * When xHCI starts, for some reasons USB 3 ports do not have
272 * the CSC bit, even though they are connected. Try to find
273 * such ports.
274 */
275 if (XHCI_REG_RD(port->regs, XHCI_PORT_CCS) && port->base.state == PORT_DISABLED)
276 usb_port_connected(&port->base, &rh_enumerate_device);
277 }
278}
279
[c8bb7090]280/**
281 * @}
282 */
Note: See TracBrowser for help on using the repository browser.