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 259 static void ibm_led(void *softc, int onoff); 260 static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); 261 262 static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); 263 static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); 264 static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); 265 static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); 266 267 static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); 268 static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); 269 static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); 270 271 static device_method_t acpi_ibm_methods[] = { 272 /* Device interface */ 273 DEVMETHOD(device_probe, acpi_ibm_probe), 274 DEVMETHOD(device_attach, acpi_ibm_attach), 275 DEVMETHOD(device_detach, acpi_ibm_detach), 276 277 {0, 0} 278 }; 279 280 static driver_t acpi_ibm_driver = { 281 "acpi_ibm", 282 acpi_ibm_methods, 283 sizeof(struct acpi_ibm_softc), 284 }; 285 286 static devclass_t acpi_ibm_devclass; 287 288 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 289 0, 0); 290 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 291 static char *ibm_ids[] = {"IBM0068", NULL}; 292 293 static void 294 ibm_led(void *softc, int onoff) 295 { 296 struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; 297 298 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 299 300 if (sc->led_busy) 301 return; 302 303 sc->led_busy = 1; 304 sc->led_state = onoff; 305 306 AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); 307 } 308 309 static void 310 ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) 311 { 312 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 313 314 ACPI_SERIAL_BEGIN(ibm); 315 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); 316 ACPI_SERIAL_END(ibm); 317 318 sc->led_busy = 0; 319 } 320 321 static int 322 acpi_ibm_probe(device_t dev) 323 { 324 if (acpi_disabled("ibm") || 325 ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || 326 device_get_unit(dev) != 0) 327 return (ENXIO); 328 329 device_set_desc(dev, "IBM ThinkPad ACPI Extras"); 330 331 return (0); 332 } 333 334 static int 335 acpi_ibm_attach(device_t dev) 336 { 337 struct acpi_ibm_softc *sc; 338 devclass_t ec_devclass; 339 340 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 341 342 sc = device_get_softc(dev); 343 sc->dev = dev; 344 sc->handle = acpi_get_handle(dev); 345 346 /* Look for the first embedded controller */ 347 if (!(ec_devclass = devclass_find ("acpi_ec"))) { 348 if (bootverbose) 349 device_printf(dev, "Couldn't find acpi_ec devclass\n"); 350 return (EINVAL); 351 } 352 if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { 353 if (bootverbose) 354 device_printf(dev, "Couldn't find acpi_ec device\n"); 355 return (EINVAL); 356 } 357 sc->ec_handle = acpi_get_handle(sc->ec_dev); 358 359 /* Get the sysctl tree */ 360 sc->sysctl_ctx = device_get_sysctl_ctx(dev); 361 sc->sysctl_tree = device_get_sysctl_tree(dev); 362 363 /* Look for event mask and hook up the nodes */ 364 sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, 365 IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); 366 367 if (sc->events_mask_supported) { 368 SYSCTL_ADD_INT(sc->sysctl_ctx, 369 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 370 "initialmask", CTLFLAG_RD, 371 &sc->events_initialmask, 0, "Initial eventmask"); 372 373 /* The availmask is the bitmask of supported events */ 374 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 375 IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) 376 sc->events_availmask = 0xffffffff; 377 378 SYSCTL_ADD_INT(sc->sysctl_ctx, 379 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 380 "availmask", CTLFLAG_RD, 381 &sc->events_availmask, 0, "Mask of supported events"); 382 } 383 384 /* Hook up proc nodes */ 385 for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 386 if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) 387 continue; 388 389 SYSCTL_ADD_PROC(sc->sysctl_ctx, 390 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 391 acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, 392 sc, i, acpi_ibm_sysctl, "I", 393 acpi_ibm_sysctls[i].description); 394 } 395 396 /* Hook up thermal node */ 397 if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) { 398 SYSCTL_ADD_PROC(sc->sysctl_ctx, 399 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 400 "thermal", CTLTYPE_STRING | CTLFLAG_RD, 401 sc, 0, acpi_ibm_thermal_sysctl, "I", 402 "Thermal zones"); 403 } 404 405 /* Handle notifies */ 406 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 407 acpi_ibm_notify, dev); 408 409 /* Hook up light to led(4) */ 410 if (sc->light_set_supported) 411 sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); 412 413 return (0); 414 } 415 416 static int 417 acpi_ibm_detach(device_t dev) 418 { 419 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 420 421 struct acpi_ibm_softc *sc = device_get_softc(dev); 422 423 /* Disable events and restore eventmask */ 424 ACPI_SERIAL_BEGIN(ibm); 425 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); 426 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); 427 ACPI_SERIAL_END(ibm); 428 429 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); 430 431 if (sc->led_dev != NULL) 432 led_destroy(sc->led_dev); 433 434 return (0); 435 } 436 437 static int 438 acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) 439 { 440 ACPI_OBJECT arg[2]; 441 ACPI_OBJECT_LIST args; 442 ACPI_STATUS status; 443 444 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 445 ACPI_SERIAL_ASSERT(ibm); 446 447 args.Count = 2; 448 args.Pointer = arg; 449 arg[0].Type = ACPI_TYPE_INTEGER; 450 arg[1].Type = ACPI_TYPE_INTEGER; 451 452 for (int i = 0; i < 32; ++i) { 453 arg[0].Integer.Value = i+1; 454 arg[1].Integer.Value = (((1 << i) & val) != 0); 455 status = AcpiEvaluateObject(sc->handle, 456 IBM_NAME_EVENTS_MASK_SET, &args, NULL); 457 458 if (ACPI_FAILURE(status)) 459 return (status); 460 } 461 462 return (0); 463 } 464 465 static int 466 acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) 467 { 468 struct acpi_ibm_softc *sc; 469 int arg; 470 int error = 0; 471 int function; 472 int method; 473 474 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 475 476 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 477 function = oidp->oid_arg2; 478 method = acpi_ibm_sysctls[function].method; 479 480 ACPI_SERIAL_BEGIN(ibm); 481 arg = acpi_ibm_sysctl_get(sc, method); 482 error = sysctl_handle_int(oidp, &arg, 0, req); 483 484 /* Sanity check */ 485 if (error != 0 || req->newptr == NULL) 486 goto out; 487 488 /* Update */ 489 error = acpi_ibm_sysctl_set(sc, method, arg); 490 491 out: 492 ACPI_SERIAL_END(ibm); 493 return (error); 494 } 495 496 static int 497 acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) 498 { 499 ACPI_INTEGER val_ec; 500 int val = 0, key; 501 502 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 503 ACPI_SERIAL_ASSERT(ibm); 504 505 switch (method) { 506 case ACPI_IBM_METHOD_EVENTS: 507 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); 508 break; 509 510 case ACPI_IBM_METHOD_EVENTMASK: 511 if (sc->events_mask_supported) 512 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); 513 break; 514 515 case ACPI_IBM_METHOD_HOTKEY: 516 /* 517 * Construct the hotkey as a bitmask as illustrated below. 518 * Note that whenever a key was pressed, the respecting bit 519 * toggles and nothing else changes. 520 * +--+--+-+-+-+-+-+-+-+-+-+-+ 521 * |11|10|9|8|7|6|5|4|3|2|1|0| 522 * +--+--+-+-+-+-+-+-+-+-+-+-+ 523 * | | | | | | | | | | | | 524 * | | | | | | | | | | | +- Home Button 525 * | | | | | | | | | | +--- Search Button 526 * | | | | | | | | | +----- Mail Button 527 * | | | | | | | | +------- Thinkpad Button 528 * | | | | | | | +--------- Zoom (Fn + Space) 529 * | | | | | | +----------- WLAN Button 530 * | | | | | +------------- Video Button 531 * | | | | +--------------- Hibernate Button 532 * | | | +----------------- Thinklight Button 533 * | | +------------------- Screen expand (Fn + F8) 534 * | +--------------------- Brightness 535 * +------------------------ Volume/Mute 536 */ 537 key = rtcin(IBM_RTC_HOTKEY1); 538 val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; 539 key = rtcin(IBM_RTC_HOTKEY2); 540 val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; 541 val |= (IBM_RTC_MASK_ZOOM & key) >> 1; 542 key = rtcin(IBM_RTC_THINKLIGHT); 543 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 544 key = rtcin(IBM_RTC_SCREENEXPAND); 545 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 546 key = rtcin(IBM_RTC_BRIGHTNESS); 547 val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; 548 key = rtcin(IBM_RTC_VOLUME); 549 val |= (IBM_RTC_MASK_VOLUME & key) << 4; 550 break; 551 552 case ACPI_IBM_METHOD_BRIGHTNESS: 553 ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 554 val = val_ec & IBM_EC_MASK_BRI; 555 break; 556 557 case ACPI_IBM_METHOD_VOLUME: 558 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 559 val = val_ec & IBM_EC_MASK_VOL; 560 break; 561 562 case ACPI_IBM_METHOD_MUTE: 563 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 564 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 565 break; 566 567 case ACPI_IBM_METHOD_THINKLIGHT: 568 if (sc->light_get_supported) 569 acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); 570 else 571 val = sc->light_val; 572 break; 573 574 case ACPI_IBM_METHOD_BLUETOOTH: 575 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 576 sc->wlan_bt_flags = val; 577 val = ((val & IBM_NAME_MASK_BT) != 0); 578 break; 579 580 case ACPI_IBM_METHOD_WLAN: 581 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 582 sc->wlan_bt_flags = val; 583 val = ((val & IBM_NAME_MASK_WLAN) != 0); 584 break; 585 586 case ACPI_IBM_METHOD_FANSPEED: 587 if (sc->fan_handle) { 588 if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) 589 val = -1; 590 } 591 else { 592 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); 593 val = val_ec; 594 } 595 break; 596 597 case ACPI_IBM_METHOD_FANLEVEL: 598 /* 599 * The IBM_EC_FANSTATUS register works as follows: 600 * Bit 0-5 indicate the level at which the fan operates. Only 601 * values between 0 and 7 have an effect. Everything 602 * above 7 is treated the same as level 7 603 * Bit 6 overrides the fan speed limit if set to 1 604 * Bit 7 indicates at which mode the fan operates: 605 * manual (0) or automatic (1) 606 */ 607 if (!sc->fan_handle) { 608 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 609 val = val_ec & IBM_EC_MASK_FANLEVEL; 610 } 611 break; 612 613 case ACPI_IBM_METHOD_FANSTATUS: 614 if (!sc->fan_handle) { 615 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 616 val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; 617 } 618 else 619 val = -1; 620 break; 621 } 622 623 return (val); 624 } 625 626 static int 627 acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) 628 { 629 int val, step; 630 ACPI_INTEGER val_ec; 631 ACPI_OBJECT Arg; 632 ACPI_OBJECT_LIST Args; 633 ACPI_STATUS status; 634 635 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 636 ACPI_SERIAL_ASSERT(ibm); 637 638 switch (method) { 639 case ACPI_IBM_METHOD_EVENTS: 640 if (arg < 0 || arg > 1) 641 return (EINVAL); 642 643 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); 644 if (ACPI_FAILURE(status)) 645 return (status); 646 if (sc->events_mask_supported) 647 return acpi_ibm_eventmask_set(sc, sc->events_availmask); 648 break; 649 650 case ACPI_IBM_METHOD_EVENTMASK: 651 if (sc->events_mask_supported) 652 return acpi_ibm_eventmask_set(sc, arg); 653 break; 654 655 case ACPI_IBM_METHOD_BRIGHTNESS: 656 if (arg < 0 || arg > 7) 657 return (EINVAL); 658 659 if (sc->cmos_handle) { 660 /* Read the current brightness */ 661 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 662 if (ACPI_FAILURE(status)) 663 return (status); 664 val = val_ec & IBM_EC_MASK_BRI; 665 666 Args.Count = 1; 667 Args.Pointer = &Arg; 668 Arg.Type = ACPI_TYPE_INTEGER; 669 Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN; 670 671 step = (arg > val) ? 1 : -1; 672 for (int i = val; i != arg; i += step) { 673 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 674 if (ACPI_FAILURE(status)) 675 break; 676 } 677 } 678 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); 679 break; 680 681 case ACPI_IBM_METHOD_VOLUME: 682 if (arg < 0 || arg > 14) 683 return (EINVAL); 684 685 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 686 if (ACPI_FAILURE(status)) 687 return (status); 688 689 if (sc->cmos_handle) { 690 val = val_ec & IBM_EC_MASK_VOL; 691 692 Args.Count = 1; 693 Args.Pointer = &Arg; 694 Arg.Type = ACPI_TYPE_INTEGER; 695 Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN; 696 697 step = (arg > val) ? 1 : -1; 698 for (int i = val; i != arg; i += step) { 699 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 700 if (ACPI_FAILURE(status)) 701 break; 702 } 703 } 704 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1); 705 break; 706 707 case ACPI_IBM_METHOD_MUTE: 708 if (arg < 0 || arg > 1) 709 return (EINVAL); 710 711 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 712 if (ACPI_FAILURE(status)) 713 return (status); 714 715 if (sc->cmos_handle) { 716 val = val_ec & IBM_EC_MASK_VOL; 717 718 Args.Count = 1; 719 Args.Pointer = &Arg; 720 Arg.Type = ACPI_TYPE_INTEGER; 721 Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; 722 723 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 724 if (ACPI_FAILURE(status)) 725 break; 726 } 727 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); 728 break; 729 730 case ACPI_IBM_METHOD_THINKLIGHT: 731 if (arg < 0 || arg > 1) 732 return (EINVAL); 733 734 if (sc->light_set_supported) { 735 Args.Count = 1; 736 Args.Pointer = &Arg; 737 Arg.Type = ACPI_TYPE_INTEGER; 738 Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; 739 740 status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL); 741 if (ACPI_SUCCESS(status)) 742 sc->light_val = arg; 743 return (status); 744 } 745 break; 746 747 case ACPI_IBM_METHOD_BLUETOOTH: 748 if (arg < 0 || arg > 1) 749 return (EINVAL); 750 751 val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); 752 return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); 753 break; 754 755 case ACPI_IBM_METHOD_FANLEVEL: 756 if (arg < 0 || arg > 7) 757 return (EINVAL); 758 759 if (!sc->fan_handle) { 760 /* Read the current fanstatus */ 761 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 762 val = val_ec & (~IBM_EC_MASK_FANLEVEL); 763 764 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); 765 } 766 break; 767 768 case ACPI_IBM_METHOD_FANSTATUS: 769 if (arg < 0 || arg > 1) 770 return (EINVAL); 771 772 if (!sc->fan_handle) { 773 /* Read the current fanstatus */ 774 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 775 776 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, 777 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); 778 } 779 break; 780 } 781 782 return (0); 783 } 784 785 static int 786 acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) 787 { 788 int dummy; 789 ACPI_OBJECT_TYPE cmos_t; 790 ACPI_HANDLE ledb_handle; 791 792 switch (method) { 793 case ACPI_IBM_METHOD_EVENTS: 794 /* Events are disabled by default */ 795 return (TRUE); 796 797 case ACPI_IBM_METHOD_EVENTMASK: 798 return (sc->events_mask_supported); 799 800 case ACPI_IBM_METHOD_HOTKEY: 801 case ACPI_IBM_METHOD_BRIGHTNESS: 802 case ACPI_IBM_METHOD_VOLUME: 803 case ACPI_IBM_METHOD_MUTE: 804 /* EC is required here, which was aready checked before */ 805 return (TRUE); 806 807 case ACPI_IBM_METHOD_THINKLIGHT: 808 sc->cmos_handle = NULL; 809 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 810 sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); 811 812 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || 813 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || 814 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && 815 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 816 cmos_t == ACPI_TYPE_METHOD) { 817 sc->light_cmd_on = 0x0c; 818 sc->light_cmd_off = 0x0d; 819 sc->cmos_handle = sc->light_handle; 820 } 821 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { 822 sc->light_cmd_on = 1; 823 sc->light_cmd_off = 0; 824 } 825 else 826 sc->light_handle = NULL; 827 828 sc->light_set_supported = (sc->light_handle && 829 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); 830 831 if (sc->light_get_supported) 832 return (TRUE); 833 834 if (sc->light_set_supported) { 835 sc->light_val = 0; 836 return (TRUE); 837 } 838 839 return (FALSE); 840 841 case ACPI_IBM_METHOD_BLUETOOTH: 842 case ACPI_IBM_METHOD_WLAN: 843 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) 844 return (TRUE); 845 return (FALSE); 846 847 case ACPI_IBM_METHOD_FANSPEED: 848 /* 849 * Some models report the fan speed in levels from 0-7 850 * Newer models report it contiguously 851 */ 852 sc->fan_levels = 853 (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || 854 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); 855 return (TRUE); 856 857 case ACPI_IBM_METHOD_FANLEVEL: 858 case ACPI_IBM_METHOD_FANSTATUS: 859 /* 860 * Fan status is only supported on those models, 861 * which report fan RPM contiguously, not in levels 862 */ 863 if (sc->fan_levels) 864 return (FALSE); 865 return (TRUE); 866 867 case ACPI_IBM_METHOD_THERMAL: 868 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { 869 sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); 870 return (TRUE); 871 } 872 return (FALSE); 873 } 874 return (FALSE); 875 } 876 877 static int 878 acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) 879 { 880 struct acpi_ibm_softc *sc; 881 int error = 0; 882 char temp_cmd[] = "TMP0"; 883 int temp[8]; 884 885 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 886 887 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 888 889 ACPI_SERIAL_BEGIN(ibm); 890 891 for (int i = 0; i < 8; ++i) { 892 temp_cmd[3] = '0' + i; 893 894 /* 895 * The TMPx methods seem to return +/- 128 or 0 896 * when the respecting sensor is not available 897 */ 898 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 899 &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) 900 temp[i] = -1; 901 else if (sc->thermal_updt_supported) 902 /* Temperature is reported in tenth of Kelvin */ 903 temp[i] = (temp[i] - 2732 + 5) / 10; 904 } 905 906 error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); 907 908 ACPI_SERIAL_END(ibm); 909 return (error); 910 } 911 912 static void 913 acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) 914 { 915 int event, arg, type; 916 device_t dev = context; 917 struct acpi_ibm_softc *sc = device_get_softc(dev); 918 919 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 920 921 if (notify != 0x80) 922 device_printf(dev, "Unknown notify\n"); 923 924 for (;;) { 925 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); 926 927 if (event == 0) 928 break; 929 930 931 type = (event >> 12) & 0xf; 932 arg = event & 0xfff; 933 switch (type) { 934 case 1: 935 if (!(sc->events_availmask & (1 << (arg - 1)))) { 936 device_printf(dev, "Unknown key %d\n", arg); 937 break; 938 } 939 940 /* Notify devd(8) */ 941 acpi_UserNotify("IBM", h, (arg & 0xff)); 942 break; 943 default: 944 break; 945 } 946 } 947 } 948