1 /*- 2 * Copyright (c) 2004 Takanori Watanabe 3 * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 /* 32 * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops. 33 * Inspired by the ibm-acpi and tpb projects which implement these features 34 * on Linux. 35 * 36 * acpi-ibm: <http://ibm-acpi.sourceforge.net/> 37 * tpb: <http://www.nongnu.org/tpb/> 38 */ 39 40 #include "opt_acpi.h" 41 #include <sys/param.h> 42 #include <sys/kernel.h> 43 #include <sys/bus.h> 44 #include <machine/cpufunc.h> 45 46 #include <contrib/dev/acpica/include/acpi.h> 47 #include <contrib/dev/acpica/include/accommon.h> 48 49 #include "acpi_if.h" 50 #include <sys/module.h> 51 #include <dev/acpica/acpivar.h> 52 #include <dev/led/led.h> 53 #include <sys/sysctl.h> 54 #include <isa/rtc.h> 55 56 #define _COMPONENT ACPI_OEM 57 ACPI_MODULE_NAME("IBM") 58 59 /* Internal methods */ 60 #define ACPI_IBM_METHOD_EVENTS 1 61 #define ACPI_IBM_METHOD_EVENTMASK 2 62 #define ACPI_IBM_METHOD_HOTKEY 3 63 #define ACPI_IBM_METHOD_BRIGHTNESS 4 64 #define ACPI_IBM_METHOD_VOLUME 5 65 #define ACPI_IBM_METHOD_MUTE 6 66 #define ACPI_IBM_METHOD_THINKLIGHT 7 67 #define ACPI_IBM_METHOD_BLUETOOTH 8 68 #define ACPI_IBM_METHOD_WLAN 9 69 #define ACPI_IBM_METHOD_FANSPEED 10 70 #define ACPI_IBM_METHOD_FANLEVEL 11 71 #define ACPI_IBM_METHOD_FANSTATUS 12 72 #define ACPI_IBM_METHOD_THERMAL 13 73 74 /* Hotkeys/Buttons */ 75 #define IBM_RTC_HOTKEY1 0x64 76 #define IBM_RTC_MASK_HOME (1 << 0) 77 #define IBM_RTC_MASK_SEARCH (1 << 1) 78 #define IBM_RTC_MASK_MAIL (1 << 2) 79 #define IBM_RTC_MASK_WLAN (1 << 5) 80 #define IBM_RTC_HOTKEY2 0x65 81 #define IBM_RTC_MASK_THINKPAD (1 << 3) 82 #define IBM_RTC_MASK_ZOOM (1 << 5) 83 #define IBM_RTC_MASK_VIDEO (1 << 6) 84 #define IBM_RTC_MASK_HIBERNATE (1 << 7) 85 #define IBM_RTC_THINKLIGHT 0x66 86 #define IBM_RTC_MASK_THINKLIGHT (1 << 4) 87 #define IBM_RTC_SCREENEXPAND 0x67 88 #define IBM_RTC_MASK_SCREENEXPAND (1 << 5) 89 #define IBM_RTC_BRIGHTNESS 0x6c 90 #define IBM_RTC_MASK_BRIGHTNESS (1 << 5) 91 #define IBM_RTC_VOLUME 0x6e 92 #define IBM_RTC_MASK_VOLUME (1 << 7) 93 94 /* Embedded Controller registers */ 95 #define IBM_EC_BRIGHTNESS 0x31 96 #define IBM_EC_MASK_BRI 0x7 97 #define IBM_EC_VOLUME 0x30 98 #define IBM_EC_MASK_VOL 0xf 99 #define IBM_EC_MASK_MUTE (1 << 6) 100 #define IBM_EC_FANSTATUS 0x2F 101 #define IBM_EC_MASK_FANLEVEL 0x3f 102 #define IBM_EC_MASK_FANDISENGAGED (1 << 6) 103 #define IBM_EC_MASK_FANSTATUS (1 << 7) 104 #define IBM_EC_FANSPEED 0x84 105 106 /* CMOS Commands */ 107 #define IBM_CMOS_VOLUME_DOWN 0 108 #define IBM_CMOS_VOLUME_UP 1 109 #define IBM_CMOS_VOLUME_MUTE 2 110 #define IBM_CMOS_BRIGHTNESS_UP 4 111 #define IBM_CMOS_BRIGHTNESS_DOWN 5 112 113 /* ACPI methods */ 114 #define IBM_NAME_KEYLIGHT "KBLT" 115 #define IBM_NAME_WLAN_BT_GET "GBDC" 116 #define IBM_NAME_WLAN_BT_SET "SBDC" 117 #define IBM_NAME_MASK_BT (1 << 1) 118 #define IBM_NAME_MASK_WLAN (1 << 2) 119 #define IBM_NAME_THERMAL_GET "TMP7" 120 #define IBM_NAME_THERMAL_UPDT "UPDT" 121 122 #define IBM_NAME_EVENTS_STATUS_GET "DHKC" 123 #define IBM_NAME_EVENTS_MASK_GET "DHKN" 124 #define IBM_NAME_EVENTS_STATUS_SET "MHKC" 125 #define IBM_NAME_EVENTS_MASK_SET "MHKM" 126 #define IBM_NAME_EVENTS_GET "MHKP" 127 #define IBM_NAME_EVENTS_AVAILMASK "MHKA" 128 129 #define ABS(x) (((x) < 0)? -(x) : (x)) 130 131 struct acpi_ibm_softc { 132 device_t dev; 133 ACPI_HANDLE handle; 134 135 /* Embedded controller */ 136 device_t ec_dev; 137 ACPI_HANDLE ec_handle; 138 139 /* CMOS */ 140 ACPI_HANDLE cmos_handle; 141 142 /* Fan status */ 143 ACPI_HANDLE fan_handle; 144 int fan_levels; 145 146 /* Keylight commands and states */ 147 ACPI_HANDLE light_handle; 148 int light_cmd_on; 149 int light_cmd_off; 150 int light_val; 151 int light_get_supported; 152 int light_set_supported; 153 154 /* led(4) interface */ 155 struct cdev *led_dev; 156 int led_busy; 157 int led_state; 158 159 int wlan_bt_flags; 160 int thermal_updt_supported; 161 162 unsigned int events_availmask; 163 unsigned int events_initialmask; 164 int events_mask_supported; 165 int events_enable; 166 167 struct sysctl_ctx_list *sysctl_ctx; 168 struct sysctl_oid *sysctl_tree; 169 }; 170 171 static struct { 172 char *name; 173 int method; 174 char *description; 175 int access; 176 } acpi_ibm_sysctls[] = { 177 { 178 .name = "events", 179 .method = ACPI_IBM_METHOD_EVENTS, 180 .description = "ACPI events enable", 181 .access = CTLTYPE_INT | CTLFLAG_RW 182 }, 183 { 184 .name = "eventmask", 185 .method = ACPI_IBM_METHOD_EVENTMASK, 186 .description = "ACPI eventmask", 187 .access = CTLTYPE_INT | CTLFLAG_RW 188 }, 189 { 190 .name = "hotkey", 191 .method = ACPI_IBM_METHOD_HOTKEY, 192 .description = "Key Status", 193 .access = CTLTYPE_INT | CTLFLAG_RD 194 }, 195 { 196 .name = "lcd_brightness", 197 .method = ACPI_IBM_METHOD_BRIGHTNESS, 198 .description = "LCD Brightness", 199 .access = CTLTYPE_INT | CTLFLAG_RW 200 }, 201 { 202 .name = "volume", 203 .method = ACPI_IBM_METHOD_VOLUME, 204 .description = "Volume", 205 .access = CTLTYPE_INT | CTLFLAG_RW 206 }, 207 { 208 .name = "mute", 209 .method = ACPI_IBM_METHOD_MUTE, 210 .description = "Mute", 211 .access = CTLTYPE_INT | CTLFLAG_RW 212 }, 213 { 214 .name = "thinklight", 215 .method = ACPI_IBM_METHOD_THINKLIGHT, 216 .description = "Thinklight enable", 217 .access = CTLTYPE_INT | CTLFLAG_RW 218 }, 219 { 220 .name = "bluetooth", 221 .method = ACPI_IBM_METHOD_BLUETOOTH, 222 .description = "Bluetooth enable", 223 .access = CTLTYPE_INT | CTLFLAG_RW 224 }, 225 { 226 .name = "wlan", 227 .method = ACPI_IBM_METHOD_WLAN, 228 .description = "WLAN enable", 229 .access = CTLTYPE_INT | CTLFLAG_RD 230 }, 231 { 232 .name = "fan_speed", 233 .method = ACPI_IBM_METHOD_FANSPEED, 234 .description = "Fan speed", 235 .access = CTLTYPE_INT | CTLFLAG_RD 236 }, 237 { 238 .name = "fan_level", 239 .method = ACPI_IBM_METHOD_FANLEVEL, 240 .description = "Fan level", 241 .access = CTLTYPE_INT | CTLFLAG_RW 242 }, 243 { 244 .name = "fan", 245 .method = ACPI_IBM_METHOD_FANSTATUS, 246 .description = "Fan enable", 247 .access = CTLTYPE_INT | CTLFLAG_RW 248 }, 249 250 { NULL, 0, NULL, 0 } 251 }; 252 253 ACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); 254 255 static int acpi_ibm_probe(device_t dev); 256 static int acpi_ibm_attach(device_t dev); 257 static int acpi_ibm_detach(device_t dev); 258 static int acpi_ibm_resume(device_t dev); 259 260 static void ibm_led(void *softc, int onoff); 261 static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); 262 263 static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); 264 static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); 265 static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); 266 static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); 267 268 static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); 269 static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); 270 static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); 271 272 static device_method_t acpi_ibm_methods[] = { 273 /* Device interface */ 274 DEVMETHOD(device_probe, acpi_ibm_probe), 275 DEVMETHOD(device_attach, acpi_ibm_attach), 276 DEVMETHOD(device_detach, acpi_ibm_detach), 277 DEVMETHOD(device_resume, acpi_ibm_resume), 278 279 {0, 0} 280 }; 281 282 static driver_t acpi_ibm_driver = { 283 "acpi_ibm", 284 acpi_ibm_methods, 285 sizeof(struct acpi_ibm_softc), 286 }; 287 288 static devclass_t acpi_ibm_devclass; 289 290 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 291 0, 0); 292 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 293 static char *ibm_ids[] = {"IBM0068", NULL}; 294 295 static void 296 ibm_led(void *softc, int onoff) 297 { 298 struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; 299 300 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 301 302 if (sc->led_busy) 303 return; 304 305 sc->led_busy = 1; 306 sc->led_state = onoff; 307 308 AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); 309 } 310 311 static void 312 ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) 313 { 314 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 315 316 ACPI_SERIAL_BEGIN(ibm); 317 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); 318 ACPI_SERIAL_END(ibm); 319 320 sc->led_busy = 0; 321 } 322 323 static int 324 acpi_ibm_probe(device_t dev) 325 { 326 if (acpi_disabled("ibm") || 327 ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || 328 device_get_unit(dev) != 0) 329 return (ENXIO); 330 331 device_set_desc(dev, "IBM ThinkPad ACPI Extras"); 332 333 return (0); 334 } 335 336 static int 337 acpi_ibm_attach(device_t dev) 338 { 339 struct acpi_ibm_softc *sc; 340 devclass_t ec_devclass; 341 342 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 343 344 sc = device_get_softc(dev); 345 sc->dev = dev; 346 sc->handle = acpi_get_handle(dev); 347 348 /* Look for the first embedded controller */ 349 if (!(ec_devclass = devclass_find ("acpi_ec"))) { 350 if (bootverbose) 351 device_printf(dev, "Couldn't find acpi_ec devclass\n"); 352 return (EINVAL); 353 } 354 if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { 355 if (bootverbose) 356 device_printf(dev, "Couldn't find acpi_ec device\n"); 357 return (EINVAL); 358 } 359 sc->ec_handle = acpi_get_handle(sc->ec_dev); 360 361 /* Get the sysctl tree */ 362 sc->sysctl_ctx = device_get_sysctl_ctx(dev); 363 sc->sysctl_tree = device_get_sysctl_tree(dev); 364 365 /* Look for event mask and hook up the nodes */ 366 sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, 367 IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); 368 369 if (sc->events_mask_supported) { 370 SYSCTL_ADD_UINT(sc->sysctl_ctx, 371 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 372 "initialmask", CTLFLAG_RD, 373 &sc->events_initialmask, 0, "Initial eventmask"); 374 375 /* The availmask is the bitmask of supported events */ 376 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 377 IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) 378 sc->events_availmask = 0xffffffff; 379 380 SYSCTL_ADD_UINT(sc->sysctl_ctx, 381 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 382 "availmask", CTLFLAG_RD, 383 &sc->events_availmask, 0, "Mask of supported events"); 384 } 385 386 /* Hook up proc nodes */ 387 for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 388 if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) 389 continue; 390 391 SYSCTL_ADD_PROC(sc->sysctl_ctx, 392 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 393 acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, 394 sc, i, acpi_ibm_sysctl, "I", 395 acpi_ibm_sysctls[i].description); 396 } 397 398 /* Hook up thermal node */ 399 if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) { 400 SYSCTL_ADD_PROC(sc->sysctl_ctx, 401 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 402 "thermal", CTLTYPE_INT | CTLFLAG_RD, 403 sc, 0, acpi_ibm_thermal_sysctl, "I", 404 "Thermal zones"); 405 } 406 407 /* Handle notifies */ 408 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 409 acpi_ibm_notify, dev); 410 411 /* Hook up light to led(4) */ 412 if (sc->light_set_supported) 413 sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); 414 415 return (0); 416 } 417 418 static int 419 acpi_ibm_detach(device_t dev) 420 { 421 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 422 423 struct acpi_ibm_softc *sc = device_get_softc(dev); 424 425 /* Disable events and restore eventmask */ 426 ACPI_SERIAL_BEGIN(ibm); 427 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); 428 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); 429 ACPI_SERIAL_END(ibm); 430 431 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); 432 433 if (sc->led_dev != NULL) 434 led_destroy(sc->led_dev); 435 436 return (0); 437 } 438 439 static int 440 acpi_ibm_resume(device_t dev) 441 { 442 struct acpi_ibm_softc *sc = device_get_softc(dev); 443 444 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 445 446 ACPI_SERIAL_BEGIN(ibm); 447 for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 448 int val; 449 450 if ((acpi_ibm_sysctls[i].access & CTLFLAG_RD) == 0) { 451 continue; 452 } 453 454 val = acpi_ibm_sysctl_get(sc, i); 455 456 if ((acpi_ibm_sysctls[i].access & CTLFLAG_WR) == 0) { 457 continue; 458 } 459 460 acpi_ibm_sysctl_set(sc, i, val); 461 } 462 ACPI_SERIAL_END(ibm); 463 464 return (0); 465 } 466 467 static int 468 acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) 469 { 470 ACPI_OBJECT arg[2]; 471 ACPI_OBJECT_LIST args; 472 ACPI_STATUS status; 473 474 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 475 ACPI_SERIAL_ASSERT(ibm); 476 477 args.Count = 2; 478 args.Pointer = arg; 479 arg[0].Type = ACPI_TYPE_INTEGER; 480 arg[1].Type = ACPI_TYPE_INTEGER; 481 482 for (int i = 0; i < 32; ++i) { 483 arg[0].Integer.Value = i+1; 484 arg[1].Integer.Value = (((1 << i) & val) != 0); 485 status = AcpiEvaluateObject(sc->handle, 486 IBM_NAME_EVENTS_MASK_SET, &args, NULL); 487 488 if (ACPI_FAILURE(status)) 489 return (status); 490 } 491 492 return (0); 493 } 494 495 static int 496 acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) 497 { 498 struct acpi_ibm_softc *sc; 499 int arg; 500 int error = 0; 501 int function; 502 int method; 503 504 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 505 506 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 507 function = oidp->oid_arg2; 508 method = acpi_ibm_sysctls[function].method; 509 510 ACPI_SERIAL_BEGIN(ibm); 511 arg = acpi_ibm_sysctl_get(sc, method); 512 error = sysctl_handle_int(oidp, &arg, 0, req); 513 514 /* Sanity check */ 515 if (error != 0 || req->newptr == NULL) 516 goto out; 517 518 /* Update */ 519 error = acpi_ibm_sysctl_set(sc, method, arg); 520 521 out: 522 ACPI_SERIAL_END(ibm); 523 return (error); 524 } 525 526 static int 527 acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) 528 { 529 UINT64 val_ec; 530 int val = 0, key; 531 532 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 533 ACPI_SERIAL_ASSERT(ibm); 534 535 switch (method) { 536 case ACPI_IBM_METHOD_EVENTS: 537 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); 538 break; 539 540 case ACPI_IBM_METHOD_EVENTMASK: 541 if (sc->events_mask_supported) 542 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); 543 break; 544 545 case ACPI_IBM_METHOD_HOTKEY: 546 /* 547 * Construct the hotkey as a bitmask as illustrated below. 548 * Note that whenever a key was pressed, the respecting bit 549 * toggles and nothing else changes. 550 * +--+--+-+-+-+-+-+-+-+-+-+-+ 551 * |11|10|9|8|7|6|5|4|3|2|1|0| 552 * +--+--+-+-+-+-+-+-+-+-+-+-+ 553 * | | | | | | | | | | | | 554 * | | | | | | | | | | | +- Home Button 555 * | | | | | | | | | | +--- Search Button 556 * | | | | | | | | | +----- Mail Button 557 * | | | | | | | | +------- Thinkpad Button 558 * | | | | | | | +--------- Zoom (Fn + Space) 559 * | | | | | | +----------- WLAN Button 560 * | | | | | +------------- Video Button 561 * | | | | +--------------- Hibernate Button 562 * | | | +----------------- Thinklight Button 563 * | | +------------------- Screen expand (Fn + F8) 564 * | +--------------------- Brightness 565 * +------------------------ Volume/Mute 566 */ 567 key = rtcin(IBM_RTC_HOTKEY1); 568 val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; 569 key = rtcin(IBM_RTC_HOTKEY2); 570 val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; 571 val |= (IBM_RTC_MASK_ZOOM & key) >> 1; 572 key = rtcin(IBM_RTC_THINKLIGHT); 573 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 574 key = rtcin(IBM_RTC_SCREENEXPAND); 575 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 576 key = rtcin(IBM_RTC_BRIGHTNESS); 577 val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; 578 key = rtcin(IBM_RTC_VOLUME); 579 val |= (IBM_RTC_MASK_VOLUME & key) << 4; 580 break; 581 582 case ACPI_IBM_METHOD_BRIGHTNESS: 583 ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 584 val = val_ec & IBM_EC_MASK_BRI; 585 break; 586 587 case ACPI_IBM_METHOD_VOLUME: 588 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 589 val = val_ec & IBM_EC_MASK_VOL; 590 break; 591 592 case ACPI_IBM_METHOD_MUTE: 593 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 594 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 595 break; 596 597 case ACPI_IBM_METHOD_THINKLIGHT: 598 if (sc->light_get_supported) 599 acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); 600 else 601 val = sc->light_val; 602 break; 603 604 case ACPI_IBM_METHOD_BLUETOOTH: 605 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 606 sc->wlan_bt_flags = val; 607 val = ((val & IBM_NAME_MASK_BT) != 0); 608 break; 609 610 case ACPI_IBM_METHOD_WLAN: 611 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 612 sc->wlan_bt_flags = val; 613 val = ((val & IBM_NAME_MASK_WLAN) != 0); 614 break; 615 616 case ACPI_IBM_METHOD_FANSPEED: 617 if (sc->fan_handle) { 618 if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) 619 val = -1; 620 } 621 else { 622 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); 623 val = val_ec; 624 } 625 break; 626 627 case ACPI_IBM_METHOD_FANLEVEL: 628 /* 629 * The IBM_EC_FANSTATUS register works as follows: 630 * Bit 0-5 indicate the level at which the fan operates. Only 631 * values between 0 and 7 have an effect. Everything 632 * above 7 is treated the same as level 7 633 * Bit 6 overrides the fan speed limit if set to 1 634 * Bit 7 indicates at which mode the fan operates: 635 * manual (0) or automatic (1) 636 */ 637 if (!sc->fan_handle) { 638 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 639 val = val_ec & IBM_EC_MASK_FANLEVEL; 640 } 641 break; 642 643 case ACPI_IBM_METHOD_FANSTATUS: 644 if (!sc->fan_handle) { 645 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 646 val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; 647 } 648 else 649 val = -1; 650 break; 651 } 652 653 return (val); 654 } 655 656 static int 657 acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) 658 { 659 int val, step; 660 UINT64 val_ec; 661 ACPI_OBJECT Arg; 662 ACPI_OBJECT_LIST Args; 663 ACPI_STATUS status; 664 665 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 666 ACPI_SERIAL_ASSERT(ibm); 667 668 switch (method) { 669 case ACPI_IBM_METHOD_EVENTS: 670 if (arg < 0 || arg > 1) 671 return (EINVAL); 672 673 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); 674 if (ACPI_FAILURE(status)) 675 return (status); 676 if (sc->events_mask_supported) 677 return acpi_ibm_eventmask_set(sc, sc->events_availmask); 678 break; 679 680 case ACPI_IBM_METHOD_EVENTMASK: 681 if (sc->events_mask_supported) 682 return acpi_ibm_eventmask_set(sc, arg); 683 break; 684 685 case ACPI_IBM_METHOD_BRIGHTNESS: 686 if (arg < 0 || arg > 7) 687 return (EINVAL); 688 689 if (sc->cmos_handle) { 690 /* Read the current brightness */ 691 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 692 if (ACPI_FAILURE(status)) 693 return (status); 694 val = val_ec & IBM_EC_MASK_BRI; 695 696 Args.Count = 1; 697 Args.Pointer = &Arg; 698 Arg.Type = ACPI_TYPE_INTEGER; 699 Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN; 700 701 step = (arg > val) ? 1 : -1; 702 for (int i = val; i != arg; i += step) { 703 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 704 if (ACPI_FAILURE(status)) 705 break; 706 } 707 } 708 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); 709 break; 710 711 case ACPI_IBM_METHOD_VOLUME: 712 if (arg < 0 || arg > 14) 713 return (EINVAL); 714 715 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 716 if (ACPI_FAILURE(status)) 717 return (status); 718 719 if (sc->cmos_handle) { 720 val = val_ec & IBM_EC_MASK_VOL; 721 722 Args.Count = 1; 723 Args.Pointer = &Arg; 724 Arg.Type = ACPI_TYPE_INTEGER; 725 Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN; 726 727 step = (arg > val) ? 1 : -1; 728 for (int i = val; i != arg; i += step) { 729 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 730 if (ACPI_FAILURE(status)) 731 break; 732 } 733 } 734 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1); 735 break; 736 737 case ACPI_IBM_METHOD_MUTE: 738 if (arg < 0 || arg > 1) 739 return (EINVAL); 740 741 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 742 if (ACPI_FAILURE(status)) 743 return (status); 744 745 if (sc->cmos_handle) { 746 Args.Count = 1; 747 Args.Pointer = &Arg; 748 Arg.Type = ACPI_TYPE_INTEGER; 749 Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; 750 751 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 752 if (ACPI_FAILURE(status)) 753 break; 754 } 755 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1); 756 break; 757 758 case ACPI_IBM_METHOD_THINKLIGHT: 759 if (arg < 0 || arg > 1) 760 return (EINVAL); 761 762 if (sc->light_set_supported) { 763 Args.Count = 1; 764 Args.Pointer = &Arg; 765 Arg.Type = ACPI_TYPE_INTEGER; 766 Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; 767 768 status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL); 769 if (ACPI_SUCCESS(status)) 770 sc->light_val = arg; 771 return (status); 772 } 773 break; 774 775 case ACPI_IBM_METHOD_BLUETOOTH: 776 if (arg < 0 || arg > 1) 777 return (EINVAL); 778 779 val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); 780 return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); 781 break; 782 783 case ACPI_IBM_METHOD_FANLEVEL: 784 if (arg < 0 || arg > 7) 785 return (EINVAL); 786 787 if (!sc->fan_handle) { 788 /* Read the current fanstatus */ 789 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 790 val = val_ec & (~IBM_EC_MASK_FANLEVEL); 791 792 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); 793 } 794 break; 795 796 case ACPI_IBM_METHOD_FANSTATUS: 797 if (arg < 0 || arg > 1) 798 return (EINVAL); 799 800 if (!sc->fan_handle) { 801 /* Read the current fanstatus */ 802 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 803 804 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, 805 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); 806 } 807 break; 808 } 809 810 return (0); 811 } 812 813 static int 814 acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) 815 { 816 int dummy; 817 ACPI_OBJECT_TYPE cmos_t; 818 ACPI_HANDLE ledb_handle; 819 820 switch (method) { 821 case ACPI_IBM_METHOD_EVENTS: 822 /* Events are disabled by default */ 823 return (TRUE); 824 825 case ACPI_IBM_METHOD_EVENTMASK: 826 return (sc->events_mask_supported); 827 828 case ACPI_IBM_METHOD_HOTKEY: 829 case ACPI_IBM_METHOD_BRIGHTNESS: 830 case ACPI_IBM_METHOD_VOLUME: 831 case ACPI_IBM_METHOD_MUTE: 832 /* EC is required here, which was aready checked before */ 833 return (TRUE); 834 835 case ACPI_IBM_METHOD_THINKLIGHT: 836 sc->cmos_handle = NULL; 837 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 838 sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); 839 840 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || 841 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || 842 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && 843 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 844 cmos_t == ACPI_TYPE_METHOD) { 845 sc->light_cmd_on = 0x0c; 846 sc->light_cmd_off = 0x0d; 847 sc->cmos_handle = sc->light_handle; 848 } 849 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { 850 sc->light_cmd_on = 1; 851 sc->light_cmd_off = 0; 852 } 853 else 854 sc->light_handle = NULL; 855 856 sc->light_set_supported = (sc->light_handle && 857 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); 858 859 if (sc->light_get_supported) 860 return (TRUE); 861 862 if (sc->light_set_supported) { 863 sc->light_val = 0; 864 return (TRUE); 865 } 866 867 return (FALSE); 868 869 case ACPI_IBM_METHOD_BLUETOOTH: 870 case ACPI_IBM_METHOD_WLAN: 871 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) 872 return (TRUE); 873 return (FALSE); 874 875 case ACPI_IBM_METHOD_FANSPEED: 876 /* 877 * Some models report the fan speed in levels from 0-7 878 * Newer models report it contiguously 879 */ 880 sc->fan_levels = 881 (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || 882 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); 883 return (TRUE); 884 885 case ACPI_IBM_METHOD_FANLEVEL: 886 case ACPI_IBM_METHOD_FANSTATUS: 887 /* 888 * Fan status is only supported on those models, 889 * which report fan RPM contiguously, not in levels 890 */ 891 if (sc->fan_levels) 892 return (FALSE); 893 return (TRUE); 894 895 case ACPI_IBM_METHOD_THERMAL: 896 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { 897 sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); 898 return (TRUE); 899 } 900 return (FALSE); 901 } 902 return (FALSE); 903 } 904 905 static int 906 acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) 907 { 908 struct acpi_ibm_softc *sc; 909 int error = 0; 910 char temp_cmd[] = "TMP0"; 911 int temp[8]; 912 913 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 914 915 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 916 917 ACPI_SERIAL_BEGIN(ibm); 918 919 for (int i = 0; i < 8; ++i) { 920 temp_cmd[3] = '0' + i; 921 922 /* 923 * The TMPx methods seem to return +/- 128 or 0 924 * when the respecting sensor is not available 925 */ 926 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 927 &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) 928 temp[i] = -1; 929 else if (sc->thermal_updt_supported) 930 /* Temperature is reported in tenth of Kelvin */ 931 temp[i] = (temp[i] - 2732 + 5) / 10; 932 } 933 934 error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); 935 936 ACPI_SERIAL_END(ibm); 937 return (error); 938 } 939 940 static void 941 acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) 942 { 943 int event, arg, type; 944 device_t dev = context; 945 struct acpi_ibm_softc *sc = device_get_softc(dev); 946 947 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 948 949 if (notify != 0x80) 950 device_printf(dev, "Unknown notify\n"); 951 952 for (;;) { 953 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); 954 955 if (event == 0) 956 break; 957 958 959 type = (event >> 12) & 0xf; 960 arg = event & 0xfff; 961 switch (type) { 962 case 1: 963 if (!(sc->events_availmask & (1 << (arg - 1)))) { 964 device_printf(dev, "Unknown key %d\n", arg); 965 break; 966 } 967 968 /* Notify devd(8) */ 969 acpi_UserNotify("IBM", h, (arg & 0xff)); 970 break; 971 default: 972 break; 973 } 974 } 975 } 976