1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. 5 * Copyright (c) 1998 Lennart Augustsson. All rights reserved. 6 * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved. 7 * Copyright (c) 2019 Takanori Watanabe. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * USB spec: http://www.usb.org/developers/docs/usbspec.zip 33 */ 34 35 #ifdef USB_GLOBAL_INCLUDE_FILE 36 #include USB_GLOBAL_INCLUDE_FILE 37 #else 38 #include <sys/stdint.h> 39 #include <sys/stddef.h> 40 #include <sys/param.h> 41 #include <sys/queue.h> 42 #include <sys/types.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/bus.h> 46 #include <sys/module.h> 47 #include <sys/lock.h> 48 #include <sys/mutex.h> 49 #include <sys/condvar.h> 50 #include <sys/sysctl.h> 51 #include <sys/sx.h> 52 #include <sys/unistd.h> 53 #include <sys/callout.h> 54 #include <sys/malloc.h> 55 #include <sys/priv.h> 56 57 #include <dev/usb/usb.h> 58 #include <dev/usb/usbdi.h> 59 #include <dev/usb/usbdi_util.h> 60 61 #define USB_DEBUG_VAR uhub_debug 62 63 #include <dev/usb/usb_core.h> 64 #include <dev/usb/usb_process.h> 65 #include <dev/usb/usb_device.h> 66 #include <dev/usb/usb_request.h> 67 #include <dev/usb/usb_debug.h> 68 #include <dev/usb/usb_hub.h> 69 #include <dev/usb/usb_util.h> 70 #include <dev/usb/usb_busdma.h> 71 #include <dev/usb/usb_transfer.h> 72 #include <dev/usb/usb_dynamic.h> 73 74 #include <dev/usb/usb_controller.h> 75 #include <dev/usb/usb_bus.h> 76 #endif /* USB_GLOBAL_INCLUDE_FILE */ 77 #include <dev/usb/usb_hub_private.h> 78 #include <contrib/dev/acpica/include/acpi.h> 79 #include <contrib/dev/acpica/include/accommon.h> 80 #include <dev/acpica/acpivar.h> 81 #include <sys/sbuf.h> 82 83 #define ACPI_PLD_SIZE 20 84 struct acpi_uhub_port { 85 ACPI_HANDLE handle; 86 #define ACPI_UPC_CONNECTABLE 0x80000000 87 #define ACPI_UPC_PORTTYPE(x) ((x)&0xff) 88 uint32_t upc; 89 uint8_t pld[ACPI_PLD_SIZE]; 90 }; 91 92 struct acpi_uhub_softc { 93 struct uhub_softc usc; 94 uint8_t nports; 95 ACPI_HANDLE ah; 96 struct acpi_uhub_port *port; 97 }; 98 99 static UINT32 100 acpi_uhub_find_rh_cb(ACPI_HANDLE ah, UINT32 nl, void *ctx, void **status) 101 { 102 ACPI_DEVICE_INFO *devinfo; 103 UINT32 ret; 104 105 *status = NULL; 106 devinfo = NULL; 107 108 ret = AcpiGetObjectInfo(ah, &devinfo); 109 if (ACPI_SUCCESS(ret)) { 110 if ((devinfo->Valid & ACPI_VALID_ADR) && 111 (devinfo->Address == 0)) { 112 ret = AE_CTRL_TERMINATE; 113 *status = ah; 114 } 115 AcpiOsFree(devinfo); 116 } 117 return (ret); 118 } 119 120 static const char * 121 acpi_uhub_upc_type(uint8_t type) 122 { 123 const char *typelist[] = {"TypeA", "MiniAB", "Express", 124 "USB3-A", "USB3-B", "USB-MicroB", 125 "USB3-MicroAB", "USB3-PowerB", 126 "TypeC-USB2", "TypeC-Switch", 127 "TypeC-nonSwitch"}; 128 const int last = sizeof(typelist) / sizeof(typelist[0]); 129 130 if (type == 0xff) { 131 return "Proprietary"; 132 } 133 134 return (type < last) ? typelist[type] : "Unknown"; 135 } 136 137 static int 138 acpi_uhub_parse_upc(device_t dev, unsigned p, ACPI_HANDLE ah, struct sysctl_oid_list *poid) 139 { 140 ACPI_BUFFER buf; 141 struct acpi_uhub_softc *sc = device_get_softc(dev); 142 struct acpi_uhub_port *port = &sc->port[p - 1]; 143 144 buf.Pointer = NULL; 145 buf.Length = ACPI_ALLOCATE_BUFFER; 146 147 if (AcpiEvaluateObject(ah, "_UPC", NULL, &buf) == AE_OK) { 148 ACPI_OBJECT *obj = buf.Pointer; 149 UINT64 porttypenum, conn; 150 uint8_t *connectable; 151 152 acpi_PkgInt(obj, 0, &conn); 153 acpi_PkgInt(obj, 1, &porttypenum); 154 connectable = conn ? "" : "non"; 155 156 port->upc = porttypenum; 157 port->upc |= (conn) ? (ACPI_UPC_CONNECTABLE) : 0; 158 159 if (usb_debug) 160 device_printf(dev, "Port %u %sconnectable %s\n", 161 p, connectable, 162 acpi_uhub_upc_type(porttypenum)); 163 164 SYSCTL_ADD_U32( 165 device_get_sysctl_ctx(dev), 166 poid, OID_AUTO, 167 "upc", 168 CTLFLAG_RD | CTLFLAG_MPSAFE, 169 SYSCTL_NULL_U32_PTR, port->upc, 170 "UPC value. MSB is visible flag"); 171 } 172 AcpiOsFree(buf.Pointer); 173 174 return (0); 175 } 176 static int 177 acpi_uhub_port_sysctl(SYSCTL_HANDLER_ARGS) 178 { 179 struct acpi_uhub_port *port = oidp->oid_arg1; 180 struct sbuf sb; 181 int error; 182 183 sbuf_new_for_sysctl(&sb, NULL, 256, req); 184 sbuf_printf(&sb, "Handle %s\n", acpi_name(port->handle)); 185 if (port->upc == 0xffffffff) { 186 sbuf_printf(&sb, "\tNo information\n"); 187 goto end; 188 } 189 sbuf_printf(&sb, "\t"); 190 if (port->upc & ACPI_UPC_CONNECTABLE) { 191 sbuf_printf(&sb, "Connectable "); 192 } 193 sbuf_printf(&sb, "%s port\n", acpi_uhub_upc_type(port->upc & 0xff)); 194 195 if ((port->pld[0] & 0x80) == 0) { 196 sbuf_printf(&sb, 197 "\tColor:#%02x%02x%02x\n", 198 port->pld[1], port->pld[2], 199 port->pld[3]); 200 } 201 sbuf_printf(&sb, "\tWidth %d mm Height %d mm\n", 202 port->pld[4] | (port->pld[5] << 8), 203 port->pld[6] | (port->pld[7] << 8)); 204 if (port->pld[8] & 1) { 205 sbuf_printf(&sb, "\tVisible\n"); 206 } 207 if (port->pld[8] & 2) { 208 sbuf_printf(&sb, "\tDock\n"); 209 } 210 if (port->pld[8] & 4) { 211 sbuf_printf(&sb, "\tLid\n"); 212 } { 213 int panelpos = (port->pld[8] >> 3) & 7; 214 const char *panposstr[] = {"Top", "Bottom", "Left", 215 "Right", "Front", "Back", 216 "Unknown", "Invalid"}; 217 const char *shapestr[] = { 218 "Round", "Oval", "Square", "VRect", "HRect", 219 "VTrape", "HTrape", "Unknown", "Chamferd", 220 "Rsvd", "Rsvd", "Rsvd", "Rsvd", 221 "Rsvd", "Rsvd", "Rsvd", "Rsvd"}; 222 223 sbuf_printf(&sb, "\tPanelPosition: %s\n", panposstr[panelpos]); 224 if (panelpos < 6) { 225 const char *posstr[] = {"Upper", "Center", 226 "Lower", "Invalid"}; 227 228 sbuf_printf(&sb, "\tVertPosition: %s\n", 229 posstr[(port->pld[8] >> 6) & 3]); 230 sbuf_printf(&sb, "\tHorizPosition: %s\n", 231 posstr[(port->pld[9]) & 3]); 232 } 233 sbuf_printf(&sb, "\tShape: %s\n", 234 shapestr[(port->pld[9] >> 2) & 0xf]); 235 sbuf_printf(&sb, "\tGroup Orientation %s\n", 236 ((port->pld[9] >> 6) & 1) ? "Vertical" : 237 "Horizontal"); 238 sbuf_printf(&sb, "\tGroupToken %x\n", 239 ((port->pld[9] >> 7) 240 | (port->pld[10] << 1)) & 0xff); 241 sbuf_printf(&sb, "\tGroupPosition %x\n", 242 ((port->pld[10] >> 7) 243 | (port->pld[11] << 1)) & 0xff); 244 sbuf_printf(&sb, "\t%s %s %s\n", 245 (port->pld[11] & 0x80) ? 246 "Bay" : "", 247 (port->pld[12] & 1) ? "Eject" : "", 248 (port->pld[12] & 2) ? "OSPM" : "" 249 ); 250 } 251 if ((port->pld[0] & 0x7f) >= 2) { 252 sbuf_printf(&sb, "\tVOFF%d mm HOFF %dmm", 253 port->pld[16] | (port->pld[17] << 8), 254 port->pld[18] | (port->pld[19] << 8)); 255 } 256 257 end: 258 error = sbuf_finish(&sb); 259 sbuf_delete(&sb); 260 return (error); 261 } 262 263 static int 264 acpi_uhub_parse_pld(device_t dev, unsigned p, ACPI_HANDLE ah, struct sysctl_oid_list *tree) 265 { 266 ACPI_BUFFER buf; 267 struct acpi_uhub_softc *sc = device_get_softc(dev); 268 struct acpi_uhub_port *port = &sc->port[p - 1]; 269 270 buf.Pointer = NULL; 271 buf.Length = ACPI_ALLOCATE_BUFFER; 272 273 if (AcpiEvaluateObject(ah, "_PLD", NULL, &buf) == AE_OK) { 274 ACPI_OBJECT *obj; 275 unsigned char *resbuf; 276 int len; 277 278 obj = buf.Pointer; 279 280 if (obj->Type == ACPI_TYPE_PACKAGE 281 && obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) { 282 ACPI_OBJECT *obj1; 283 284 obj1 = &obj->Package.Elements[0]; 285 len = obj1->Buffer.Length; 286 resbuf = obj1->Buffer.Pointer; 287 } else if (obj->Type == ACPI_TYPE_BUFFER) { 288 len = obj->Buffer.Length; 289 resbuf = obj->Buffer.Pointer; 290 } else { 291 goto skip; 292 } 293 len = (len < ACPI_PLD_SIZE) ? len : ACPI_PLD_SIZE; 294 memcpy(port->pld, resbuf, len); 295 SYSCTL_ADD_OPAQUE( 296 device_get_sysctl_ctx(dev), tree, OID_AUTO, 297 "pldraw", CTLFLAG_RD | CTLFLAG_MPSAFE, 298 port->pld, len, "A", "Raw PLD value"); 299 300 if (usb_debug) { 301 device_printf(dev, "Revision:%d\n", 302 resbuf[0] & 0x7f); 303 if ((resbuf[0] & 0x80) == 0) { 304 device_printf(dev, 305 "Color:#%02x%02x%02x\n", 306 resbuf[1], resbuf[2], 307 resbuf[3]); 308 } 309 device_printf(dev, "Width %d mm Height %d mm\n", 310 resbuf[4] | (resbuf[5] << 8), 311 resbuf[6] | (resbuf[7] << 8)); 312 if (resbuf[8] & 1) { 313 device_printf(dev, "Visible\n"); 314 } 315 if (resbuf[8] & 2) { 316 device_printf(dev, "Dock\n"); 317 } 318 if (resbuf[8] & 4) { 319 device_printf(dev, "Lid\n"); 320 } 321 device_printf(dev, "PanelPosition: %d\n", 322 (resbuf[8] >> 3) & 7); 323 device_printf(dev, "VertPosition: %d\n", 324 (resbuf[8] >> 6) & 3); 325 device_printf(dev, "HorizPosition: %d\n", 326 (resbuf[9]) & 3); 327 device_printf(dev, "Shape: %d\n", 328 (resbuf[9] >> 2) & 0xf); 329 device_printf(dev, "80: %02x, %02x, %02x\n", 330 resbuf[9], resbuf[10], resbuf[11]); 331 device_printf(dev, "96: %02x, %02x, %02x, %02x\n", 332 resbuf[12], resbuf[13], 333 resbuf[14], resbuf[15]); 334 335 if ((resbuf[0] & 0x7f) >= 2) { 336 device_printf(dev, "VOFF%d mm HOFF %dmm", 337 resbuf[16] | (resbuf[17] << 8), 338 resbuf[18] | (resbuf[19] << 8)); 339 } 340 } 341 skip: 342 AcpiOsFree(buf.Pointer); 343 } 344 return (0); 345 } 346 347 static ACPI_STATUS 348 acpi_uhub_find_rh(device_t dev, ACPI_HANDLE *ah) 349 { 350 device_t grand; 351 ACPI_HANDLE gah; 352 353 *ah = NULL; 354 grand = device_get_parent(device_get_parent(dev)); 355 356 if ((gah = acpi_get_handle(grand)) == NULL) 357 return (AE_ERROR); 358 359 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, gah, 1, 360 acpi_uhub_find_rh_cb, NULL, dev, ah)); 361 } 362 363 static ACPI_STATUS 364 acpi_usb_hub_port_probe_cb(ACPI_HANDLE ah, UINT32 lv, void *ctx, void **rv) 365 { 366 ACPI_DEVICE_INFO *devinfo; 367 device_t dev = ctx; 368 struct acpi_uhub_softc *sc = device_get_softc(dev); 369 UINT32 ret; 370 371 ret = AcpiGetObjectInfo(ah, &devinfo); 372 if (ACPI_SUCCESS(ret)) { 373 if ((devinfo->Valid & ACPI_VALID_ADR) && 374 (devinfo->Address > 0) && 375 (devinfo->Address <= (uint64_t)sc->nports)) { 376 char buf[] = "portXXX"; 377 struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 378 struct sysctl_oid *oid; 379 struct sysctl_oid_list *tree; 380 381 snprintf(buf, sizeof(buf), "port%ju", 382 (uintmax_t)devinfo->Address); 383 oid = SYSCTL_ADD_NODE(ctx, 384 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 385 OID_AUTO, buf, CTLFLAG_RD | CTLFLAG_MPSAFE, 386 NULL, "port nodes"); 387 tree = SYSCTL_CHILDREN(oid); 388 sc->port[devinfo->Address - 1].handle = ah; 389 sc->port[devinfo->Address - 1].upc = 0xffffffff; 390 acpi_uhub_parse_upc(dev, devinfo->Address, ah, tree); 391 acpi_uhub_parse_pld(dev, devinfo->Address, ah, tree); 392 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), tree, 393 OID_AUTO, "info", 394 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 395 &sc->port[devinfo->Address - 1], 0, 396 acpi_uhub_port_sysctl, "A", "Port information"); 397 } 398 AcpiOsFree(devinfo); 399 } 400 return (AE_OK); 401 } 402 403 static ACPI_STATUS 404 acpi_usb_hub_port_probe(device_t dev, ACPI_HANDLE ah) 405 { 406 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, 407 ah, 1, 408 acpi_usb_hub_port_probe_cb, 409 NULL, dev, NULL)); 410 } 411 412 static int 413 acpi_uhub_root_probe(device_t dev) 414 { 415 ACPI_STATUS status; 416 ACPI_HANDLE ah; 417 418 if (acpi_disabled("usb")) 419 return (ENXIO); 420 421 status = acpi_uhub_find_rh(dev, &ah); 422 if (ACPI_SUCCESS(status) && ah != NULL && 423 uhub_probe(dev) <= 0) { 424 /* success prior than non-ACPI USB HUB */ 425 return (BUS_PROBE_DEFAULT + 1); 426 } 427 return (ENXIO); 428 } 429 430 static int 431 acpi_uhub_probe(device_t dev) 432 { 433 ACPI_HANDLE ah; 434 435 if (acpi_disabled("usb")) 436 return (ENXIO); 437 438 ah = acpi_get_handle(dev); 439 if (ah == NULL) 440 return (ENXIO); 441 442 if (uhub_probe(dev) <= 0) { 443 /* success prior than non-ACPI USB HUB */ 444 return (BUS_PROBE_DEFAULT + 1); 445 } 446 return (ENXIO); 447 } 448 static int 449 acpi_uhub_attach_common(device_t dev) 450 { 451 struct usb_hub *uh; 452 struct acpi_uhub_softc *sc = device_get_softc(dev); 453 ACPI_STATUS status; 454 int ret = ENXIO; 455 456 uh = sc->usc.sc_udev->hub; 457 sc->nports = uh->nports; 458 sc->port = malloc(sizeof(struct acpi_uhub_port) * uh->nports, 459 M_USBDEV, M_WAITOK | M_ZERO); 460 status = acpi_usb_hub_port_probe(dev, sc->ah); 461 462 if (ACPI_SUCCESS(status)){ 463 ret = 0; 464 } 465 466 return (ret); 467 } 468 469 static int 470 acpi_uhub_detach(device_t dev) 471 { 472 struct acpi_uhub_softc *sc = device_get_softc(dev); 473 474 free(sc->port, M_USBDEV); 475 476 return (uhub_detach(dev)); 477 } 478 479 static int 480 acpi_uhub_root_attach(device_t dev) 481 { 482 int ret; 483 struct acpi_uhub_softc *sc = device_get_softc(dev); 484 485 if (ACPI_FAILURE(acpi_uhub_find_rh(dev, &sc->ah)) || 486 (sc->ah == NULL)) { 487 return (ENXIO); 488 } 489 if ((ret = uhub_attach(dev)) != 0) { 490 return (ret); 491 } 492 493 if ((ret = acpi_uhub_attach_common(dev)) != 0) { 494 acpi_uhub_detach(dev); 495 } 496 return ret; 497 } 498 499 static int 500 acpi_uhub_attach(device_t dev) 501 { 502 int ret; 503 struct acpi_uhub_softc *sc = device_get_softc(dev); 504 505 sc->ah = acpi_get_handle(dev); 506 507 if (sc->ah == NULL) { 508 return (ENXIO); 509 } 510 if ((ret = uhub_attach(dev)) != 0) { 511 return (ret); 512 } 513 514 if ((ret = acpi_uhub_attach_common(dev)) != 0) { 515 acpi_uhub_detach(dev); 516 } 517 518 return (ret); 519 } 520 521 static int 522 acpi_uhub_read_ivar(device_t dev, device_t child, int idx, uintptr_t *res) 523 { 524 struct hub_result hres; 525 struct acpi_uhub_softc *sc = device_get_softc(dev); 526 ACPI_HANDLE ah; 527 528 bus_topo_lock(); 529 uhub_find_iface_index(sc->usc.sc_udev->hub, child, &hres); 530 bus_topo_unlock(); 531 532 if ((idx == ACPI_IVAR_HANDLE) && 533 (hres.portno > 0) && 534 (hres.portno <= sc->nports) && 535 (ah = sc->port[hres.portno - 1].handle)) { 536 *res = (uintptr_t)ah; 537 return (0); 538 } 539 return (ENXIO); 540 } 541 542 static int 543 acpi_uhub_child_location(device_t parent, device_t child, struct sbuf *sb) 544 { 545 ACPI_HANDLE ah; 546 547 uhub_child_location(parent, child, sb); 548 549 ah = acpi_get_handle(child); 550 if (ah != NULL) 551 sbuf_printf(sb, " handle=%s", acpi_name(ah)); 552 return (0); 553 } 554 555 static int 556 acpi_uhub_get_device_path(device_t bus, device_t child, const char *locator, struct sbuf *sb) 557 { 558 if (strcmp(locator, BUS_LOCATOR_ACPI) == 0) 559 return (acpi_get_acpi_device_path(bus, child, locator, sb)); 560 561 /* Otherwise call the parent class' method. */ 562 return (uhub_get_device_path(bus, child, locator, sb)); 563 } 564 565 static device_method_t acpi_uhub_methods[] = { 566 DEVMETHOD(device_probe, acpi_uhub_probe), 567 DEVMETHOD(device_attach, acpi_uhub_attach), 568 DEVMETHOD(device_detach, acpi_uhub_detach), 569 DEVMETHOD(bus_child_location, acpi_uhub_child_location), 570 DEVMETHOD(bus_get_device_path, acpi_uhub_get_device_path), 571 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar), 572 DEVMETHOD_END 573 574 }; 575 576 static device_method_t acpi_uhub_root_methods[] = { 577 DEVMETHOD(device_probe, acpi_uhub_root_probe), 578 DEVMETHOD(device_attach, acpi_uhub_root_attach), 579 DEVMETHOD(device_detach, acpi_uhub_detach), 580 DEVMETHOD(bus_read_ivar, acpi_uhub_read_ivar), 581 DEVMETHOD(bus_child_location, acpi_uhub_child_location), 582 DEVMETHOD(bus_get_device_path, acpi_uhub_get_device_path), 583 DEVMETHOD_END 584 }; 585 586 extern driver_t uhub_driver; 587 static kobj_class_t uhub_baseclasses[] = {&uhub_driver, NULL}; 588 589 static driver_t acpi_uhub_driver = { 590 .name = "uhub", 591 .methods = acpi_uhub_methods, 592 .size = sizeof(struct acpi_uhub_softc), 593 .baseclasses = uhub_baseclasses, 594 }; 595 596 static driver_t acpi_uhub_root_driver = { 597 .name = "uhub", 598 .methods = acpi_uhub_root_methods, 599 .size = sizeof(struct acpi_uhub_softc), 600 .baseclasses = uhub_baseclasses, 601 }; 602 603 DRIVER_MODULE(uacpi, uhub, acpi_uhub_driver, 0, 0); 604 DRIVER_MODULE(uacpi, usbus, acpi_uhub_root_driver, 0, 0); 605 606 MODULE_DEPEND(uacpi, acpi, 1, 1, 1); 607 MODULE_DEPEND(uacpi, usb, 1, 1, 1); 608 609 MODULE_VERSION(uacpi, 1); 610