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