1 /* $NetBSD: uhub.c,v 1.15 1999/01/10 11:13:36 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 /* FIXME what does FreeBSD need? */ 90 #if defined(__NetBSD__) 91 struct cfattach uhub_uhub_ca = { 92 sizeof(struct uhub_softc), uhub_match, uhub_attach 93 }; 94 #endif 95 96 USB_MATCH(uhub) 97 { 98 USB_MATCH_START(uhub, uaa); 99 usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); 100 101 DPRINTFN(5,("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 USB_ATTACH(uhub) 112 { 113 USB_ATTACH_START(uhub, sc, uaa); 114 usbd_device_handle dev = uaa->device; 115 char devinfo[1024]; 116 usbd_status r; 117 struct usbd_hub *hub; 118 usb_device_request_t req; 119 usb_hub_descriptor_t hubdesc; 120 int p, port, nports, nremov; 121 usbd_interface_handle iface; 122 usb_endpoint_descriptor_t *ed; 123 124 DPRINTFN(1,("uhub_attach\n")); 125 sc->sc_hub = dev; 126 usbd_devinfo(dev, 1, devinfo); 127 USB_ATTACH_SETUP; 128 printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); 129 130 r = usbd_set_config_index(dev, 0, 1); 131 if (r != USBD_NORMAL_COMPLETION) { 132 DPRINTF(("%s: configuration failed, error=%d(%s)\n", 133 USBDEVNAME(sc->sc_dev), r, usbd_error_strs[r])); 134 USB_ATTACH_ERROR_RETURN; 135 } 136 137 if (dev->depth > USB_HUB_MAX_DEPTH) { 138 printf("%s: hub depth (%d) exceeded, hub ignored\n", 139 USBDEVNAME(sc->sc_dev), USB_HUB_MAX_DEPTH); 140 USB_ATTACH_ERROR_RETURN; 141 } 142 143 /* Get hub descriptor. */ 144 req.bmRequestType = UT_READ_CLASS_DEVICE; 145 req.bRequest = UR_GET_DESCRIPTOR; 146 USETW(req.wValue, 0); 147 USETW(req.wIndex, 0); 148 USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); 149 DPRINTFN(1,("usb_init_hub: getting hub descriptor\n")); 150 r = usbd_do_request(dev, &req, &hubdesc); 151 nports = hubdesc.bNbrPorts; 152 if (r == USBD_NORMAL_COMPLETION && nports > 7) { 153 USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8); 154 r = usbd_do_request(dev, &req, &hubdesc); 155 } 156 if (r != USBD_NORMAL_COMPLETION) { 157 DPRINTF(("%s: getting hub descriptor failed, error=%d(%s)\n", 158 USBDEVNAME(sc->sc_dev), r, usbd_error_strs[r])); 159 USB_ATTACH_ERROR_RETURN; 160 } 161 162 for (nremov = 0, port = 1; port <= nports; port++) 163 if (!UHD_NOT_REMOV(&hubdesc, port)) 164 nremov++; 165 printf("%s: %d port%s with %d removable, %s powered\n", 166 USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "", 167 nremov, dev->self_powered ? "self" : "bus"); 168 169 170 hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), 171 M_USB, M_NOWAIT); 172 if (hub == 0) 173 USB_ATTACH_ERROR_RETURN; 174 dev->hub = hub; 175 dev->hub->hubsoftc = sc; 176 hub->explore = uhub_explore; 177 hub->hubdesc = hubdesc; 178 179 DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, " 180 "parent->selfpowered=%d\n", 181 dev->self_powered, dev->powersrc->parent, 182 dev->powersrc->parent ? 183 dev->powersrc->parent->self_powered : 0)); 184 if (!dev->self_powered && dev->powersrc->parent && 185 !dev->powersrc->parent->self_powered) { 186 printf("%s: bus powered hub connected to bus powered hub, " 187 "ignored\n", 188 USBDEVNAME(sc->sc_dev)); 189 USB_ATTACH_ERROR_RETURN; 190 } 191 192 /* Set up interrupt pipe. */ 193 r = usbd_device2interface_handle(dev, 0, &iface); 194 if (r != USBD_NORMAL_COMPLETION) { 195 printf("%s: no interface handle\n", USBDEVNAME(sc->sc_dev)); 196 USB_ATTACH_ERROR_RETURN; 197 } 198 ed = usbd_interface2endpoint_descriptor(iface, 0); 199 if (ed == 0) { 200 printf("%s: no endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); 201 USB_ATTACH_ERROR_RETURN; 202 } 203 if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { 204 printf("%s: bad interrupt endpoint\n", USBDEVNAME(sc->sc_dev)); 205 USB_ATTACH_ERROR_RETURN; 206 } 207 208 r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK, 209 &sc->sc_ipipe, sc, sc->sc_status, 210 sizeof(sc->sc_status), 211 uhub_intr); 212 if (r != USBD_NORMAL_COMPLETION) { 213 printf("%s: cannot open interrupt pipe\n", 214 USBDEVNAME(sc->sc_dev)); 215 USB_ATTACH_ERROR_RETURN; 216 } 217 218 /* Wait with power off for a while. */ 219 usbd_delay_ms(dev, USB_POWER_DOWN_TIME); 220 221 for (p = 0; p < nports; p++) { 222 struct usbd_port *up = &hub->ports[p]; 223 up->device = 0; 224 up->parent = dev; 225 up->portno = p+1; 226 r = uhub_init_port(up); 227 if (r != USBD_NORMAL_COMPLETION) 228 printf("%s: init of port %d failed\n", 229 USBDEVNAME(sc->sc_dev), up->portno); 230 } 231 sc->sc_running = 1; 232 233 USB_ATTACH_SUCCESS_RETURN; 234 } 235 236 #if defined(__FreeBSD__) 237 static int 238 uhub_detach(device_t self) 239 { 240 struct uhub_softc *sc = device_get_softc(self); 241 int nports = sc->sc_hub->hub->hubdesc.bNbrPorts; 242 int p; 243 244 for (p = 0; p < nports; p++) { 245 struct usbd_port *up = &sc->sc_hub->hub->ports[p]; 246 if (up->device) 247 uhub_disconnect(up); 248 } 249 250 free(sc->sc_hub->hub, M_USB); 251 252 return 0; 253 } 254 #endif 255 256 usbd_status 257 uhub_init_port(up) 258 struct usbd_port *up; 259 { 260 int port = up->portno; 261 usbd_device_handle dev = up->parent; 262 usbd_status r; 263 u_int16_t pstatus; 264 265 r = usbd_get_port_status(dev, port, &up->status); 266 if (r != USBD_NORMAL_COMPLETION) 267 return (r); 268 pstatus = UGETW(up->status.wPortStatus); 269 DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x " 270 "change=0x%04x\n", 271 port, pstatus, UGETW(up->status.wPortChange))); 272 if ((pstatus & UPS_PORT_POWER) == 0) { 273 /* Port lacks power, turn it on */ 274 275 /* First let the device go through a good power cycle, */ 276 usbd_delay_ms(dev, USB_PORT_POWER_DOWN_TIME); 277 278 /* then turn the power on. */ 279 r = usbd_set_port_feature(dev, port, UHF_PORT_POWER); 280 if (r != USBD_NORMAL_COMPLETION) 281 return (r); 282 r = usbd_get_port_status(dev, port, &up->status); 283 if (r != USBD_NORMAL_COMPLETION) 284 return (r); 285 DPRINTF(("usb_init_port: turn on port %d power status=0x%04x " 286 "change=0x%04x\n", 287 port, UGETW(up->status.wPortStatus), 288 UGETW(up->status.wPortChange))); 289 /* Wait for stable power. */ 290 usbd_delay_ms(dev, dev->hub->hubdesc.bPwrOn2PwrGood * 291 UHD_PWRON_FACTOR); 292 } 293 if (dev->self_powered) 294 /* Self powered hub, give ports maximum current. */ 295 up->power = USB_MAX_POWER; 296 else 297 up->power = USB_MIN_POWER; 298 return (USBD_NORMAL_COMPLETION); 299 } 300 301 usbd_status 302 uhub_explore(dev) 303 usbd_device_handle dev; 304 { 305 usb_hub_descriptor_t *hd = &dev->hub->hubdesc; 306 struct uhub_softc *sc = dev->hub->hubsoftc; 307 struct usbd_port *up; 308 usbd_status r; 309 int port; 310 int change, status; 311 312 DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address)); 313 314 if (!sc->sc_running) 315 return (USBD_NOT_STARTED); 316 317 /* Ignore hubs that are too deep. */ 318 if (dev->depth > USB_HUB_MAX_DEPTH) 319 return (USBD_TOO_DEEP); 320 321 for(port = 1; port <= hd->bNbrPorts; port++) { 322 up = &dev->hub->ports[port-1]; 323 r = usbd_get_port_status(dev, port, &up->status); 324 if (r != USBD_NORMAL_COMPLETION) { 325 DPRINTF(("uhub_explore: get port status failed, " 326 "error=%d(%s)\n", 327 r, usbd_error_strs[r])); 328 continue; 329 } 330 status = UGETW(up->status.wPortStatus); 331 change = UGETW(up->status.wPortChange); 332 DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n", 333 port, status, change)); 334 if (change & UPS_C_PORT_ENABLED) { 335 usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); 336 if (status & UPS_PORT_ENABLED) { 337 printf("%s: illegal enable change, port %d\n", 338 USBDEVNAME(sc->sc_dev), port); 339 } else { 340 /* Port error condition. */ 341 if (up->restartcnt++ < USBD_RESTART_MAX) { 342 printf("%s: port error, restarting " 343 "port %d\n", 344 USBDEVNAME(sc->sc_dev), port); 345 goto disco; 346 } else { 347 printf("%s: port error, giving up " 348 "port %d\n", 349 USBDEVNAME(sc->sc_dev), port); 350 } 351 } 352 } 353 if (!(change & UPS_C_CONNECT_STATUS)) { 354 /* No status change, just do recursive explore. */ 355 if (up->device && up->device->hub) 356 up->device->hub->explore(up->device); 357 continue; 358 } 359 DPRINTF(("uhub_explore: status change hub=%d port=%d\n", 360 dev->address, port)); 361 usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); 362 usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); 363 /* 364 * If there is already a device on the port the change status 365 * must mean that is has disconnected. Looking at the 366 * current connect status is not enough to figure this out 367 * since a new unit may have been connected before we handle 368 * the disconnect. 369 */ 370 disco: 371 if (up->device) { 372 /* Disconnected */ 373 DPRINTF(("uhub_explore: device %d disappeared " 374 "on port %d\n", 375 up->device->address, port)); 376 uhub_disconnect(up); 377 usbd_clear_port_feature(dev, port, 378 UHF_C_PORT_CONNECTION); 379 } 380 if (!(status & UPS_CURRENT_CONNECT_STATUS)) 381 continue; 382 383 /* Connected */ 384 up->restartcnt = 0; 385 386 /* Wait for maximum device power up time. */ 387 usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY); 388 389 /* Reset port, which implies enabling it. */ 390 if (usbd_reset_port(dev, port, &up->status) != 391 USBD_NORMAL_COMPLETION) 392 continue; 393 394 /* Get device info and set its address. */ 395 r = usbd_new_device(&sc->sc_dev, dev->bus, 396 dev->depth + 1, status & UPS_LOW_SPEED, 397 port, up); 398 /* XXX retry a few times? */ 399 if (r != USBD_NORMAL_COMPLETION) { 400 DPRINTFN(-1,("uhub_explore: usb_new_device failed, " 401 "error=%d(%s)\n", r, usbd_error_strs[r])); 402 /* Avoid addressing problems by disabling. */ 403 /* usbd_reset_port(dev, port, &up->status); */ 404 /* XXX 405 * What should we do. The device may or may not be at its 406 * assigned address. In any case we'd like to ignore it. 407 * Maybe the port should be disabled until the device is 408 * disconnected. 409 */ 410 if (r == USBD_SET_ADDR_FAILED || 1) {/* XXX */ 411 /* The unit refused to accept a new 412 * address, and since we cannot leave 413 * at 0 we have to disable the port 414 * instead. */ 415 printf("%s: device problem, disabling " 416 "port %d\n", 417 USBDEVNAME(sc->sc_dev), port); 418 usbd_clear_port_feature(dev, port, 419 UHF_PORT_ENABLE); 420 /* Make sure we don't try to restart it. */ 421 up->restartcnt = USBD_RESTART_MAX; 422 } 423 } else { 424 if (up->device->hub) 425 up->device->hub->explore(up->device); 426 } 427 } 428 return (USBD_NORMAL_COMPLETION); 429 } 430 431 void 432 uhub_disconnect(up) 433 struct usbd_port *up; 434 { 435 usbd_device_handle dev = up->device; 436 usbd_pipe_handle p, n; 437 int i; 438 struct softc { bdevice sc_dev; }; /* all softc begin like this */ 439 440 DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", 441 up, dev, up->portno)); 442 443 printf("%s: at %s port %d (addr %d) disconnected\n", 444 USBDEVNAME(((struct softc *)dev->softc)->sc_dev), 445 USBDEVNAME(((struct uhub_softc *)up->parent->softc)->sc_dev), 446 up->portno, dev->address); 447 448 if (!dev->cdesc) { 449 /* Partially attached device, just drop it. */ 450 dev->bus->devices[dev->address] = 0; 451 up->device = 0; 452 return; 453 } 454 455 for (i = 0; i < dev->cdesc->bNumInterface; i++) { 456 for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) { 457 n = LIST_NEXT(p, next); 458 if (p->disco) 459 p->disco(p->discoarg); 460 usbd_abort_pipe(p); 461 usbd_close_pipe(p); 462 } 463 } 464 465 /* XXX Free all data structures and disable further I/O. */ 466 if (dev->hub) { 467 struct usbd_port *rup; 468 int p, nports; 469 470 DPRINTFN(3,("usb_disconnect: hub, recursing\n")); 471 nports = dev->hub->hubdesc.bNbrPorts; 472 for(p = 0; p < nports; p++) { 473 rup = &dev->hub->ports[p]; 474 if (rup->device) 475 uhub_disconnect(rup); 476 } 477 } 478 479 dev->bus->devices[dev->address] = 0; 480 up->device = 0; 481 /* XXX free */ 482 #if defined(__FreeBSD__) 483 device_delete_child( 484 device_get_parent(((struct softc *)dev->softc)->sc_dev), 485 ((struct softc *)dev->softc)->sc_dev); 486 #endif 487 } 488 489 void 490 uhub_intr(reqh, addr, status) 491 usbd_request_handle reqh; 492 usbd_private_handle addr; 493 usbd_status status; 494 { 495 struct uhub_softc *sc = addr; 496 497 DPRINTFN(5,("uhub_intr: sc=%p\n", sc)); 498 if (status != USBD_NORMAL_COMPLETION) 499 usbd_clear_endpoint_stall_async(sc->sc_ipipe); 500 else 501 usb_needs_explore(sc->sc_hub->bus); 502 } 503 504 #if defined(__FreeBSD__) 505 DRIVER_MODULE(uhub, usb, uhub_driver, uhub_devclass, usbd_driver_load, 0); 506 #endif 507