1 /* $NetBSD: uhub.c,v 1.5 1998/08/02 22:30:52 augustss Exp $ */ 2 3 /* 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Author: Lennart Augustsson <augustss@carlstedt.se> 8 * Carlstedt Research & Technology 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/malloc.h> 44 #include <sys/device.h> 45 #include <sys/proc.h> 46 #include <sys/device.h> 47 48 #include <dev/usb/usb.h> 49 50 #include <dev/usb/usbdi.h> 51 #include <dev/usb/usbdi_util.h> 52 #include <dev/usb/usbdivar.h> 53 54 #ifdef USB_DEBUG 55 #define DPRINTF(x) if (usbdebug) printf x 56 #define DPRINTFN(n,x) if (usbdebug>(n)) printf x 57 extern int usbdebug; 58 #else 59 #define DPRINTF(x) 60 #define DPRINTFN(n,x) 61 #endif 62 63 struct uhub_softc { 64 struct device sc_dev; /* base device */ 65 usbd_device_handle sc_hub; /* USB device */ 66 usbd_pipe_handle sc_ipipe; /* interrupt pipe */ 67 u_int8_t sc_status[1]; /* XXX more ports */ 68 u_char sc_running; 69 }; 70 71 int uhub_match __P((struct device *, struct cfdata *, void *)); 72 void uhub_attach __P((struct device *, struct device *, void *)); 73 74 usbd_status uhub_init_port __P((int, struct usbd_port *, usbd_device_handle)); 75 void uhub_disconnect __P((struct uhub_softc *sc, struct device *parent, 76 struct usbd_port *up, int portno)); 77 usbd_status uhub_explore __P((struct device *parent, usbd_device_handle hub)); 78 void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); 79 80 /*void uhub_disco __P((void *));*/ 81 82 extern struct cfdriver uhub_cd; 83 84 struct cfattach uhub_ca = { 85 sizeof(struct uhub_softc), uhub_match, uhub_attach 86 }; 87 88 struct cfattach uhub_uhub_ca = { 89 sizeof(struct uhub_softc), uhub_match, uhub_attach 90 }; 91 92 int 93 uhub_match(parent, match, aux) 94 struct device *parent; 95 struct cfdata *match; 96 void *aux; 97 { 98 struct usb_attach_arg *uaa = aux; 99 usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); 100 101 DPRINTFN(1,("uhub_match, dd=%p\n", dd)); 102 /* 103 * The subclass for hubs seems to be 0 for some and 1 for others, 104 * so we just ignore the subclass. 105 */ 106 if (uaa->iface == 0 && dd->bDeviceClass == UCLASS_HUB) 107 return (UMATCH_DEVCLASS_DEVSUBCLASS); 108 return (UMATCH_NONE); 109 } 110 111 void 112 uhub_attach(parent, self, aux) 113 struct device *parent; 114 struct device *self; 115 void *aux; 116 { 117 struct uhub_softc *sc = (struct uhub_softc *)self; 118 struct usb_attach_arg *uaa = aux; 119 usbd_device_handle dev = uaa->device; 120 char devinfo[1024]; 121 usbd_status r; 122 struct usbd_hub *hub; 123 usb_device_request_t req; 124 usb_hub_descriptor_t hubdesc; 125 int port, nports; 126 usbd_interface_handle iface; 127 usb_endpoint_descriptor_t *ed; 128 129 DPRINTFN(1,("uhub_attach\n")); 130 sc->sc_hub = dev; 131 usbd_devinfo(dev, 1, devinfo); 132 printf(": %s\n", devinfo); 133 134 r = usbd_set_config_no(dev, 0, 1); 135 if (r != USBD_NORMAL_COMPLETION) { 136 printf("%s: configuration failed, error=%d\n", 137 sc->sc_dev.dv_xname, r); 138 return; 139 } 140 141 if (dev->depth > USB_HUB_MAX_DEPTH) { 142 printf("%s: hub depth (%d) exceeded, hub ignored\n", 143 sc->sc_dev.dv_xname, USB_HUB_MAX_DEPTH); 144 return; 145 } 146 147 /* Get hub descriptor. */ 148 req.bmRequestType = UT_READ_CLASS_DEVICE; 149 req.bRequest = UR_GET_DESCRIPTOR; 150 USETW(req.wValue, 0); 151 USETW(req.wIndex, 0); 152 USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); 153 DPRINTFN(1,("usb_init_hub: getting hub descriptor\n")); 154 /* XXX not correct for hubs with >7 ports */ 155 r = usbd_do_request(dev, &req, &hubdesc); 156 if (r != USBD_NORMAL_COMPLETION) { 157 printf("%s: getting hub descriptor failed, error=%d\n", 158 sc->sc_dev.dv_xname, r); 159 return; 160 } 161 162 nports = hubdesc.bNbrPorts; 163 hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), 164 M_USB, M_NOWAIT); 165 if (hub == 0) 166 return; 167 dev->hub = hub; 168 dev->hub->hubdata = sc; 169 hub->explore = uhub_explore; 170 hub->hubdesc = hubdesc; 171 172 DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, parent->selfpowered=%d\n", 173 dev->self_powered, dev->powersrc->parent, 174 dev->powersrc->parent ? 175 dev->powersrc->parent->self_powered : 0)); 176 if (!dev->self_powered && dev->powersrc->parent && 177 !dev->powersrc->parent->self_powered) { 178 printf("%s: bus powered hub connected to bus powered hub, ignored\n", 179 sc->sc_dev.dv_xname); 180 return; 181 } 182 183 /* Set up interrupt pipe. */ 184 r = usbd_device2interface_handle(dev, 0, &iface); 185 if (r != USBD_NORMAL_COMPLETION) { 186 printf("%s: no interface handle\n", sc->sc_dev.dv_xname); 187 return; 188 } 189 ed = usbd_interface2endpoint_descriptor(iface, 0); 190 if (ed == 0) { 191 printf("%s: no endpoint descriptor\n", sc->sc_dev.dv_xname); 192 return; 193 } 194 if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { 195 printf("%s: bad interrupt endpoint\n", sc->sc_dev.dv_xname); 196 return; 197 } 198 199 r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK, 200 &sc->sc_ipipe, sc, sc->sc_status, 201 sizeof(sc->sc_status), 202 uhub_intr); 203 if (r != USBD_NORMAL_COMPLETION) { 204 printf("%s: cannot open interrupt pipe\n",sc->sc_dev.dv_xname); 205 return; 206 } 207 208 for (port = 1; port <= nports; port++) { 209 r = uhub_init_port(port, &hub->ports[port-1], dev); 210 if (r != USBD_NORMAL_COMPLETION) 211 printf("%s: init of port %d failed\n", 212 sc->sc_dev.dv_xname, port); 213 } 214 sc->sc_running = 1; 215 } 216 217 usbd_status 218 uhub_init_port(port, uport, dev) 219 int port; 220 struct usbd_port *uport; 221 usbd_device_handle dev; 222 { 223 usbd_status r; 224 u_int16_t pstatus; 225 226 uport->device = 0; 227 uport->parent = dev; 228 r = usbd_get_port_status(dev, port, &uport->status); 229 if (r != USBD_NORMAL_COMPLETION) 230 return r; 231 pstatus = UGETW(uport->status.wPortStatus); 232 DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x change=0x%04x\n", 233 port, pstatus, UGETW(uport->status.wPortChange))); 234 if ((pstatus & UPS_PORT_POWER) == 0) { 235 /* Port lacks power, turn it on */ 236 r = usbd_set_port_feature(dev, port, UHF_PORT_POWER); 237 if (r != USBD_NORMAL_COMPLETION) 238 return (r); 239 r = usbd_get_port_status(dev, port, &uport->status); 240 if (r != USBD_NORMAL_COMPLETION) 241 return (r); 242 DPRINTF(("usb_init_port: turn on port %d power status=0x%04x change=0x%04x\n", 243 port, UGETW(uport->status.wPortStatus), 244 UGETW(uport->status.wPortChange))); 245 /* Wait for stable power. */ 246 usbd_delay_ms(dev->bus, dev->hub->hubdesc.bPwrOn2PwrGood * 247 UHD_PWRON_FACTOR); 248 } 249 if (dev->self_powered) 250 /* Self powered hub, give ports maximum current. */ 251 uport->power = USB_MAX_POWER; 252 else 253 uport->power = USB_MIN_POWER; 254 return (USBD_NORMAL_COMPLETION); 255 } 256 257 usbd_status 258 uhub_explore(parent, dev) 259 struct device *parent; 260 usbd_device_handle dev; 261 { 262 usb_hub_descriptor_t *hd = &dev->hub->hubdesc; 263 struct uhub_softc *sc = dev->hub->hubdata; 264 struct usbd_port *up; 265 usbd_status r; 266 int port; 267 int change, status; 268 269 DPRINTF(("uhub_explore dev=%p addr=%d\n", dev, dev->address)); 270 271 if (!sc->sc_running) 272 return (USBD_NOT_STARTED); 273 274 /* Ignore hubs that are too deep. */ 275 if (dev->depth > USB_HUB_MAX_DEPTH) 276 return (USBD_TOO_DEEP); 277 278 for(port = 1; port <= hd->bNbrPorts; port++) { 279 up = &dev->hub->ports[port-1]; 280 r = usbd_get_port_status(dev, port, &up->status); 281 if (r != USBD_NORMAL_COMPLETION) { 282 DPRINTF(("uhub_explore: get port status failed, error=%d\n", 283 r)); 284 continue; 285 } 286 status = UGETW(up->status.wPortStatus); 287 change = UGETW(up->status.wPortChange); 288 DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n", 289 port, status, change)); 290 if (!(change & UPS_CURRENT_CONNECT_STATUS)) { 291 /* No status change, just do recursive explore. */ 292 if (up->device && up->device->hub) 293 up->device->hub->explore(parent, up->device); 294 continue; 295 } 296 DPRINTF(("uhub_explore: status change hub=%d port=%d\n", 297 dev->address, port)); 298 usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); 299 usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); 300 /* 301 * If there is already a device on the port the change status 302 * must mean that is has disconnected. Looking at the 303 * current connect status is not enough to figure this out 304 * since a new unit may have been connected before we handle 305 * the disconnect. 306 */ 307 if (up->device) { 308 /* Disconnected */ 309 DPRINTF(("uhub_explore: device %d disappeared on port %d\n", 310 up->device->address, port)); 311 uhub_disconnect(sc, parent, up, port); 312 usbd_clear_port_feature(dev, port, 313 UHF_C_PORT_CONNECTION); 314 } 315 if (!(status & UPS_CURRENT_CONNECT_STATUS)) 316 continue; 317 318 /* Connected */ 319 /* Wait for maximum device power up time. */ 320 usbd_delay_ms(dev->bus, USB_PORT_POWERUP_DELAY); 321 /* Reset port, which implies enabling it. */ 322 if (usbd_reset_port(dev, port, &up->status) != 323 USBD_NORMAL_COMPLETION) 324 continue; 325 326 /* Wait for power to settle in device. */ 327 usbd_delay_ms(dev->bus, USB_POWER_SETTLE); 328 329 /* Get device info and set its address. */ 330 r = usbd_new_device((struct device *)sc, dev->bus, 331 dev->depth + 1, status & UPS_LOW_SPEED, 332 port, up); 333 /* XXX retry a few times? */ 334 if (r != USBD_NORMAL_COMPLETION) { 335 DPRINTFN(-1,("uhub_explore: usb_new_device failed, error=%d\n", r)); 336 /* Avoid addressing problems by disabling. */ 337 /* usbd_reset_port(dev, port, &up->status); */ 338 /* XXX 339 * What should we do. The device may or may not be at its 340 * assigned address. In any case we'd like to ignore it. 341 * Maybe the port should be disabled until the device is 342 * disconnected. 343 */ 344 if (r == USBD_SET_ADDR_FAILED || 1) {/* XXX */ 345 /* The unit refused to accept a new 346 * address, and since we cannot leave 347 * at 0 we have to disable the port 348 * instead. */ 349 printf("%s: device problem, disable port %d\n", 350 parent->dv_xname, port); 351 usbd_clear_port_feature(dev, port, 352 UHF_PORT_ENABLE); 353 } 354 } else { 355 if (up->device->hub) 356 up->device->hub->explore(parent, up->device); 357 } 358 } 359 return (USBD_NORMAL_COMPLETION); 360 } 361 362 void 363 uhub_disconnect(sc, parent, up, portno) 364 struct uhub_softc *sc; 365 struct device *parent; 366 struct usbd_port *up; 367 int portno; 368 { 369 usbd_device_handle dev = up->device; 370 usbd_pipe_handle p, n; 371 usb_hub_descriptor_t *hd; 372 struct usbd_port *spi; 373 int i, port; 374 375 DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", 376 up, dev, portno)); 377 378 printf("%s: device addr %d%s on hub addr %d, port %d disconnected\n", 379 parent->dv_xname, dev->address, dev->hub ? " (hub)" : "", 380 up->parent->address, portno); 381 382 for (i = 0; i < dev->cdesc->bNumInterface; i++) { 383 for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) { 384 n = LIST_NEXT(p, next); 385 if (p->disco) 386 p->disco(p->discoarg); 387 usbd_abort_pipe(p); 388 usbd_close_pipe(p); 389 } 390 } 391 392 /* XXX Free all data structures and disable further I/O. */ 393 394 395 if (dev->hub) { 396 DPRINTFN(3,("usb_disconnect: hub, recursing\n")); 397 hd = &dev->hub->hubdesc; 398 for(port = 1; port <= hd->bNbrPorts; port++) { 399 spi = &dev->hub->ports[port-1]; 400 if (spi->device) 401 uhub_disconnect(sc, parent, spi, port); 402 } 403 } 404 405 dev->bus->devices[dev->address] = 0; 406 up->device = 0; 407 /* XXX free */ 408 } 409 410 void 411 uhub_intr(reqh, addr, status) 412 usbd_request_handle reqh; 413 usbd_private_handle addr; 414 usbd_status status; 415 { 416 struct uhub_softc *sc = addr; 417 418 DPRINTFN(1,("uhub_intr: sc=%p\n", sc)); 419 #if 0 420 if (status != USBD_NORMAL_COMPLETION) 421 usbd_clear_endpoint_stall(sc->sc_ipipe); 422 else 423 #endif 424 usb_needs_explore(sc->sc_hub->bus); 425 } 426