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