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