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 struct acpi_uhub_softc { 84 struct uhub_softc usc; 85 uint8_t nports; 86 ACPI_HANDLE *porthandle; 87 }; 88 89 static UINT32 90 acpi_uhub_find_rh_cb(ACPI_HANDLE ah, UINT32 nl, void *ctx, void **status) 91 { 92 ACPI_DEVICE_INFO *devinfo; 93 UINT32 ret; 94 95 *status = NULL; 96 devinfo = NULL; 97 98 ret = AcpiGetObjectInfo(ah, &devinfo); 99 if (ACPI_SUCCESS(ret)) { 100 if ((devinfo->Valid & ACPI_VALID_ADR) && 101 (devinfo->Address == 0)) { 102 ret = AE_CTRL_TERMINATE; 103 *status = ah; 104 } 105 AcpiOsFree(devinfo); 106 } 107 return (ret); 108 } 109 110 static int 111 acpi_uhub_parse_upc(device_t dev, unsigned int port, ACPI_HANDLE ah) 112 { 113 ACPI_BUFFER buf; 114 115 buf.Pointer = NULL; 116 buf.Length = ACPI_ALLOCATE_BUFFER; 117 118 if (AcpiEvaluateObject(ah, "_UPC", NULL, &buf) == AE_OK) { 119 UINT64 porttypenum, conn; 120 const char *connectable; 121 const char *typelist[] = { 122 "TypeA", "MiniAB", "Express", 123 "USB3-A", "USB3-B", "USB-MicroB", 124 "USB3-MicroAB", "USB3-PowerB", 125 "TypeC-USB2", "TypeC-Switch", 126 "TypeC-nonSwitch" 127 }; 128 const char *porttype; 129 const int last = sizeof(typelist) / sizeof(typelist[0]); 130 ACPI_OBJECT *obj = buf.Pointer; 131 132 acpi_PkgInt(obj, 0, &conn); 133 acpi_PkgInt(obj, 1, &porttypenum); 134 connectable = conn ? "" : "non"; 135 if (porttypenum == 0xff) 136 porttype = "Proprietary"; 137 else if (porttypenum < last) { 138 porttype = typelist[porttypenum]; 139 } else { 140 porttype = "Unknown"; 141 } 142 if (usb_debug) 143 device_printf(dev, "Port %u %sconnectable %s\n", 144 port, connectable, porttype); 145 } 146 AcpiOsFree(buf.Pointer); 147 148 return (0); 149 } 150 151 static int 152 acpi_uhub_parse_pld(device_t dev, unsigned int port, ACPI_HANDLE ah) 153 { 154 ACPI_BUFFER buf; 155 156 buf.Pointer = NULL; 157 buf.Length = ACPI_ALLOCATE_BUFFER; 158 159 if (AcpiEvaluateObject(ah, "_PLD", NULL, &buf) == AE_OK) { 160 ACPI_OBJECT *obj; 161 unsigned char *resbuf; 162 int len; 163 164 obj = buf.Pointer; 165 166 if (obj->Type == ACPI_TYPE_PACKAGE 167 && obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) { 168 ACPI_OBJECT *obj1; 169 170 obj1 = &obj->Package.Elements[0]; 171 len = obj1->Buffer.Length; 172 resbuf = obj1->Buffer.Pointer; 173 } else if (obj->Type == ACPI_TYPE_BUFFER) { 174 len = obj->Buffer.Length; 175 resbuf = obj->Buffer.Pointer; 176 } else { 177 goto skip; 178 } 179 if (usb_debug) { 180 device_printf(dev, "Revision:%d\n", 181 resbuf[0] & 0x7f); 182 if ((resbuf[0] & 0x80) == 0) { 183 device_printf(dev, 184 "Color:#%02x%02x%02x\n", 185 resbuf[1], resbuf[2], 186 resbuf[3]); 187 } 188 device_printf(dev, "Width %d mm Height %d mm\n", 189 resbuf[4] | (resbuf[5] << 8), 190 resbuf[6] | (resbuf[7] << 8)); 191 if (resbuf[8] & 1) { 192 device_printf(dev, "Visible\n"); 193 } 194 if (resbuf[8] & 2) { 195 device_printf(dev, "Dock\n"); 196 } 197 if (resbuf[8] & 4) { 198 device_printf(dev, "Lid\n"); 199 } 200 device_printf(dev, "PanelPosition: %d\n", 201 (resbuf[8] >> 3) & 7); 202 device_printf(dev, "VertPosition: %d\n", 203 (resbuf[8] >> 6) & 3); 204 device_printf(dev, "HorizPosition: %d\n", 205 (resbuf[9]) & 3); 206 device_printf(dev, "Shape: %d\n", 207 (resbuf[9] >> 2) & 0xf); 208 device_printf(dev, "80: %02x, %02x, %02x\n", 209 resbuf[9], resbuf[10], resbuf[11]); 210 device_printf(dev, "96: %02x, %02x, %02x, %02x\n", 211 resbuf[12], resbuf[13], 212 resbuf[14], resbuf[15]); 213 214 if ((resbuf[0] & 0x7f) >= 2) { 215 device_printf(dev, "VOFF%d mm HOFF %dmm", 216 resbuf[16] | (resbuf[17] << 8), 217 resbuf[18] | (resbuf[19] << 8)); 218 } 219 } 220 skip: 221 AcpiOsFree(buf.Pointer); 222 } 223 return (0); 224 } 225 226 static ACPI_STATUS 227 acpi_uhub_find_rh(device_t dev, ACPI_HANDLE *ah) 228 { 229 device_t grand; 230 ACPI_HANDLE gah; 231 232 *ah = NULL; 233 grand = device_get_parent(device_get_parent(dev)); 234 235 if ((gah = acpi_get_handle(grand)) == NULL) 236 return (AE_ERROR); 237 238 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, gah, 1, 239 acpi_uhub_find_rh_cb, NULL, dev, ah)); 240 } 241 242 static ACPI_STATUS 243 acpi_usb_hub_port_probe_cb(ACPI_HANDLE ah, UINT32 lv, void *ctx, void **rv) 244 { 245 ACPI_DEVICE_INFO *devinfo; 246 device_t dev = ctx; 247 struct acpi_uhub_softc *sc = device_get_softc(dev); 248 UINT32 ret; 249 250 ret = AcpiGetObjectInfo(ah, &devinfo); 251 if (ACPI_SUCCESS(ret)) { 252 if ((devinfo->Valid & ACPI_VALID_ADR) && 253 (devinfo->Address > 0) && 254 (devinfo->Address <= (uint64_t)sc->nports)) { 255 sc->porthandle[devinfo->Address - 1] = ah; 256 acpi_uhub_parse_upc(dev, devinfo->Address, ah); 257 acpi_uhub_parse_pld(dev, devinfo->Address, ah); 258 } 259 AcpiOsFree(devinfo); 260 } 261 return (AE_OK); 262 } 263 264 static ACPI_STATUS 265 acpi_usb_hub_port_probe(device_t dev, ACPI_HANDLE ah) 266 { 267 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, 268 ah, 1, 269 acpi_usb_hub_port_probe_cb, 270 NULL, dev, NULL)); 271 } 272 273 static int 274 acpi_uhub_root_probe(device_t dev) 275 { 276 ACPI_STATUS status; 277 ACPI_HANDLE ah; 278 279 if (acpi_disabled("usb")) 280 return (ENXIO); 281 282 status = acpi_uhub_find_rh(dev, &ah); 283 if (ACPI_SUCCESS(status) && ah != NULL && 284 uhub_probe(dev) <= 0) { 285 /* success prior than non-ACPI USB HUB */ 286 return (BUS_PROBE_DEFAULT + 1); 287 } 288 return (ENXIO); 289 } 290 291 static int 292 acpi_uhub_probe(device_t dev) 293 { 294 ACPI_HANDLE ah; 295 296 if (acpi_disabled("usb")) 297 return (ENXIO); 298 299 ah = acpi_get_handle(dev); 300 if (ah == NULL) 301 return (ENXIO); 302 303 if (uhub_probe(dev) <= 0) { 304 /* success prior than non-ACPI USB HUB */ 305 return (BUS_PROBE_DEFAULT + 1); 306 } 307 return (ENXIO); 308 } 309 310 static int 311 acpi_uhub_root_attach(device_t dev) 312 { 313 struct acpi_uhub_softc *sc = device_get_softc(dev); 314 ACPI_STATUS status; 315 ACPI_HANDLE ah; 316 int ret; 317 318 ret = uhub_attach(dev); 319 if (ret != 0) 320 goto done; 321 322 status = acpi_uhub_find_rh(dev, &ah); 323 if (ACPI_SUCCESS(status) && ah != NULL) { 324 struct usb_hub *uh = sc->usc.sc_udev->hub; 325 326 sc->nports = uh->nports; 327 sc->porthandle = malloc(sizeof(ACPI_HANDLE) * uh->nports, 328 M_USBDEV, M_WAITOK | M_ZERO); 329 acpi_usb_hub_port_probe(dev, ah); 330 } 331 done: 332 return (ret); 333 } 334 335 static int 336 acpi_uhub_attach(device_t dev) 337 { 338 struct acpi_uhub_softc *sc = device_get_softc(dev); 339 ACPI_HANDLE ah; 340 int ret; 341 342 ret = uhub_attach(dev); 343 if (ret != 0) 344 goto done; 345 346 ah = acpi_get_handle(dev); 347 if (ah != NULL) { 348 struct usb_hub *uh = sc->usc.sc_udev->hub; 349 350 sc->nports = uh->nports; 351 sc->porthandle = malloc(sizeof(ACPI_HANDLE) * uh->nports, 352 M_USBDEV, M_WAITOK | M_ZERO); 353 acpi_usb_hub_port_probe(dev, ah); 354 } 355 done: 356 return (ret); 357 } 358 359 static int 360 acpi_uhub_read_ivar(device_t dev, device_t child, int idx, uintptr_t *res) 361 { 362 struct hub_result hres; 363 struct acpi_uhub_softc *sc = device_get_softc(dev); 364 ACPI_HANDLE ah; 365 366 mtx_lock(&Giant); 367 uhub_find_iface_index(sc->usc.sc_udev->hub, child, &hres); 368 mtx_unlock(&Giant); 369 370 if ((idx == ACPI_IVAR_HANDLE) && 371 (hres.portno > 0) && 372 (hres.portno <= sc->nports) && 373 (ah = sc->porthandle[hres.portno - 1])) { 374 *res = (uintptr_t)ah; 375 return (0); 376 } 377 return (ENXIO); 378 } 379 380 static int 381 acpi_uhub_child_location_string(device_t parent, device_t child, 382 char *buf, size_t buflen) 383 { 384 ACPI_HANDLE ah; 385 386 uhub_child_location_string(parent, child, buf, buflen); 387 388 ah = acpi_get_handle(child); 389 if (ah != NULL) { 390 strlcat(buf, " handle=", buflen); 391 strlcat(buf, acpi_name(ah), buflen); 392 } 393 return (0); 394 } 395 396 static int 397 acpi_uhub_detach(device_t dev) 398 { 399 struct acpi_uhub_softc *sc = device_get_softc(dev); 400 401 free(sc->porthandle, M_USBDEV); 402 403 return (uhub_detach(dev)); 404 } 405 406 static device_method_t acpi_uhub_methods[] = { 407 DEVMETHOD(device_probe, acpi_uhub_probe), 408 DEVMETHOD(device_attach, acpi_uhub_attach), 409 DEVMETHOD(device_detach, acpi_uhub_detach), 410 DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string), 411 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar), 412 DEVMETHOD_END 413 414 }; 415 416 static device_method_t acpi_uhub_root_methods[] = { 417 DEVMETHOD(device_probe, acpi_uhub_root_probe), 418 DEVMETHOD(device_attach, acpi_uhub_root_attach), 419 DEVMETHOD(device_detach, acpi_uhub_detach), 420 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar), 421 DEVMETHOD(bus_child_location_str, acpi_uhub_child_location_string), 422 DEVMETHOD_END 423 }; 424 425 static devclass_t uhub_devclass; 426 extern driver_t uhub_driver; 427 static kobj_class_t uhub_baseclasses[] = {&uhub_driver, NULL}; 428 429 static driver_t acpi_uhub_driver = { 430 .name = "uhub", 431 .methods = acpi_uhub_methods, 432 .size = sizeof(struct acpi_uhub_softc), 433 .baseclasses = uhub_baseclasses, 434 }; 435 436 static driver_t acpi_uhub_root_driver = { 437 .name = "uhub", 438 .methods = acpi_uhub_root_methods, 439 .size = sizeof(struct acpi_uhub_softc), 440 .baseclasses = uhub_baseclasses, 441 }; 442 443 DRIVER_MODULE(uacpi, uhub, acpi_uhub_driver, uhub_devclass, 0, 0); 444 DRIVER_MODULE(uacpi, usbus, acpi_uhub_root_driver, uhub_devclass, 0, 0); 445 446 MODULE_DEPEND(uacpi, acpi, 1, 1, 1); 447 MODULE_DEPEND(uacpi, usb, 1, 1, 1); 448 449 MODULE_VERSION(uacpi, 1); 450