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