1 /*- 2 * Copyright (c) 2005-2006 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include "opt_acpi.h" 30 #include <sys/param.h> 31 #include <sys/bus.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 35 #include <contrib/dev/acpica/acpi.h> 36 #include <contrib/dev/acpica/acnamesp.h> 37 #include <dev/acpica/acpivar.h> 38 #include <dev/acpica/acpiio.h> 39 40 /* Hooks for the ACPI CA debugging infrastructure */ 41 #define _COMPONENT ACPI_DOCK 42 ACPI_MODULE_NAME("DOCK") 43 44 /* For Notify handler */ 45 #define ACPI_DOCK_NOTIFY_BUS_CHECK 0x00 46 #define ACPI_DOCK_NOTIFY_DEVICE_CHECK 0x01 47 #define ACPI_DOCK_NOTIFY_EJECT_REQUEST 0x03 48 49 /* For Docking status */ 50 #define ACPI_DOCK_STATUS_UNKNOWN -1 51 #define ACPI_DOCK_STATUS_UNDOCKED 0 52 #define ACPI_DOCK_STATUS_DOCKED 1 53 54 #define ACPI_DOCK_UNLOCK 0 /* Allow device to be ejected */ 55 #define ACPI_DOCK_LOCK 1 /* Prevent dev from being removed */ 56 57 #define ACPI_DOCK_ISOLATE 0 /* Isolate from dock connector */ 58 #define ACPI_DOCK_CONNECT 1 /* Connect to dock */ 59 60 struct acpi_dock_softc { 61 int _sta; 62 int _bdn; 63 int _uid; 64 int status; 65 struct sysctl_ctx_list *sysctl_ctx; 66 struct sysctl_oid *sysctl_tree; 67 }; 68 69 ACPI_SERIAL_DECL(dock, "ACPI Docking Station"); 70 71 /* 72 * Utility functions 73 */ 74 75 static void 76 acpi_dock_get_info(device_t dev) 77 { 78 struct acpi_dock_softc *sc; 79 ACPI_HANDLE h; 80 81 sc = device_get_softc(dev); 82 h = acpi_get_handle(dev); 83 84 if (ACPI_FAILURE(acpi_GetInteger(h, "_STA", &sc->_sta))) 85 sc->_sta = ACPI_DOCK_STATUS_UNKNOWN; 86 if (ACPI_FAILURE(acpi_GetInteger(h, "_BDN", &sc->_bdn))) 87 sc->_bdn = ACPI_DOCK_STATUS_UNKNOWN; 88 if (ACPI_FAILURE(acpi_GetInteger(h, "_UID", &sc->_uid))) 89 sc->_uid = ACPI_DOCK_STATUS_UNKNOWN; 90 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 91 "_STA: %04x, _BDN: %04x, _UID: %04x\n", sc->_sta, 92 sc->_bdn, sc->_uid); 93 } 94 95 static int 96 acpi_dock_execute_dck(device_t dev, int dock) 97 { 98 ACPI_HANDLE h; 99 ACPI_OBJECT argobj; 100 ACPI_OBJECT_LIST args; 101 ACPI_BUFFER buf; 102 ACPI_OBJECT retobj; 103 ACPI_STATUS status; 104 105 h = acpi_get_handle(dev); 106 107 argobj.Type = ACPI_TYPE_INTEGER; 108 argobj.Integer.Value = dock; 109 args.Count = 1; 110 args.Pointer = &argobj; 111 buf.Pointer = &retobj; 112 buf.Length = sizeof(retobj); 113 status = AcpiEvaluateObject(h, "_DCK", &args, &buf); 114 115 /* 116 * When _DCK is called with 0, OSPM will ignore the return value. 117 */ 118 if (dock == ACPI_DOCK_ISOLATE) 119 return (0); 120 121 /* If _DCK returned 1, the request succeeded. */ 122 if (ACPI_SUCCESS(status) && retobj.Type == ACPI_TYPE_INTEGER && 123 retobj.Integer.Value == 1) 124 return (0); 125 126 return (-1); 127 } 128 129 /* Lock devices while docked to prevent surprise removal. */ 130 static void 131 acpi_dock_execute_lck(device_t dev, int lock) 132 { 133 ACPI_HANDLE h; 134 135 h = acpi_get_handle(dev); 136 acpi_SetInteger(h, "_LCK", lock); 137 } 138 139 /* Eject a device (i.e., motorized). */ 140 static int 141 acpi_dock_execute_ejx(device_t dev, int eject, int state) 142 { 143 ACPI_HANDLE h; 144 ACPI_STATUS status; 145 char ejx[5]; 146 147 h = acpi_get_handle(dev); 148 snprintf(ejx, sizeof(ejx), "_EJ%d", state); 149 status = acpi_SetInteger(h, ejx, eject); 150 if (ACPI_SUCCESS(status)) 151 return (0); 152 153 return (-1); 154 } 155 156 /* Find dependent devices. When their parent is removed, so are they. */ 157 static int 158 acpi_dock_is_ejd_device(ACPI_HANDLE dock_handle, ACPI_HANDLE handle) 159 { 160 int ret; 161 ACPI_STATUS ret_status; 162 ACPI_BUFFER ejd_buffer; 163 ACPI_OBJECT *obj; 164 165 ret = 0; 166 167 ejd_buffer.Pointer = NULL; 168 ejd_buffer.Length = ACPI_ALLOCATE_BUFFER; 169 ret_status = AcpiEvaluateObject(handle, "_EJD", NULL, &ejd_buffer); 170 if (ACPI_FAILURE(ret_status)) 171 goto out; 172 173 obj = (ACPI_OBJECT *)ejd_buffer.Pointer; 174 if (dock_handle == acpi_GetReference(NULL, obj)) 175 ret = 1; 176 177 out: 178 if (ejd_buffer.Pointer != NULL) 179 AcpiOsFree(ejd_buffer.Pointer); 180 181 return (ret); 182 } 183 184 /* 185 * Docking functions 186 */ 187 188 static void 189 acpi_dock_attach_later(void *context) 190 { 191 device_t dev; 192 193 dev = (device_t)context; 194 195 if (!device_is_enabled(dev)) 196 device_enable(dev); 197 198 mtx_lock(&Giant); 199 device_probe_and_attach(dev); 200 mtx_unlock(&Giant); 201 } 202 203 static ACPI_STATUS 204 acpi_dock_insert_child(ACPI_HANDLE handle, UINT32 level, void *context, 205 void **status) 206 { 207 device_t dock_dev, dev; 208 ACPI_HANDLE dock_handle; 209 210 dock_dev = (device_t)context; 211 dock_handle = acpi_get_handle(dock_dev); 212 213 if (!acpi_dock_is_ejd_device(dock_handle, handle)) 214 goto out; 215 216 ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev), 217 "inserting device for %s\n", acpi_name(handle)); 218 219 #if 0 220 /* 221 * If the system boot up w/o Docking, the devices under the dock 222 * still un-initialized, also control methods such as _INI, _STA 223 * are not executed. 224 * Normal devices are initialized at booting by calling 225 * AcpiInitializeObjects(), however the devices under the dock 226 * need to be initialized here on the scheme of ACPICA. 227 */ 228 ACPI_INIT_WALK_INFO Info; 229 230 AcpiNsWalkNamespace(ACPI_TYPE_ANY, handle, 231 100, TRUE, AcpiNsInitOneDevice, &Info, NULL); 232 #endif 233 234 dev = acpi_get_device(handle); 235 if (dev == NULL) { 236 device_printf(dock_dev, "error: %s has no associated device\n", 237 acpi_name(handle)); 238 goto out; 239 } 240 241 AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_dock_attach_later, dev); 242 243 out: 244 return (AE_OK); 245 } 246 247 static void 248 acpi_dock_insert_children(device_t dev) 249 { 250 ACPI_STATUS status; 251 ACPI_HANDLE sb_handle; 252 253 status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle); 254 if (ACPI_SUCCESS(status)) { 255 AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle, 256 100, acpi_dock_insert_child, dev, NULL); 257 } 258 } 259 260 static void 261 acpi_dock_insert(device_t dev) 262 { 263 struct acpi_dock_softc *sc; 264 ACPI_HANDLE h; 265 266 ACPI_SERIAL_ASSERT(dock); 267 268 sc = device_get_softc(dev); 269 h = acpi_get_handle(dev); 270 271 if (sc->status == ACPI_DOCK_STATUS_UNDOCKED || 272 sc->status == ACPI_DOCK_STATUS_UNKNOWN) { 273 acpi_dock_execute_lck(dev, ACPI_DOCK_LOCK); 274 if (acpi_dock_execute_dck(dev, ACPI_DOCK_CONNECT) != 0) { 275 device_printf(dev, "_DCK failed\n"); 276 return; 277 } 278 279 if (!cold) 280 acpi_dock_insert_children(dev); 281 sc->status = ACPI_DOCK_STATUS_DOCKED; 282 } 283 } 284 285 /* 286 * Undock 287 */ 288 289 static ACPI_STATUS 290 acpi_dock_eject_child(ACPI_HANDLE handle, UINT32 level, void *context, 291 void **status) 292 { 293 device_t dock_dev, dev; 294 ACPI_HANDLE dock_handle; 295 296 dock_dev = *(device_t *)context; 297 dock_handle = acpi_get_handle(dock_dev); 298 299 if (!acpi_dock_is_ejd_device(dock_handle, handle)) 300 goto out; 301 302 ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev), 303 "ejecting device for %s\n", acpi_name(handle)); 304 305 dev = acpi_get_device(handle); 306 if (dev != NULL && device_is_attached(dev)) { 307 mtx_lock(&Giant); 308 device_detach(dev); 309 mtx_unlock(&Giant); 310 } 311 312 acpi_SetInteger(handle, "_EJ0", 0); 313 out: 314 return (AE_OK); 315 } 316 317 static void 318 acpi_dock_eject_children(device_t dev) 319 { 320 ACPI_HANDLE sb_handle; 321 ACPI_STATUS status; 322 323 status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle); 324 if (ACPI_SUCCESS(status)) { 325 AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle, 326 100, acpi_dock_eject_child, &dev, NULL); 327 } 328 } 329 330 static void 331 acpi_dock_removal(device_t dev) 332 { 333 struct acpi_dock_softc *sc; 334 335 ACPI_SERIAL_ASSERT(dock); 336 337 sc = device_get_softc(dev); 338 if (sc->status == ACPI_DOCK_STATUS_DOCKED || 339 sc->status == ACPI_DOCK_STATUS_UNKNOWN) { 340 acpi_dock_eject_children(dev); 341 if (acpi_dock_execute_dck(dev, ACPI_DOCK_ISOLATE) != 0) 342 return; 343 344 acpi_dock_execute_lck(dev, ACPI_DOCK_UNLOCK); 345 346 if (acpi_dock_execute_ejx(dev, 1, 0) != 0) { 347 device_printf(dev, "_EJ0 failed\n"); 348 return; 349 } 350 351 sc->status = ACPI_DOCK_STATUS_UNDOCKED; 352 } 353 354 acpi_dock_get_info(dev); 355 if (sc->_sta != 0) 356 device_printf(dev, "mechanical failure (%#x).\n", sc->_sta); 357 } 358 359 /* 360 * Device/Bus check 361 */ 362 363 static void 364 acpi_dock_device_check(device_t dev) 365 { 366 struct acpi_dock_softc *sc; 367 368 ACPI_SERIAL_ASSERT(dock); 369 370 sc = device_get_softc(dev); 371 acpi_dock_get_info(dev); 372 373 /* 374 * If the _STA method indicates 'present' and 'functioning', the 375 * system is docked. If _STA does not exist for this device, it 376 * is always present. 377 */ 378 if (sc->_sta == ACPI_DOCK_STATUS_UNKNOWN || 379 ACPI_DEVICE_PRESENT(sc->_sta)) 380 acpi_dock_insert(dev); 381 else if (sc->_sta == 0) 382 acpi_dock_removal(dev); 383 } 384 385 /* 386 * Notify Handler 387 */ 388 389 static void 390 acpi_dock_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 391 { 392 device_t dev; 393 394 dev = (device_t) context; 395 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 396 "got notification %#x\n", notify); 397 398 ACPI_SERIAL_BEGIN(dock); 399 switch (notify) { 400 case ACPI_DOCK_NOTIFY_BUS_CHECK: 401 case ACPI_DOCK_NOTIFY_DEVICE_CHECK: 402 acpi_dock_device_check(dev); 403 break; 404 case ACPI_DOCK_NOTIFY_EJECT_REQUEST: 405 acpi_dock_removal(dev); 406 break; 407 default: 408 device_printf(dev, "unknown notify %#x\n", notify); 409 break; 410 } 411 ACPI_SERIAL_END(dock); 412 } 413 414 static int 415 acpi_dock_status_sysctl(SYSCTL_HANDLER_ARGS) 416 { 417 struct acpi_dock_softc *sc; 418 device_t dev; 419 int status, err; 420 421 err = 0; 422 dev = (device_t)arg1; 423 424 sc = device_get_softc(dev); 425 status = sc->status; 426 427 ACPI_SERIAL_BEGIN(dock); 428 err = sysctl_handle_int(oidp, &status, 0, req); 429 if (err != 0 || req->newptr == NULL) 430 goto out; 431 432 if (status != ACPI_DOCK_STATUS_UNDOCKED && 433 status != ACPI_DOCK_STATUS_DOCKED) { 434 err = EINVAL; 435 goto out; 436 } 437 438 if (status == sc->status) 439 goto out; 440 441 switch (status) { 442 case ACPI_DOCK_STATUS_UNDOCKED: 443 acpi_dock_removal(dev); 444 break; 445 case ACPI_DOCK_STATUS_DOCKED: 446 acpi_dock_device_check(dev); 447 break; 448 default: 449 err = EINVAL; 450 break; 451 } 452 out: 453 ACPI_SERIAL_END(dock); 454 return (err); 455 } 456 457 static int 458 acpi_dock_probe(device_t dev) 459 { 460 ACPI_HANDLE h, tmp; 461 462 h = acpi_get_handle(dev); 463 if (acpi_disabled("dock") || 464 ACPI_FAILURE(AcpiGetHandle(h, "_DCK", &tmp))) 465 return (ENXIO); 466 467 device_set_desc(dev, "ACPI Docking Station"); 468 469 /* 470 * XXX Somewhere else in the kernel panics on "sysctl kern" if we 471 * return a negative value here (reprobe ok). 472 */ 473 return (0); 474 } 475 476 static int 477 acpi_dock_attach(device_t dev) 478 { 479 struct acpi_dock_softc *sc; 480 ACPI_HANDLE h; 481 482 sc = device_get_softc(dev); 483 h = acpi_get_handle(dev); 484 if (sc == NULL || h == NULL) 485 return (ENXIO); 486 487 sc->status = ACPI_DOCK_STATUS_UNKNOWN; 488 489 AcpiEvaluateObject(h, "_INI", NULL, NULL); 490 491 ACPI_SERIAL_BEGIN(dock); 492 493 acpi_dock_device_check(dev); 494 495 /* Get the sysctl tree */ 496 sc->sysctl_ctx = device_get_sysctl_ctx(dev); 497 sc->sysctl_tree = device_get_sysctl_tree(dev); 498 499 SYSCTL_ADD_INT(sc->sysctl_ctx, 500 SYSCTL_CHILDREN(sc->sysctl_tree), 501 OID_AUTO, "_sta", CTLFLAG_RD, 502 &sc->_sta, 0, "Dock _STA"); 503 SYSCTL_ADD_INT(sc->sysctl_ctx, 504 SYSCTL_CHILDREN(sc->sysctl_tree), 505 OID_AUTO, "_bdn", CTLFLAG_RD, 506 &sc->_bdn, 0, "Dock _BDN"); 507 SYSCTL_ADD_INT(sc->sysctl_ctx, 508 SYSCTL_CHILDREN(sc->sysctl_tree), 509 OID_AUTO, "_uid", CTLFLAG_RD, 510 &sc->_uid, 0, "Dock _UID"); 511 SYSCTL_ADD_PROC(sc->sysctl_ctx, 512 SYSCTL_CHILDREN(sc->sysctl_tree), 513 OID_AUTO, "status", 514 CTLTYPE_INT|CTLFLAG_RW, dev, 0, 515 acpi_dock_status_sysctl, "I", 516 "Dock/Undock operation"); 517 518 ACPI_SERIAL_END(dock); 519 520 AcpiInstallNotifyHandler(h, ACPI_ALL_NOTIFY, 521 acpi_dock_notify_handler, dev); 522 523 return (0); 524 } 525 526 static device_method_t acpi_dock_methods[] = { 527 /* Device interface */ 528 DEVMETHOD(device_probe, acpi_dock_probe), 529 DEVMETHOD(device_attach, acpi_dock_attach), 530 531 {0, 0} 532 }; 533 534 static driver_t acpi_dock_driver = { 535 "acpi_dock", 536 acpi_dock_methods, 537 sizeof(struct acpi_dock_softc), 538 }; 539 540 static devclass_t acpi_dock_devclass; 541 542 DRIVER_MODULE(acpi_dock, acpi, acpi_dock_driver, acpi_dock_devclass, 0, 0); 543 MODULE_DEPEND(acpi_dock, acpi, 1, 1, 1); 544 545