acpi_ibm.c revision 283360
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 283360 2015-05-24 07:45:42Z ganbold $"); 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/power.h> 54#include <sys/sbuf.h> 55#include <sys/sysctl.h> 56#include <isa/rtc.h> 57 58#define _COMPONENT ACPI_OEM 59ACPI_MODULE_NAME("IBM") 60 61/* Internal methods */ 62#define ACPI_IBM_METHOD_EVENTS 1 63#define ACPI_IBM_METHOD_EVENTMASK 2 64#define ACPI_IBM_METHOD_HOTKEY 3 65#define ACPI_IBM_METHOD_BRIGHTNESS 4 66#define ACPI_IBM_METHOD_VOLUME 5 67#define ACPI_IBM_METHOD_MUTE 6 68#define ACPI_IBM_METHOD_THINKLIGHT 7 69#define ACPI_IBM_METHOD_BLUETOOTH 8 70#define ACPI_IBM_METHOD_WLAN 9 71#define ACPI_IBM_METHOD_FANSPEED 10 72#define ACPI_IBM_METHOD_FANLEVEL 11 73#define ACPI_IBM_METHOD_FANSTATUS 12 74#define ACPI_IBM_METHOD_THERMAL 13 75#define ACPI_IBM_METHOD_HANDLEREVENTS 14 76 77/* Hotkeys/Buttons */ 78#define IBM_RTC_HOTKEY1 0x64 79#define IBM_RTC_MASK_HOME (1 << 0) 80#define IBM_RTC_MASK_SEARCH (1 << 1) 81#define IBM_RTC_MASK_MAIL (1 << 2) 82#define IBM_RTC_MASK_WLAN (1 << 5) 83#define IBM_RTC_HOTKEY2 0x65 84#define IBM_RTC_MASK_THINKPAD (1 << 3) 85#define IBM_RTC_MASK_ZOOM (1 << 5) 86#define IBM_RTC_MASK_VIDEO (1 << 6) 87#define IBM_RTC_MASK_HIBERNATE (1 << 7) 88#define IBM_RTC_THINKLIGHT 0x66 89#define IBM_RTC_MASK_THINKLIGHT (1 << 4) 90#define IBM_RTC_SCREENEXPAND 0x67 91#define IBM_RTC_MASK_SCREENEXPAND (1 << 5) 92#define IBM_RTC_BRIGHTNESS 0x6c 93#define IBM_RTC_MASK_BRIGHTNESS (1 << 5) 94#define IBM_RTC_VOLUME 0x6e 95#define IBM_RTC_MASK_VOLUME (1 << 7) 96 97/* Embedded Controller registers */ 98#define IBM_EC_BRIGHTNESS 0x31 99#define IBM_EC_MASK_BRI 0x7 100#define IBM_EC_VOLUME 0x30 101#define IBM_EC_MASK_VOL 0xf 102#define IBM_EC_MASK_MUTE (1 << 6) 103#define IBM_EC_FANSTATUS 0x2F 104#define IBM_EC_MASK_FANLEVEL 0x3f 105#define IBM_EC_MASK_FANDISENGAGED (1 << 6) 106#define IBM_EC_MASK_FANSTATUS (1 << 7) 107#define IBM_EC_FANSPEED 0x84 108 109/* CMOS Commands */ 110#define IBM_CMOS_VOLUME_DOWN 0 111#define IBM_CMOS_VOLUME_UP 1 112#define IBM_CMOS_VOLUME_MUTE 2 113#define IBM_CMOS_BRIGHTNESS_UP 4 114#define IBM_CMOS_BRIGHTNESS_DOWN 5 115 116/* ACPI methods */ 117#define IBM_NAME_KEYLIGHT "KBLT" 118#define IBM_NAME_WLAN_BT_GET "GBDC" 119#define IBM_NAME_WLAN_BT_SET "SBDC" 120#define IBM_NAME_MASK_BT (1 << 1) 121#define IBM_NAME_MASK_WLAN (1 << 2) 122#define IBM_NAME_THERMAL_GET "TMP7" 123#define IBM_NAME_THERMAL_UPDT "UPDT" 124 125#define IBM_NAME_EVENTS_STATUS_GET "DHKC" 126#define IBM_NAME_EVENTS_MASK_GET "DHKN" 127#define IBM_NAME_EVENTS_STATUS_SET "MHKC" 128#define IBM_NAME_EVENTS_MASK_SET "MHKM" 129#define IBM_NAME_EVENTS_GET "MHKP" 130#define IBM_NAME_EVENTS_AVAILMASK "MHKA" 131 132/* Event Code */ 133#define IBM_EVENT_LCD_BACKLIGHT 0x03 134#define IBM_EVENT_SUSPEND_TO_RAM 0x04 135#define IBM_EVENT_BLUETOOTH 0x05 136#define IBM_EVENT_SCREEN_EXPAND 0x07 137#define IBM_EVENT_SUSPEND_TO_DISK 0x0c 138#define IBM_EVENT_BRIGHTNESS_UP 0x10 139#define IBM_EVENT_BRIGHTNESS_DOWN 0x11 140#define IBM_EVENT_THINKLIGHT 0x12 141#define IBM_EVENT_ZOOM 0x14 142#define IBM_EVENT_VOLUME_UP 0x15 143#define IBM_EVENT_VOLUME_DOWN 0x16 144#define IBM_EVENT_MUTE 0x17 145#define IBM_EVENT_ACCESS_IBM_BUTTON 0x18 146 147#define ABS(x) (((x) < 0)? -(x) : (x)) 148 149struct acpi_ibm_softc { 150 device_t dev; 151 ACPI_HANDLE handle; 152 153 /* Embedded controller */ 154 device_t ec_dev; 155 ACPI_HANDLE ec_handle; 156 157 /* CMOS */ 158 ACPI_HANDLE cmos_handle; 159 160 /* Fan status */ 161 ACPI_HANDLE fan_handle; 162 int fan_levels; 163 164 /* Keylight commands and states */ 165 ACPI_HANDLE light_handle; 166 int light_cmd_on; 167 int light_cmd_off; 168 int light_val; 169 int light_get_supported; 170 int light_set_supported; 171 172 /* led(4) interface */ 173 struct cdev *led_dev; 174 int led_busy; 175 int led_state; 176 177 int wlan_bt_flags; 178 int thermal_updt_supported; 179 180 unsigned int events_availmask; 181 unsigned int events_initialmask; 182 int events_mask_supported; 183 int events_enable; 184 185 unsigned int handler_events; 186 187 struct sysctl_ctx_list *sysctl_ctx; 188 struct sysctl_oid *sysctl_tree; 189}; 190 191static struct { 192 char *name; 193 int method; 194 char *description; 195 int flag_rdonly; 196} acpi_ibm_sysctls[] = { 197 { 198 .name = "events", 199 .method = ACPI_IBM_METHOD_EVENTS, 200 .description = "ACPI events enable", 201 }, 202 { 203 .name = "eventmask", 204 .method = ACPI_IBM_METHOD_EVENTMASK, 205 .description = "ACPI eventmask", 206 }, 207 { 208 .name = "hotkey", 209 .method = ACPI_IBM_METHOD_HOTKEY, 210 .description = "Key Status", 211 .flag_rdonly = 1 212 }, 213 { 214 .name = "lcd_brightness", 215 .method = ACPI_IBM_METHOD_BRIGHTNESS, 216 .description = "LCD Brightness", 217 }, 218 { 219 .name = "volume", 220 .method = ACPI_IBM_METHOD_VOLUME, 221 .description = "Volume", 222 }, 223 { 224 .name = "mute", 225 .method = ACPI_IBM_METHOD_MUTE, 226 .description = "Mute", 227 }, 228 { 229 .name = "thinklight", 230 .method = ACPI_IBM_METHOD_THINKLIGHT, 231 .description = "Thinklight enable", 232 }, 233 { 234 .name = "bluetooth", 235 .method = ACPI_IBM_METHOD_BLUETOOTH, 236 .description = "Bluetooth enable", 237 }, 238 { 239 .name = "wlan", 240 .method = ACPI_IBM_METHOD_WLAN, 241 .description = "WLAN enable", 242 .flag_rdonly = 1 243 }, 244 { 245 .name = "fan_speed", 246 .method = ACPI_IBM_METHOD_FANSPEED, 247 .description = "Fan speed", 248 .flag_rdonly = 1 249 }, 250 { 251 .name = "fan_level", 252 .method = ACPI_IBM_METHOD_FANLEVEL, 253 .description = "Fan level", 254 }, 255 { 256 .name = "fan", 257 .method = ACPI_IBM_METHOD_FANSTATUS, 258 .description = "Fan enable", 259 }, 260 261 { NULL, 0, NULL, 0 } 262}; 263 264ACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); 265 266static int acpi_ibm_probe(device_t dev); 267static int acpi_ibm_attach(device_t dev); 268static int acpi_ibm_detach(device_t dev); 269static int acpi_ibm_resume(device_t dev); 270 271static void ibm_led(void *softc, int onoff); 272static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); 273 274static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); 275static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); 276static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); 277static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); 278 279static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); 280static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); 281static int acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS); 282static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); 283 284static int acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg); 285static int acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg); 286static int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg); 287static int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg); 288static int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg); 289 290static device_method_t acpi_ibm_methods[] = { 291 /* Device interface */ 292 DEVMETHOD(device_probe, acpi_ibm_probe), 293 DEVMETHOD(device_attach, acpi_ibm_attach), 294 DEVMETHOD(device_detach, acpi_ibm_detach), 295 DEVMETHOD(device_resume, acpi_ibm_resume), 296 297 DEVMETHOD_END 298}; 299 300static driver_t acpi_ibm_driver = { 301 "acpi_ibm", 302 acpi_ibm_methods, 303 sizeof(struct acpi_ibm_softc), 304}; 305 306static devclass_t acpi_ibm_devclass; 307 308DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 309 0, 0); 310MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 311static char *ibm_ids[] = {"IBM0068", "LEN0068", NULL}; 312 313static void 314ibm_led(void *softc, int onoff) 315{ 316 struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; 317 318 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 319 320 if (sc->led_busy) 321 return; 322 323 sc->led_busy = 1; 324 sc->led_state = onoff; 325 326 AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); 327} 328 329static void 330ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) 331{ 332 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 333 334 ACPI_SERIAL_BEGIN(ibm); 335 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); 336 ACPI_SERIAL_END(ibm); 337 338 sc->led_busy = 0; 339} 340 341static int 342acpi_ibm_probe(device_t dev) 343{ 344 if (acpi_disabled("ibm") || 345 ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || 346 device_get_unit(dev) != 0) 347 return (ENXIO); 348 349 device_set_desc(dev, "IBM ThinkPad ACPI Extras"); 350 351 return (0); 352} 353 354static int 355acpi_ibm_attach(device_t dev) 356{ 357 struct acpi_ibm_softc *sc; 358 devclass_t ec_devclass; 359 360 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 361 362 sc = device_get_softc(dev); 363 sc->dev = dev; 364 sc->handle = acpi_get_handle(dev); 365 366 /* Look for the first embedded controller */ 367 if (!(ec_devclass = devclass_find ("acpi_ec"))) { 368 if (bootverbose) 369 device_printf(dev, "Couldn't find acpi_ec devclass\n"); 370 return (EINVAL); 371 } 372 if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { 373 if (bootverbose) 374 device_printf(dev, "Couldn't find acpi_ec device\n"); 375 return (EINVAL); 376 } 377 sc->ec_handle = acpi_get_handle(sc->ec_dev); 378 379 /* Get the sysctl tree */ 380 sc->sysctl_ctx = device_get_sysctl_ctx(dev); 381 sc->sysctl_tree = device_get_sysctl_tree(dev); 382 383 /* Look for event mask and hook up the nodes */ 384 sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, 385 IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); 386 387 if (sc->events_mask_supported) { 388 SYSCTL_ADD_UINT(sc->sysctl_ctx, 389 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 390 "initialmask", CTLFLAG_RD, 391 &sc->events_initialmask, 0, "Initial eventmask"); 392 393 /* The availmask is the bitmask of supported events */ 394 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 395 IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) 396 sc->events_availmask = 0xffffffff; 397 398 SYSCTL_ADD_UINT(sc->sysctl_ctx, 399 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 400 "availmask", CTLFLAG_RD, 401 &sc->events_availmask, 0, "Mask of supported events"); 402 } 403 404 /* Hook up proc nodes */ 405 for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 406 if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) 407 continue; 408 409 if (acpi_ibm_sysctls[i].flag_rdonly != 0) { 410 SYSCTL_ADD_PROC(sc->sysctl_ctx, 411 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 412 acpi_ibm_sysctls[i].name, CTLTYPE_INT | CTLFLAG_RD, 413 sc, i, acpi_ibm_sysctl, "I", 414 acpi_ibm_sysctls[i].description); 415 } else { 416 SYSCTL_ADD_PROC(sc->sysctl_ctx, 417 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 418 acpi_ibm_sysctls[i].name, CTLTYPE_INT | CTLFLAG_RW, 419 sc, i, acpi_ibm_sysctl, "I", 420 acpi_ibm_sysctls[i].description); 421 } 422 } 423 424 /* Hook up thermal node */ 425 if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) { 426 SYSCTL_ADD_PROC(sc->sysctl_ctx, 427 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 428 "thermal", CTLTYPE_INT | CTLFLAG_RD, 429 sc, 0, acpi_ibm_thermal_sysctl, "I", 430 "Thermal zones"); 431 } 432 433 /* Hook up handlerevents node */ 434 if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) { 435 SYSCTL_ADD_PROC(sc->sysctl_ctx, 436 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 437 "handlerevents", CTLTYPE_STRING | CTLFLAG_RW, 438 sc, 0, acpi_ibm_handlerevents_sysctl, "I", 439 "devd(8) events handled by acpi_ibm"); 440 } 441 442 /* Handle notifies */ 443 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 444 acpi_ibm_notify, dev); 445 446 /* Hook up light to led(4) */ 447 if (sc->light_set_supported) 448 sc->led_dev = led_create_state(ibm_led, sc, "thinklight", 449 (sc->light_val ? 1 : 0)); 450 451 return (0); 452} 453 454static int 455acpi_ibm_detach(device_t dev) 456{ 457 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 458 459 struct acpi_ibm_softc *sc = device_get_softc(dev); 460 461 /* Disable events and restore eventmask */ 462 ACPI_SERIAL_BEGIN(ibm); 463 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); 464 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); 465 ACPI_SERIAL_END(ibm); 466 467 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); 468 469 if (sc->led_dev != NULL) 470 led_destroy(sc->led_dev); 471 472 return (0); 473} 474 475static int 476acpi_ibm_resume(device_t dev) 477{ 478 struct acpi_ibm_softc *sc = device_get_softc(dev); 479 480 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 481 482 ACPI_SERIAL_BEGIN(ibm); 483 for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 484 int val; 485 486 val = acpi_ibm_sysctl_get(sc, i); 487 488 if (acpi_ibm_sysctls[i].flag_rdonly != 0) 489 continue; 490 491 acpi_ibm_sysctl_set(sc, i, val); 492 } 493 ACPI_SERIAL_END(ibm); 494 495 return (0); 496} 497 498static int 499acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) 500{ 501 ACPI_OBJECT arg[2]; 502 ACPI_OBJECT_LIST args; 503 ACPI_STATUS status; 504 505 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 506 ACPI_SERIAL_ASSERT(ibm); 507 508 args.Count = 2; 509 args.Pointer = arg; 510 arg[0].Type = ACPI_TYPE_INTEGER; 511 arg[1].Type = ACPI_TYPE_INTEGER; 512 513 for (int i = 0; i < 32; ++i) { 514 arg[0].Integer.Value = i+1; 515 arg[1].Integer.Value = (((1 << i) & val) != 0); 516 status = AcpiEvaluateObject(sc->handle, 517 IBM_NAME_EVENTS_MASK_SET, &args, NULL); 518 519 if (ACPI_FAILURE(status)) 520 return (status); 521 } 522 523 return (0); 524} 525 526static int 527acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) 528{ 529 struct acpi_ibm_softc *sc; 530 int arg; 531 int error = 0; 532 int function; 533 int method; 534 535 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 536 537 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 538 function = oidp->oid_arg2; 539 method = acpi_ibm_sysctls[function].method; 540 541 ACPI_SERIAL_BEGIN(ibm); 542 arg = acpi_ibm_sysctl_get(sc, method); 543 error = sysctl_handle_int(oidp, &arg, 0, req); 544 545 /* Sanity check */ 546 if (error != 0 || req->newptr == NULL) 547 goto out; 548 549 /* Update */ 550 error = acpi_ibm_sysctl_set(sc, method, arg); 551 552out: 553 ACPI_SERIAL_END(ibm); 554 return (error); 555} 556 557static int 558acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) 559{ 560 UINT64 val_ec; 561 int val = 0, key; 562 563 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 564 ACPI_SERIAL_ASSERT(ibm); 565 566 switch (method) { 567 case ACPI_IBM_METHOD_EVENTS: 568 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); 569 break; 570 571 case ACPI_IBM_METHOD_EVENTMASK: 572 if (sc->events_mask_supported) 573 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); 574 break; 575 576 case ACPI_IBM_METHOD_HOTKEY: 577 /* 578 * Construct the hotkey as a bitmask as illustrated below. 579 * Note that whenever a key was pressed, the respecting bit 580 * toggles and nothing else changes. 581 * +--+--+-+-+-+-+-+-+-+-+-+-+ 582 * |11|10|9|8|7|6|5|4|3|2|1|0| 583 * +--+--+-+-+-+-+-+-+-+-+-+-+ 584 * | | | | | | | | | | | | 585 * | | | | | | | | | | | +- Home Button 586 * | | | | | | | | | | +--- Search Button 587 * | | | | | | | | | +----- Mail Button 588 * | | | | | | | | +------- Thinkpad Button 589 * | | | | | | | +--------- Zoom (Fn + Space) 590 * | | | | | | +----------- WLAN Button 591 * | | | | | +------------- Video Button 592 * | | | | +--------------- Hibernate Button 593 * | | | +----------------- Thinklight Button 594 * | | +------------------- Screen expand (Fn + F8) 595 * | +--------------------- Brightness 596 * +------------------------ Volume/Mute 597 */ 598 key = rtcin(IBM_RTC_HOTKEY1); 599 val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; 600 key = rtcin(IBM_RTC_HOTKEY2); 601 val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; 602 val |= (IBM_RTC_MASK_ZOOM & key) >> 1; 603 key = rtcin(IBM_RTC_THINKLIGHT); 604 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 605 key = rtcin(IBM_RTC_SCREENEXPAND); 606 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 607 key = rtcin(IBM_RTC_BRIGHTNESS); 608 val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; 609 key = rtcin(IBM_RTC_VOLUME); 610 val |= (IBM_RTC_MASK_VOLUME & key) << 4; 611 break; 612 613 case ACPI_IBM_METHOD_BRIGHTNESS: 614 ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 615 val = val_ec & IBM_EC_MASK_BRI; 616 break; 617 618 case ACPI_IBM_METHOD_VOLUME: 619 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 620 val = val_ec & IBM_EC_MASK_VOL; 621 break; 622 623 case ACPI_IBM_METHOD_MUTE: 624 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 625 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 626 break; 627 628 case ACPI_IBM_METHOD_THINKLIGHT: 629 if (sc->light_get_supported) 630 acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); 631 else 632 val = sc->light_val; 633 break; 634 635 case ACPI_IBM_METHOD_BLUETOOTH: 636 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 637 sc->wlan_bt_flags = val; 638 val = ((val & IBM_NAME_MASK_BT) != 0); 639 break; 640 641 case ACPI_IBM_METHOD_WLAN: 642 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 643 sc->wlan_bt_flags = val; 644 val = ((val & IBM_NAME_MASK_WLAN) != 0); 645 break; 646 647 case ACPI_IBM_METHOD_FANSPEED: 648 if (sc->fan_handle) { 649 if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) 650 val = -1; 651 } 652 else { 653 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); 654 val = val_ec; 655 } 656 break; 657 658 case ACPI_IBM_METHOD_FANLEVEL: 659 /* 660 * The IBM_EC_FANSTATUS register works as follows: 661 * Bit 0-5 indicate the level at which the fan operates. Only 662 * values between 0 and 7 have an effect. Everything 663 * above 7 is treated the same as level 7 664 * Bit 6 overrides the fan speed limit if set to 1 665 * Bit 7 indicates at which mode the fan operates: 666 * manual (0) or automatic (1) 667 */ 668 if (!sc->fan_handle) { 669 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 670 val = val_ec & IBM_EC_MASK_FANLEVEL; 671 } 672 break; 673 674 case ACPI_IBM_METHOD_FANSTATUS: 675 if (!sc->fan_handle) { 676 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 677 val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; 678 } 679 else 680 val = -1; 681 break; 682 } 683 684 return (val); 685} 686 687static int 688acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) 689{ 690 int val; 691 UINT64 val_ec; 692 ACPI_STATUS status; 693 694 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 695 ACPI_SERIAL_ASSERT(ibm); 696 697 switch (method) { 698 case ACPI_IBM_METHOD_EVENTS: 699 if (arg < 0 || arg > 1) 700 return (EINVAL); 701 702 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); 703 if (ACPI_FAILURE(status)) 704 return (status); 705 if (sc->events_mask_supported) 706 return acpi_ibm_eventmask_set(sc, sc->events_availmask); 707 break; 708 709 case ACPI_IBM_METHOD_EVENTMASK: 710 if (sc->events_mask_supported) 711 return acpi_ibm_eventmask_set(sc, arg); 712 break; 713 714 case ACPI_IBM_METHOD_BRIGHTNESS: 715 return acpi_ibm_brightness_set(sc, arg); 716 break; 717 718 case ACPI_IBM_METHOD_VOLUME: 719 return acpi_ibm_volume_set(sc, arg); 720 break; 721 722 case ACPI_IBM_METHOD_MUTE: 723 return acpi_ibm_mute_set(sc, arg); 724 break; 725 726 case ACPI_IBM_METHOD_THINKLIGHT: 727 return acpi_ibm_thinklight_set(sc, arg); 728 break; 729 730 case ACPI_IBM_METHOD_BLUETOOTH: 731 return acpi_ibm_bluetooth_set(sc, arg); 732 break; 733 734 case ACPI_IBM_METHOD_FANLEVEL: 735 if (arg < 0 || arg > 7) 736 return (EINVAL); 737 738 if (!sc->fan_handle) { 739 /* Read the current fanstatus */ 740 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 741 val = val_ec & (~IBM_EC_MASK_FANLEVEL); 742 743 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); 744 } 745 break; 746 747 case ACPI_IBM_METHOD_FANSTATUS: 748 if (arg < 0 || arg > 1) 749 return (EINVAL); 750 751 if (!sc->fan_handle) { 752 /* Read the current fanstatus */ 753 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 754 755 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, 756 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); 757 } 758 break; 759 } 760 761 return (0); 762} 763 764static int 765acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) 766{ 767 int dummy; 768 ACPI_OBJECT_TYPE cmos_t; 769 ACPI_HANDLE ledb_handle; 770 771 switch (method) { 772 case ACPI_IBM_METHOD_EVENTS: 773 /* Events are disabled by default */ 774 return (TRUE); 775 776 case ACPI_IBM_METHOD_EVENTMASK: 777 return (sc->events_mask_supported); 778 779 case ACPI_IBM_METHOD_HOTKEY: 780 case ACPI_IBM_METHOD_BRIGHTNESS: 781 case ACPI_IBM_METHOD_VOLUME: 782 case ACPI_IBM_METHOD_MUTE: 783 /* EC is required here, which was aready checked before */ 784 return (TRUE); 785 786 case ACPI_IBM_METHOD_THINKLIGHT: 787 sc->cmos_handle = NULL; 788 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 789 sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); 790 791 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || 792 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || 793 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && 794 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 795 cmos_t == ACPI_TYPE_METHOD) { 796 sc->light_cmd_on = 0x0c; 797 sc->light_cmd_off = 0x0d; 798 sc->cmos_handle = sc->light_handle; 799 } 800 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { 801 sc->light_cmd_on = 1; 802 sc->light_cmd_off = 0; 803 } 804 else 805 sc->light_handle = NULL; 806 807 sc->light_set_supported = (sc->light_handle && 808 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); 809 810 if (sc->light_get_supported) 811 return (TRUE); 812 813 if (sc->light_set_supported) { 814 sc->light_val = 0; 815 return (TRUE); 816 } 817 818 return (FALSE); 819 820 case ACPI_IBM_METHOD_BLUETOOTH: 821 case ACPI_IBM_METHOD_WLAN: 822 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) 823 return (TRUE); 824 return (FALSE); 825 826 case ACPI_IBM_METHOD_FANSPEED: 827 /* 828 * Some models report the fan speed in levels from 0-7 829 * Newer models report it contiguously 830 */ 831 sc->fan_levels = 832 (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || 833 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); 834 return (TRUE); 835 836 case ACPI_IBM_METHOD_FANLEVEL: 837 case ACPI_IBM_METHOD_FANSTATUS: 838 /* 839 * Fan status is only supported on those models, 840 * which report fan RPM contiguously, not in levels 841 */ 842 if (sc->fan_levels) 843 return (FALSE); 844 return (TRUE); 845 846 case ACPI_IBM_METHOD_THERMAL: 847 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { 848 sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); 849 return (TRUE); 850 } 851 return (FALSE); 852 853 case ACPI_IBM_METHOD_HANDLEREVENTS: 854 return (TRUE); 855 } 856 return (FALSE); 857} 858 859static int 860acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) 861{ 862 struct acpi_ibm_softc *sc; 863 int error = 0; 864 char temp_cmd[] = "TMP0"; 865 int temp[8]; 866 867 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 868 869 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 870 871 ACPI_SERIAL_BEGIN(ibm); 872 873 for (int i = 0; i < 8; ++i) { 874 temp_cmd[3] = '0' + i; 875 876 /* 877 * The TMPx methods seem to return +/- 128 or 0 878 * when the respecting sensor is not available 879 */ 880 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 881 &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) 882 temp[i] = -1; 883 else if (sc->thermal_updt_supported) 884 /* Temperature is reported in tenth of Kelvin */ 885 temp[i] = (temp[i] - 2732 + 5) / 10; 886 } 887 888 error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); 889 890 ACPI_SERIAL_END(ibm); 891 return (error); 892} 893 894static int 895acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS) 896{ 897 struct acpi_ibm_softc *sc; 898 int error = 0; 899 struct sbuf sb; 900 char *cp, *ep; 901 int l, val; 902 unsigned int handler_events; 903 char temp[128]; 904 905 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 906 907 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 908 909 if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) 910 return (ENOMEM); 911 912 ACPI_SERIAL_BEGIN(ibm); 913 914 /* Get old values if this is a get request. */ 915 if (req->newptr == NULL) { 916 for (int i = 0; i < 8 * sizeof(sc->handler_events); i++) 917 if (sc->handler_events & (1 << i)) 918 sbuf_printf(&sb, "0x%02x ", i + 1); 919 if (sbuf_len(&sb) == 0) 920 sbuf_printf(&sb, "NONE"); 921 } 922 923 sbuf_trim(&sb); 924 sbuf_finish(&sb); 925 strlcpy(temp, sbuf_data(&sb), sizeof(temp)); 926 sbuf_delete(&sb); 927 928 error = sysctl_handle_string(oidp, temp, sizeof(temp), req); 929 930 /* Check for error or no change */ 931 if (error != 0 || req->newptr == NULL) 932 goto out; 933 934 /* If the user is setting a string, parse it. */ 935 handler_events = 0; 936 cp = temp; 937 while (*cp) { 938 if (isspace(*cp)) { 939 cp++; 940 continue; 941 } 942 943 ep = cp; 944 945 while (*ep && !isspace(*ep)) 946 ep++; 947 948 l = ep - cp; 949 if (l == 0) 950 break; 951 952 if (strncmp(cp, "NONE", 4) == 0) { 953 cp = ep; 954 continue; 955 } 956 957 if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x')) 958 val = strtoul(cp, &ep, 16); 959 else 960 val = strtoul(cp, &ep, 10); 961 962 if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) { 963 cp[l] = '\0'; 964 device_printf(sc->dev, "invalid event code: %s\n", cp); 965 error = EINVAL; 966 goto out; 967 } 968 969 handler_events |= 1 << (val - 1); 970 971 cp = ep; 972 } 973 974 sc->handler_events = handler_events; 975out: 976 ACPI_SERIAL_END(ibm); 977 return (error); 978} 979 980static int 981acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg) 982{ 983 int val, step; 984 UINT64 val_ec; 985 ACPI_OBJECT Arg; 986 ACPI_OBJECT_LIST Args; 987 ACPI_STATUS status; 988 989 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 990 ACPI_SERIAL_ASSERT(ibm); 991 992 if (arg < 0 || arg > 7) 993 return (EINVAL); 994 995 /* Read the current brightness */ 996 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 997 if (ACPI_FAILURE(status)) 998 return (status); 999 1000 if (sc->cmos_handle) { 1001 val = val_ec & IBM_EC_MASK_BRI; 1002 1003 Args.Count = 1; 1004 Args.Pointer = &Arg; 1005 Arg.Type = ACPI_TYPE_INTEGER; 1006 Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : 1007 IBM_CMOS_BRIGHTNESS_DOWN; 1008 1009 step = (arg > val) ? 1 : -1; 1010 for (int i = val; i != arg; i += step) { 1011 status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1012 &Args, NULL); 1013 if (ACPI_FAILURE(status)) { 1014 /* Record the last value */ 1015 if (i != val) { 1016 ACPI_EC_WRITE(sc->ec_dev, 1017 IBM_EC_BRIGHTNESS, i - step, 1); 1018 } 1019 return (status); 1020 } 1021 } 1022 } 1023 1024 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); 1025} 1026 1027static int 1028acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg) 1029{ 1030 int val; 1031 1032 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1033 ACPI_SERIAL_ASSERT(ibm); 1034 1035 if (arg < 0 || arg > 1) 1036 return (EINVAL); 1037 1038 val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : 1039 sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); 1040 return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); 1041} 1042 1043static int 1044acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg) 1045{ 1046 ACPI_OBJECT Arg; 1047 ACPI_OBJECT_LIST Args; 1048 ACPI_STATUS status; 1049 1050 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1051 ACPI_SERIAL_ASSERT(ibm); 1052 1053 if (arg < 0 || arg > 1) 1054 return (EINVAL); 1055 1056 if (sc->light_set_supported) { 1057 Args.Count = 1; 1058 Args.Pointer = &Arg; 1059 Arg.Type = ACPI_TYPE_INTEGER; 1060 Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; 1061 1062 status = AcpiEvaluateObject(sc->light_handle, NULL, 1063 &Args, NULL); 1064 if (ACPI_SUCCESS(status)) 1065 sc->light_val = arg; 1066 return (status); 1067 } 1068 1069 return (0); 1070} 1071 1072static int 1073acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg) 1074{ 1075 int val, step; 1076 UINT64 val_ec; 1077 ACPI_OBJECT Arg; 1078 ACPI_OBJECT_LIST Args; 1079 ACPI_STATUS status; 1080 1081 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1082 ACPI_SERIAL_ASSERT(ibm); 1083 1084 if (arg < 0 || arg > 14) 1085 return (EINVAL); 1086 1087 /* Read the current volume */ 1088 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1089 if (ACPI_FAILURE(status)) 1090 return (status); 1091 1092 if (sc->cmos_handle) { 1093 val = val_ec & IBM_EC_MASK_VOL; 1094 1095 Args.Count = 1; 1096 Args.Pointer = &Arg; 1097 Arg.Type = ACPI_TYPE_INTEGER; 1098 Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : 1099 IBM_CMOS_VOLUME_DOWN; 1100 1101 step = (arg > val) ? 1 : -1; 1102 for (int i = val; i != arg; i += step) { 1103 status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1104 &Args, NULL); 1105 if (ACPI_FAILURE(status)) { 1106 /* Record the last value */ 1107 if (i != val) { 1108 val_ec = i - step + 1109 (val_ec & (~IBM_EC_MASK_VOL)); 1110 ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, 1111 val_ec, 1); 1112 } 1113 return (status); 1114 } 1115 } 1116 } 1117 1118 val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL)); 1119 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1120} 1121 1122static int 1123acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg) 1124{ 1125 UINT64 val_ec; 1126 ACPI_OBJECT Arg; 1127 ACPI_OBJECT_LIST Args; 1128 ACPI_STATUS status; 1129 1130 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1131 ACPI_SERIAL_ASSERT(ibm); 1132 1133 if (arg < 0 || arg > 1) 1134 return (EINVAL); 1135 1136 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1137 if (ACPI_FAILURE(status)) 1138 return (status); 1139 1140 if (sc->cmos_handle) { 1141 Args.Count = 1; 1142 Args.Pointer = &Arg; 1143 Arg.Type = ACPI_TYPE_INTEGER; 1144 Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; 1145 1146 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 1147 if (ACPI_FAILURE(status)) 1148 return (status); 1149 } 1150 1151 val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE : 1152 val_ec & (~IBM_EC_MASK_MUTE); 1153 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1154} 1155 1156static void 1157acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg) 1158{ 1159 int val; 1160 UINT64 val_ec; 1161 ACPI_STATUS status; 1162 1163 ACPI_SERIAL_BEGIN(ibm); 1164 switch (arg) { 1165 case IBM_EVENT_SUSPEND_TO_RAM: 1166 power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); 1167 break; 1168 1169 case IBM_EVENT_BLUETOOTH: 1170 acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0)); 1171 break; 1172 1173 case IBM_EVENT_BRIGHTNESS_UP: 1174 case IBM_EVENT_BRIGHTNESS_DOWN: 1175 /* Read the current brightness */ 1176 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, 1177 &val_ec, 1); 1178 if (ACPI_FAILURE(status)) 1179 return; 1180 1181 val = val_ec & IBM_EC_MASK_BRI; 1182 val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1; 1183 acpi_ibm_brightness_set(sc, val); 1184 break; 1185 1186 case IBM_EVENT_THINKLIGHT: 1187 acpi_ibm_thinklight_set(sc, (sc->light_val == 0)); 1188 break; 1189 1190 case IBM_EVENT_VOLUME_UP: 1191 case IBM_EVENT_VOLUME_DOWN: 1192 /* Read the current volume */ 1193 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1194 if (ACPI_FAILURE(status)) 1195 return; 1196 1197 val = val_ec & IBM_EC_MASK_VOL; 1198 val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1; 1199 acpi_ibm_volume_set(sc, val); 1200 break; 1201 1202 case IBM_EVENT_MUTE: 1203 /* Read the current value */ 1204 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1205 if (ACPI_FAILURE(status)) 1206 return; 1207 1208 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 1209 acpi_ibm_mute_set(sc, (val == 0)); 1210 break; 1211 1212 default: 1213 break; 1214 } 1215 ACPI_SERIAL_END(ibm); 1216} 1217 1218static void 1219acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1220{ 1221 int event, arg, type; 1222 device_t dev = context; 1223 struct acpi_ibm_softc *sc = device_get_softc(dev); 1224 1225 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 1226 1227 if (notify != 0x80) 1228 device_printf(dev, "Unknown notify\n"); 1229 1230 for (;;) { 1231 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); 1232 1233 if (event == 0) 1234 break; 1235 1236 1237 type = (event >> 12) & 0xf; 1238 arg = event & 0xfff; 1239 switch (type) { 1240 case 1: 1241 if (!(sc->events_availmask & (1 << (arg - 1)))) { 1242 device_printf(dev, "Unknown key %d\n", arg); 1243 break; 1244 } 1245 1246 /* Execute event handler */ 1247 if (sc->handler_events & (1 << (arg - 1))) 1248 acpi_ibm_eventhandler(sc, (arg & 0xff)); 1249 1250 /* Notify devd(8) */ 1251 acpi_UserNotify("IBM", h, (arg & 0xff)); 1252 break; 1253 default: 1254 break; 1255 } 1256 } 1257} 1258