1 /* $FreeBSD$ */ 2 /*- 3 * SPDX-License-Identifier: BSD-2-Clause-NetBSD 4 * 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. 6 * Copyright (c) 1998 Lennart Augustsson. All rights reserved. 7 * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved. 8 * Copyright (c) 2019 Takanori Watanabe. 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * USB spec: http://www.usb.org/developers/docs/usbspec.zip 34 */ 35 36 #ifdef USB_GLOBAL_INCLUDE_FILE 37 #include USB_GLOBAL_INCLUDE_FILE 38 #else 39 #include <sys/stdint.h> 40 #include <sys/stddef.h> 41 #include <sys/param.h> 42 #include <sys/queue.h> 43 #include <sys/types.h> 44 #include <sys/systm.h> 45 #include <sys/kernel.h> 46 #include <sys/bus.h> 47 #include <sys/module.h> 48 #include <sys/lock.h> 49 #include <sys/mutex.h> 50 #include <sys/condvar.h> 51 #include <sys/sysctl.h> 52 #include <sys/sx.h> 53 #include <sys/unistd.h> 54 #include <sys/callout.h> 55 #include <sys/malloc.h> 56 #include <sys/priv.h> 57 58 #include <dev/usb/usb.h> 59 #include <dev/usb/usbdi.h> 60 #include <dev/usb/usbdi_util.h> 61 62 #define USB_DEBUG_VAR uhub_debug 63 64 #include <dev/usb/usb_core.h> 65 #include <dev/usb/usb_process.h> 66 #include <dev/usb/usb_device.h> 67 #include <dev/usb/usb_request.h> 68 #include <dev/usb/usb_debug.h> 69 #include <dev/usb/usb_hub.h> 70 #include <dev/usb/usb_util.h> 71 #include <dev/usb/usb_busdma.h> 72 #include <dev/usb/usb_transfer.h> 73 #include <dev/usb/usb_dynamic.h> 74 75 #include <dev/usb/usb_controller.h> 76 #include <dev/usb/usb_bus.h> 77 #endif /* USB_GLOBAL_INCLUDE_FILE */ 78 #include <dev/usb/usb_hub_private.h> 79 #include <contrib/dev/acpica/include/acpi.h> 80 #include <contrib/dev/acpica/include/accommon.h> 81 #include <dev/acpica/acpivar.h> 82 83 static UINT32 acpi_uhub_find_rh_cb(ACPI_HANDLE ah, UINT32 nl, void *ctx, void **status); 84 static ACPI_STATUS acpi_uhub_find_rh(device_t dev, ACPI_HANDLE * ah); 85 static ACPI_STATUS 86 acpi_usb_hub_port_probe_cb(ACPI_HANDLE ah, UINT32 lv, void *ctx, void **rv); 87 static ACPI_STATUS acpi_usb_hub_port_probe(device_t dev, ACPI_HANDLE ah); 88 static int acpi_uhub_root_probe(device_t dev); 89 static int acpi_uhub_probe(device_t dev); 90 static int acpi_uhub_root_attach(device_t dev); 91 static int acpi_uhub_attach(device_t dev); 92 static int acpi_uhub_detach(device_t dev); 93 static int 94 acpi_uhub_read_ivar(device_t dev, device_t child, int idx, 95 uintptr_t *res); 96 static int 97 acpi_uhub_child_location_string(device_t parent, device_t child, 98 char *buf, size_t buflen); 99 static int acpi_uhub_parse_upc(device_t dev, unsigned int port, ACPI_HANDLE ah); 100 101 struct acpi_uhub_softc { 102 struct uhub_softc usc; 103 uint8_t nports; 104 ACPI_HANDLE *porthandle; 105 }; 106 107 UINT32 108 acpi_uhub_find_rh_cb(ACPI_HANDLE ah, UINT32 nl, void *ctx, void **status){ 109 ACPI_DEVICE_INFO *devinfo; 110 UINT32 ret = AE_OK; 111 112 *status = NULL; 113 devinfo = NULL; 114 115 ret = AcpiGetObjectInfo(ah, &devinfo); 116 117 if (ACPI_FAILURE(ret)) { 118 return ret; 119 } 120 if ((devinfo->Valid & ACPI_VALID_ADR) && 121 (devinfo->Address == 0)) { 122 ret = AE_CTRL_TERMINATE; 123 *status = ah; 124 } 125 AcpiOsFree(devinfo); 126 127 return ret; 128 } 129 130 static int 131 acpi_uhub_parse_upc(device_t dev, unsigned int port, ACPI_HANDLE ah) 132 { 133 ACPI_BUFFER buf; 134 135 buf.Pointer = NULL; 136 buf.Length = ACPI_ALLOCATE_BUFFER; 137 if (AcpiEvaluateObject(ah, "_UPC", NULL, &buf) == AE_OK) { 138 UINT64 porttypenum, conn; 139 const char *connectable; 140 const char *typelist[] = {"TypeA", "MiniAB", "Express", 141 "USB3-A", "USB3-B", "USB-MicroB", 142 "USB3-MicroAB", "USB3-PowerB", 143 "TypeC-USB2", "TypeC-Switch", 144 "TypeC-nonSwitch"}; 145 const char *porttype; 146 const int last = sizeof(typelist) / sizeof(typelist[0]); 147 ACPI_OBJECT *obj = buf.Pointer; 148 149 acpi_PkgInt(obj, 0, &conn); 150 acpi_PkgInt(obj, 1, &porttypenum); 151 connectable = conn ? "" : "non"; 152 if (porttypenum == 0xff) 153 porttype = "Proprietary"; 154 else if (porttypenum < last) { 155 porttype = typelist[porttypenum]; 156 } else { 157 porttype = "Unknown"; 158 } 159 if (usb_debug) 160 device_printf(dev, "Port %u %sconnectable %s\n", 161 port, connectable, porttype); 162 } 163 AcpiOsFree(buf.Pointer); 164 165 return 0; 166 } 167 168 static int 169 acpi_uhub_parse_pld(device_t dev, unsigned int port, ACPI_HANDLE ah) 170 { 171 ACPI_BUFFER buf; 172 173 buf.Pointer = NULL; 174 buf.Length = ACPI_ALLOCATE_BUFFER; 175 if (AcpiEvaluateObject(ah, "_PLD", NULL, &buf) == AE_OK) { 176 ACPI_OBJECT *obj; 177 unsigned char *resbuf; 178 int len; 179 180 obj = buf.Pointer; 181 182 if (obj->Type == ACPI_TYPE_PACKAGE 183 && obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) { 184 ACPI_OBJECT *obj1; 185 186 obj1 = &obj->Package.Elements[0]; 187 len = obj1->Buffer.Length; 188 resbuf = obj1->Buffer.Pointer; 189 } else if (obj->Type == ACPI_TYPE_BUFFER) { 190 len = obj->Buffer.Length; 191 resbuf = obj->Buffer.Pointer; 192 } else { 193 goto skip; 194 } 195 if (usb_debug) { 196 device_printf(dev, "Revision:%d\n", 197 resbuf[0] & 0x7f); 198 if ((resbuf[0] & 0x80) == 0) { 199 device_printf(dev, 200 "Color:#%02x%02x%02x\n", 201 resbuf[1], resbuf[2], 202 resbuf[3]); 203 } 204 device_printf(dev, "Width %d mm Height %d mm\n", 205 resbuf[4] | (resbuf[5] << 8), 206 resbuf[6] | (resbuf[7] << 8)); 207 if (resbuf[8] & 1) { 208 device_printf(dev, "Visible\n"); 209 } 210 if (resbuf[8] & 2) { 211 device_printf(dev, "Dock\n"); 212 } 213 if (resbuf[8] & 4) { 214 device_printf(dev, "Lid\n"); 215 } 216 device_printf(dev, "PanelPosition: %d\n", 217 (resbuf[8] >> 3) & 7); 218 device_printf(dev, "VertPosition: %d\n", 219 (resbuf[8] >> 6) & 3); 220 device_printf(dev, "HorizPosition: %d\n", 221 (resbuf[9]) & 3); 222 device_printf(dev, "Shape: %d\n", 223 (resbuf[9] >> 2) & 0xf); 224 device_printf(dev, "80: %02x, %02x, %02x\n", 225 resbuf[9], resbuf[10], resbuf[11]); 226 device_printf(dev, "96: %02x, %02x, %02x, %02x\n", 227 resbuf[12], resbuf[13], 228 resbuf[14], resbuf[15]); 229 230 if ((resbuf[0] & 0x7f) >= 2) { 231 device_printf(dev, "VOFF%d mm HOFF %dmm", 232 resbuf[16] | (resbuf[17] << 8), 233 resbuf[18] | (resbuf[19] << 8)); 234 } 235 } 236 skip: 237 AcpiOsFree(buf.Pointer); 238 239 } 240 241 242 return 0; 243 } 244 245 ACPI_STATUS 246 acpi_uhub_find_rh(device_t dev, ACPI_HANDLE * ah) 247 { 248 device_t grand; 249 ACPI_HANDLE gah; 250 251 *ah = NULL; 252 grand = device_get_parent(device_get_parent(dev)); 253 if ((gah = acpi_get_handle(grand)) == NULL) { 254 return AE_ERROR; 255 } 256 return AcpiWalkNamespace(ACPI_TYPE_DEVICE, gah, 1, 257 acpi_uhub_find_rh_cb, NULL, dev, ah); 258 } 259 260 ACPI_STATUS 261 acpi_usb_hub_port_probe_cb(ACPI_HANDLE ah, UINT32 lv, void *ctx, void **rv) 262 { 263 ACPI_DEVICE_INFO *devinfo; 264 device_t dev = ctx; 265 struct acpi_uhub_softc *sc = device_get_softc(dev); 266 267 if (usb_debug) 268 device_printf(dev, "%s\n", acpi_name(ah)); 269 270 AcpiGetObjectInfo(ah, &devinfo); 271 if ((devinfo->Valid & ACPI_VALID_ADR) && 272 (devinfo->Address > 0) && 273 (devinfo->Address <= (uint64_t)sc->nports)) { 274 sc->porthandle[devinfo->Address - 1] = ah; 275 acpi_uhub_parse_upc(dev, devinfo->Address, ah); 276 acpi_uhub_parse_pld(dev, devinfo->Address, ah); 277 } else { 278 device_printf(dev, "Skiping invalid devobj %s\n", 279 acpi_name(ah)); 280 } 281 AcpiOsFree(devinfo); 282 return AE_OK; 283 } 284 285 ACPI_STATUS 286 acpi_usb_hub_port_probe(device_t dev, ACPI_HANDLE ah) 287 { 288 return AcpiWalkNamespace(ACPI_TYPE_DEVICE, 289 ah, 1, 290 acpi_usb_hub_port_probe_cb, 291 NULL, dev, NULL); 292 } 293 int 294 acpi_uhub_root_probe(device_t dev) 295 { 296 ACPI_HANDLE ah; 297 ACPI_STATUS status; 298 299 if(acpi_disabled("usb")) { 300 return ENXIO; 301 } 302 status = acpi_uhub_find_rh(dev, &ah); 303 if (ACPI_SUCCESS(status) 304 && ah != NULL 305 && (uhub_probe(dev) <= 0)) { 306 /* success prior than non - acpi hub */ 307 return (BUS_PROBE_DEFAULT + 1); 308 } 309 return ENXIO; 310 } 311 312 int 313 acpi_uhub_probe(device_t dev) 314 { 315 ACPI_HANDLE ah = acpi_get_handle(dev); 316 317 if (!acpi_disabled("usb") && ah && (uhub_probe(dev) <= 0)) { 318 /*success prior than non - acpi hub*/ 319 return (BUS_PROBE_DEFAULT + 1); 320 } 321 return (ENXIO); 322 } 323 int 324 acpi_uhub_root_attach(device_t dev) 325 { 326 ACPI_HANDLE devhandle; 327 struct usb_hub *uh; 328 struct acpi_uhub_softc *sc = device_get_softc(dev); 329 int ret; 330 331 if ((ret = uhub_attach(dev)) != 0) { 332 return (ret); 333 } 334 uh = sc->usc.sc_udev->hub; 335 336 if (ACPI_FAILURE(acpi_uhub_find_rh(dev, &devhandle)) || 337 (devhandle == NULL)) { 338 return ENXIO; 339 } 340 341 sc->nports = uh->nports; 342 sc->porthandle = malloc(sizeof(ACPI_HANDLE) * uh->nports, 343 M_USBDEV, M_WAITOK | M_ZERO); 344 acpi_usb_hub_port_probe(dev, devhandle); 345 346 return 0; 347 } 348 349 int 350 acpi_uhub_attach(device_t dev) 351 { 352 struct usb_hub *uh; 353 struct acpi_uhub_softc *sc = device_get_softc(dev); 354 ACPI_HANDLE devhandle; 355 int ret; 356 357 if ((ret = uhub_attach(dev)) != 0) { 358 return (ret); 359 } 360 uh = sc->usc.sc_udev->hub; 361 devhandle = acpi_get_handle(dev); 362 363 if (devhandle == NULL) { 364 return ENXIO; 365 } 366 367 sc->nports = uh->nports; 368 sc->porthandle = malloc(sizeof(ACPI_HANDLE) * uh->nports, 369 M_USBDEV, M_WAITOK | M_ZERO); 370 acpi_usb_hub_port_probe(dev, acpi_get_handle(dev)); 371 return 0; 372 } 373 374 int 375 acpi_uhub_read_ivar(device_t dev, device_t child, int idx, 376 uintptr_t *res) 377 { 378 struct hub_result hres; 379 struct acpi_uhub_softc *sc = device_get_softc(dev); 380 ACPI_HANDLE ah; 381 382 mtx_lock(&Giant); 383 uhub_find_iface_index(sc->usc.sc_udev->hub, child, &hres); 384 mtx_unlock(&Giant); 385 if ((idx == ACPI_IVAR_HANDLE) && 386 (hres.portno > 0) && 387 (hres.portno <= sc->nports) && 388 (ah = sc->porthandle[hres.portno - 1])) { 389 *res = (uintptr_t)ah; 390 return (0); 391 } 392 return (ENXIO); 393 } 394 static int 395 acpi_uhub_child_location_string(device_t parent, device_t child, 396 char *buf, size_t buflen) 397 { 398 399 ACPI_HANDLE ah; 400 401 uhub_child_location_string(parent, child, buf, buflen); 402 ah = acpi_get_handle(child); 403 if (ah) { 404 strlcat(buf, " handle=", buflen); 405 strlcat(buf, acpi_name(ah), buflen); 406 } 407 return (0); 408 } 409 410 int 411 acpi_uhub_detach(device_t dev) 412 { 413 struct acpi_uhub_softc *sc = device_get_softc(dev); 414 415 free(sc->porthandle, M_USBDEV); 416 return uhub_detach(dev); 417 } 418 419 static device_method_t acpi_uhub_methods[] = { 420 DEVMETHOD(device_probe, acpi_uhub_probe), 421 DEVMETHOD(device_attach, acpi_uhub_attach), 422 DEVMETHOD(device_detach, acpi_uhub_detach), 423 DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string), 424 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar), 425 DEVMETHOD_END 426 427 }; 428 429 static device_method_t acpi_uhub_root_methods[] = { 430 DEVMETHOD(device_probe, acpi_uhub_root_probe), 431 DEVMETHOD(device_attach, acpi_uhub_root_attach), 432 DEVMETHOD(device_detach, acpi_uhub_detach), 433 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar), 434 DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string), 435 DEVMETHOD_END 436 }; 437 438 static devclass_t uhub_devclass; 439 extern driver_t uhub_driver; 440 static kobj_class_t uhub_baseclasses[] = {&uhub_driver, NULL}; 441 static driver_t acpi_uhub_driver = { 442 .name = "uhub", 443 .methods = acpi_uhub_methods, 444 .size = sizeof(struct acpi_uhub_softc), 445 .baseclasses = uhub_baseclasses, 446 }; 447 static driver_t acpi_uhub_root_driver = { 448 .name = "uhub", 449 .methods = acpi_uhub_root_methods, 450 .size = sizeof(struct acpi_uhub_softc), 451 .baseclasses = uhub_baseclasses, 452 }; 453 454 DRIVER_MODULE(acpi_uhub, uhub, acpi_uhub_driver, uhub_devclass, 0, 0); 455 MODULE_DEPEND(acpi_uhub, acpi, 1, 1, 1); 456 DRIVER_MODULE(acpi_uhub, usbus, acpi_uhub_root_driver, uhub_devclass, 0, 0); 457