1 /*- 2 * Copyright (c) 2004 Takanori Watanabe 3 * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 /* 32 * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops. 33 * Inspired by the ibm-acpi and tpb projects which implement these features 34 * on Linux. 35 * 36 * acpi-ibm: <http://ibm-acpi.sourceforge.net/> 37 * tpb: <http://www.nongnu.org/tpb/> 38 */ 39 40 #include "opt_acpi.h" 41 #include <sys/param.h> 42 #include <sys/kernel.h> 43 #include <sys/bus.h> 44 #include <machine/cpufunc.h> 45 46 #include <contrib/dev/acpica/include/acpi.h> 47 #include <contrib/dev/acpica/include/accommon.h> 48 49 #include "acpi_if.h" 50 #include <sys/module.h> 51 #include <dev/acpica/acpivar.h> 52 #include <dev/led/led.h> 53 #include <sys/power.h> 54 #include <sys/sbuf.h> 55 #include <sys/sysctl.h> 56 #include <isa/rtc.h> 57 58 #define _COMPONENT ACPI_OEM 59 ACPI_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 149 struct 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 191 static struct { 192 char *name; 193 int method; 194 char *description; 195 int access; 196 } acpi_ibm_sysctls[] = { 197 { 198 .name = "events", 199 .method = ACPI_IBM_METHOD_EVENTS, 200 .description = "ACPI events enable", 201 .access = CTLTYPE_INT | CTLFLAG_RW 202 }, 203 { 204 .name = "eventmask", 205 .method = ACPI_IBM_METHOD_EVENTMASK, 206 .description = "ACPI eventmask", 207 .access = CTLTYPE_INT | CTLFLAG_RW 208 }, 209 { 210 .name = "hotkey", 211 .method = ACPI_IBM_METHOD_HOTKEY, 212 .description = "Key Status", 213 .access = CTLTYPE_INT | CTLFLAG_RD 214 }, 215 { 216 .name = "lcd_brightness", 217 .method = ACPI_IBM_METHOD_BRIGHTNESS, 218 .description = "LCD Brightness", 219 .access = CTLTYPE_INT | CTLFLAG_RW 220 }, 221 { 222 .name = "volume", 223 .method = ACPI_IBM_METHOD_VOLUME, 224 .description = "Volume", 225 .access = CTLTYPE_INT | CTLFLAG_RW 226 }, 227 { 228 .name = "mute", 229 .method = ACPI_IBM_METHOD_MUTE, 230 .description = "Mute", 231 .access = CTLTYPE_INT | CTLFLAG_RW 232 }, 233 { 234 .name = "thinklight", 235 .method = ACPI_IBM_METHOD_THINKLIGHT, 236 .description = "Thinklight enable", 237 .access = CTLTYPE_INT | CTLFLAG_RW 238 }, 239 { 240 .name = "bluetooth", 241 .method = ACPI_IBM_METHOD_BLUETOOTH, 242 .description = "Bluetooth enable", 243 .access = CTLTYPE_INT | CTLFLAG_RW 244 }, 245 { 246 .name = "wlan", 247 .method = ACPI_IBM_METHOD_WLAN, 248 .description = "WLAN enable", 249 .access = CTLTYPE_INT | CTLFLAG_RD 250 }, 251 { 252 .name = "fan_speed", 253 .method = ACPI_IBM_METHOD_FANSPEED, 254 .description = "Fan speed", 255 .access = CTLTYPE_INT | CTLFLAG_RD 256 }, 257 { 258 .name = "fan_level", 259 .method = ACPI_IBM_METHOD_FANLEVEL, 260 .description = "Fan level", 261 .access = CTLTYPE_INT | CTLFLAG_RW 262 }, 263 { 264 .name = "fan", 265 .method = ACPI_IBM_METHOD_FANSTATUS, 266 .description = "Fan enable", 267 .access = CTLTYPE_INT | CTLFLAG_RW 268 }, 269 270 { NULL, 0, NULL, 0 } 271 }; 272 273 ACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); 274 275 static int acpi_ibm_probe(device_t dev); 276 static int acpi_ibm_attach(device_t dev); 277 static int acpi_ibm_detach(device_t dev); 278 static int acpi_ibm_resume(device_t dev); 279 280 static void ibm_led(void *softc, int onoff); 281 static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); 282 283 static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); 284 static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); 285 static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); 286 static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); 287 288 static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); 289 static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); 290 static int acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS); 291 static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); 292 293 static int acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg); 294 static int acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg); 295 static int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg); 296 static int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg); 297 static int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg); 298 299 static device_method_t acpi_ibm_methods[] = { 300 /* Device interface */ 301 DEVMETHOD(device_probe, acpi_ibm_probe), 302 DEVMETHOD(device_attach, acpi_ibm_attach), 303 DEVMETHOD(device_detach, acpi_ibm_detach), 304 DEVMETHOD(device_resume, acpi_ibm_resume), 305 306 {0, 0} 307 }; 308 309 static driver_t acpi_ibm_driver = { 310 "acpi_ibm", 311 acpi_ibm_methods, 312 sizeof(struct acpi_ibm_softc), 313 }; 314 315 static devclass_t acpi_ibm_devclass; 316 317 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 318 0, 0); 319 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 320 static char *ibm_ids[] = {"IBM0068", NULL}; 321 322 static void 323 ibm_led(void *softc, int onoff) 324 { 325 struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; 326 327 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 328 329 if (sc->led_busy) 330 return; 331 332 sc->led_busy = 1; 333 sc->led_state = onoff; 334 335 AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); 336 } 337 338 static void 339 ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) 340 { 341 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 342 343 ACPI_SERIAL_BEGIN(ibm); 344 acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); 345 ACPI_SERIAL_END(ibm); 346 347 sc->led_busy = 0; 348 } 349 350 static int 351 acpi_ibm_probe(device_t dev) 352 { 353 if (acpi_disabled("ibm") || 354 ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || 355 device_get_unit(dev) != 0) 356 return (ENXIO); 357 358 device_set_desc(dev, "IBM ThinkPad ACPI Extras"); 359 360 return (0); 361 } 362 363 static int 364 acpi_ibm_attach(device_t dev) 365 { 366 struct acpi_ibm_softc *sc; 367 devclass_t ec_devclass; 368 369 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 370 371 sc = device_get_softc(dev); 372 sc->dev = dev; 373 sc->handle = acpi_get_handle(dev); 374 375 /* Look for the first embedded controller */ 376 if (!(ec_devclass = devclass_find ("acpi_ec"))) { 377 if (bootverbose) 378 device_printf(dev, "Couldn't find acpi_ec devclass\n"); 379 return (EINVAL); 380 } 381 if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { 382 if (bootverbose) 383 device_printf(dev, "Couldn't find acpi_ec device\n"); 384 return (EINVAL); 385 } 386 sc->ec_handle = acpi_get_handle(sc->ec_dev); 387 388 /* Get the sysctl tree */ 389 sc->sysctl_ctx = device_get_sysctl_ctx(dev); 390 sc->sysctl_tree = device_get_sysctl_tree(dev); 391 392 /* Look for event mask and hook up the nodes */ 393 sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, 394 IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); 395 396 if (sc->events_mask_supported) { 397 SYSCTL_ADD_UINT(sc->sysctl_ctx, 398 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 399 "initialmask", CTLFLAG_RD, 400 &sc->events_initialmask, 0, "Initial eventmask"); 401 402 /* The availmask is the bitmask of supported events */ 403 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 404 IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) 405 sc->events_availmask = 0xffffffff; 406 407 SYSCTL_ADD_UINT(sc->sysctl_ctx, 408 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 409 "availmask", CTLFLAG_RD, 410 &sc->events_availmask, 0, "Mask of supported events"); 411 } 412 413 /* Hook up proc nodes */ 414 for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 415 if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) 416 continue; 417 418 SYSCTL_ADD_PROC(sc->sysctl_ctx, 419 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 420 acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, 421 sc, i, acpi_ibm_sysctl, "I", 422 acpi_ibm_sysctls[i].description); 423 } 424 425 /* Hook up thermal node */ 426 if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) { 427 SYSCTL_ADD_PROC(sc->sysctl_ctx, 428 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 429 "thermal", CTLTYPE_INT | CTLFLAG_RD, 430 sc, 0, acpi_ibm_thermal_sysctl, "I", 431 "Thermal zones"); 432 } 433 434 /* Hook up handlerevents node */ 435 if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) { 436 SYSCTL_ADD_PROC(sc->sysctl_ctx, 437 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 438 "handlerevents", CTLTYPE_STRING | CTLFLAG_RW, 439 sc, 0, acpi_ibm_handlerevents_sysctl, "I", 440 "devd(8) events handled by acpi_ibm"); 441 } 442 443 /* Handle notifies */ 444 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 445 acpi_ibm_notify, dev); 446 447 /* Hook up light to led(4) */ 448 if (sc->light_set_supported) 449 sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); 450 451 return (0); 452 } 453 454 static int 455 acpi_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 475 static int 476 acpi_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 if ((acpi_ibm_sysctls[i].access & CTLFLAG_RD) == 0) { 487 continue; 488 } 489 490 val = acpi_ibm_sysctl_get(sc, i); 491 492 if ((acpi_ibm_sysctls[i].access & CTLFLAG_WR) == 0) { 493 continue; 494 } 495 496 acpi_ibm_sysctl_set(sc, i, val); 497 } 498 ACPI_SERIAL_END(ibm); 499 500 return (0); 501 } 502 503 static int 504 acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) 505 { 506 ACPI_OBJECT arg[2]; 507 ACPI_OBJECT_LIST args; 508 ACPI_STATUS status; 509 510 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 511 ACPI_SERIAL_ASSERT(ibm); 512 513 args.Count = 2; 514 args.Pointer = arg; 515 arg[0].Type = ACPI_TYPE_INTEGER; 516 arg[1].Type = ACPI_TYPE_INTEGER; 517 518 for (int i = 0; i < 32; ++i) { 519 arg[0].Integer.Value = i+1; 520 arg[1].Integer.Value = (((1 << i) & val) != 0); 521 status = AcpiEvaluateObject(sc->handle, 522 IBM_NAME_EVENTS_MASK_SET, &args, NULL); 523 524 if (ACPI_FAILURE(status)) 525 return (status); 526 } 527 528 return (0); 529 } 530 531 static int 532 acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) 533 { 534 struct acpi_ibm_softc *sc; 535 int arg; 536 int error = 0; 537 int function; 538 int method; 539 540 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 541 542 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 543 function = oidp->oid_arg2; 544 method = acpi_ibm_sysctls[function].method; 545 546 ACPI_SERIAL_BEGIN(ibm); 547 arg = acpi_ibm_sysctl_get(sc, method); 548 error = sysctl_handle_int(oidp, &arg, 0, req); 549 550 /* Sanity check */ 551 if (error != 0 || req->newptr == NULL) 552 goto out; 553 554 /* Update */ 555 error = acpi_ibm_sysctl_set(sc, method, arg); 556 557 out: 558 ACPI_SERIAL_END(ibm); 559 return (error); 560 } 561 562 static int 563 acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) 564 { 565 UINT64 val_ec; 566 int val = 0, key; 567 568 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 569 ACPI_SERIAL_ASSERT(ibm); 570 571 switch (method) { 572 case ACPI_IBM_METHOD_EVENTS: 573 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); 574 break; 575 576 case ACPI_IBM_METHOD_EVENTMASK: 577 if (sc->events_mask_supported) 578 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); 579 break; 580 581 case ACPI_IBM_METHOD_HOTKEY: 582 /* 583 * Construct the hotkey as a bitmask as illustrated below. 584 * Note that whenever a key was pressed, the respecting bit 585 * toggles and nothing else changes. 586 * +--+--+-+-+-+-+-+-+-+-+-+-+ 587 * |11|10|9|8|7|6|5|4|3|2|1|0| 588 * +--+--+-+-+-+-+-+-+-+-+-+-+ 589 * | | | | | | | | | | | | 590 * | | | | | | | | | | | +- Home Button 591 * | | | | | | | | | | +--- Search Button 592 * | | | | | | | | | +----- Mail Button 593 * | | | | | | | | +------- Thinkpad Button 594 * | | | | | | | +--------- Zoom (Fn + Space) 595 * | | | | | | +----------- WLAN Button 596 * | | | | | +------------- Video Button 597 * | | | | +--------------- Hibernate Button 598 * | | | +----------------- Thinklight Button 599 * | | +------------------- Screen expand (Fn + F8) 600 * | +--------------------- Brightness 601 * +------------------------ Volume/Mute 602 */ 603 key = rtcin(IBM_RTC_HOTKEY1); 604 val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; 605 key = rtcin(IBM_RTC_HOTKEY2); 606 val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; 607 val |= (IBM_RTC_MASK_ZOOM & key) >> 1; 608 key = rtcin(IBM_RTC_THINKLIGHT); 609 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 610 key = rtcin(IBM_RTC_SCREENEXPAND); 611 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 612 key = rtcin(IBM_RTC_BRIGHTNESS); 613 val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; 614 key = rtcin(IBM_RTC_VOLUME); 615 val |= (IBM_RTC_MASK_VOLUME & key) << 4; 616 break; 617 618 case ACPI_IBM_METHOD_BRIGHTNESS: 619 ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 620 val = val_ec & IBM_EC_MASK_BRI; 621 break; 622 623 case ACPI_IBM_METHOD_VOLUME: 624 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 625 val = val_ec & IBM_EC_MASK_VOL; 626 break; 627 628 case ACPI_IBM_METHOD_MUTE: 629 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 630 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 631 break; 632 633 case ACPI_IBM_METHOD_THINKLIGHT: 634 if (sc->light_get_supported) 635 acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); 636 else 637 val = sc->light_val; 638 break; 639 640 case ACPI_IBM_METHOD_BLUETOOTH: 641 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 642 sc->wlan_bt_flags = val; 643 val = ((val & IBM_NAME_MASK_BT) != 0); 644 break; 645 646 case ACPI_IBM_METHOD_WLAN: 647 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 648 sc->wlan_bt_flags = val; 649 val = ((val & IBM_NAME_MASK_WLAN) != 0); 650 break; 651 652 case ACPI_IBM_METHOD_FANSPEED: 653 if (sc->fan_handle) { 654 if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) 655 val = -1; 656 } 657 else { 658 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); 659 val = val_ec; 660 } 661 break; 662 663 case ACPI_IBM_METHOD_FANLEVEL: 664 /* 665 * The IBM_EC_FANSTATUS register works as follows: 666 * Bit 0-5 indicate the level at which the fan operates. Only 667 * values between 0 and 7 have an effect. Everything 668 * above 7 is treated the same as level 7 669 * Bit 6 overrides the fan speed limit if set to 1 670 * Bit 7 indicates at which mode the fan operates: 671 * manual (0) or automatic (1) 672 */ 673 if (!sc->fan_handle) { 674 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 675 val = val_ec & IBM_EC_MASK_FANLEVEL; 676 } 677 break; 678 679 case ACPI_IBM_METHOD_FANSTATUS: 680 if (!sc->fan_handle) { 681 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 682 val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; 683 } 684 else 685 val = -1; 686 break; 687 } 688 689 return (val); 690 } 691 692 static int 693 acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) 694 { 695 int val; 696 UINT64 val_ec; 697 ACPI_STATUS status; 698 699 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 700 ACPI_SERIAL_ASSERT(ibm); 701 702 switch (method) { 703 case ACPI_IBM_METHOD_EVENTS: 704 if (arg < 0 || arg > 1) 705 return (EINVAL); 706 707 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); 708 if (ACPI_FAILURE(status)) 709 return (status); 710 if (sc->events_mask_supported) 711 return acpi_ibm_eventmask_set(sc, sc->events_availmask); 712 break; 713 714 case ACPI_IBM_METHOD_EVENTMASK: 715 if (sc->events_mask_supported) 716 return acpi_ibm_eventmask_set(sc, arg); 717 break; 718 719 case ACPI_IBM_METHOD_BRIGHTNESS: 720 return acpi_ibm_brightness_set(sc, arg); 721 break; 722 723 case ACPI_IBM_METHOD_VOLUME: 724 return acpi_ibm_volume_set(sc, arg); 725 break; 726 727 case ACPI_IBM_METHOD_MUTE: 728 return acpi_ibm_mute_set(sc, arg); 729 break; 730 731 case ACPI_IBM_METHOD_THINKLIGHT: 732 return acpi_ibm_thinklight_set(sc, arg); 733 break; 734 735 case ACPI_IBM_METHOD_BLUETOOTH: 736 return acpi_ibm_bluetooth_set(sc, arg); 737 break; 738 739 case ACPI_IBM_METHOD_FANLEVEL: 740 if (arg < 0 || arg > 7) 741 return (EINVAL); 742 743 if (!sc->fan_handle) { 744 /* Read the current fanstatus */ 745 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 746 val = val_ec & (~IBM_EC_MASK_FANLEVEL); 747 748 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); 749 } 750 break; 751 752 case ACPI_IBM_METHOD_FANSTATUS: 753 if (arg < 0 || arg > 1) 754 return (EINVAL); 755 756 if (!sc->fan_handle) { 757 /* Read the current fanstatus */ 758 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 759 760 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, 761 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); 762 } 763 break; 764 } 765 766 return (0); 767 } 768 769 static int 770 acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) 771 { 772 int dummy; 773 ACPI_OBJECT_TYPE cmos_t; 774 ACPI_HANDLE ledb_handle; 775 776 switch (method) { 777 case ACPI_IBM_METHOD_EVENTS: 778 /* Events are disabled by default */ 779 return (TRUE); 780 781 case ACPI_IBM_METHOD_EVENTMASK: 782 return (sc->events_mask_supported); 783 784 case ACPI_IBM_METHOD_HOTKEY: 785 case ACPI_IBM_METHOD_BRIGHTNESS: 786 case ACPI_IBM_METHOD_VOLUME: 787 case ACPI_IBM_METHOD_MUTE: 788 /* EC is required here, which was aready checked before */ 789 return (TRUE); 790 791 case ACPI_IBM_METHOD_THINKLIGHT: 792 sc->cmos_handle = NULL; 793 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 794 sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); 795 796 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || 797 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || 798 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && 799 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 800 cmos_t == ACPI_TYPE_METHOD) { 801 sc->light_cmd_on = 0x0c; 802 sc->light_cmd_off = 0x0d; 803 sc->cmos_handle = sc->light_handle; 804 } 805 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { 806 sc->light_cmd_on = 1; 807 sc->light_cmd_off = 0; 808 } 809 else 810 sc->light_handle = NULL; 811 812 sc->light_set_supported = (sc->light_handle && 813 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); 814 815 if (sc->light_get_supported) 816 return (TRUE); 817 818 if (sc->light_set_supported) { 819 sc->light_val = 0; 820 return (TRUE); 821 } 822 823 return (FALSE); 824 825 case ACPI_IBM_METHOD_BLUETOOTH: 826 case ACPI_IBM_METHOD_WLAN: 827 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) 828 return (TRUE); 829 return (FALSE); 830 831 case ACPI_IBM_METHOD_FANSPEED: 832 /* 833 * Some models report the fan speed in levels from 0-7 834 * Newer models report it contiguously 835 */ 836 sc->fan_levels = 837 (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || 838 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); 839 return (TRUE); 840 841 case ACPI_IBM_METHOD_FANLEVEL: 842 case ACPI_IBM_METHOD_FANSTATUS: 843 /* 844 * Fan status is only supported on those models, 845 * which report fan RPM contiguously, not in levels 846 */ 847 if (sc->fan_levels) 848 return (FALSE); 849 return (TRUE); 850 851 case ACPI_IBM_METHOD_THERMAL: 852 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { 853 sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); 854 return (TRUE); 855 } 856 return (FALSE); 857 858 case ACPI_IBM_METHOD_HANDLEREVENTS: 859 return (TRUE); 860 } 861 return (FALSE); 862 } 863 864 static int 865 acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) 866 { 867 struct acpi_ibm_softc *sc; 868 int error = 0; 869 char temp_cmd[] = "TMP0"; 870 int temp[8]; 871 872 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 873 874 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 875 876 ACPI_SERIAL_BEGIN(ibm); 877 878 for (int i = 0; i < 8; ++i) { 879 temp_cmd[3] = '0' + i; 880 881 /* 882 * The TMPx methods seem to return +/- 128 or 0 883 * when the respecting sensor is not available 884 */ 885 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 886 &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) 887 temp[i] = -1; 888 else if (sc->thermal_updt_supported) 889 /* Temperature is reported in tenth of Kelvin */ 890 temp[i] = (temp[i] - 2732 + 5) / 10; 891 } 892 893 error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); 894 895 ACPI_SERIAL_END(ibm); 896 return (error); 897 } 898 899 static int 900 acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS) 901 { 902 struct acpi_ibm_softc *sc; 903 int error = 0; 904 struct sbuf sb; 905 char *cp, *ep; 906 int l, val; 907 unsigned int handler_events; 908 909 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 910 911 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 912 913 if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) 914 return (ENOMEM); 915 916 ACPI_SERIAL_BEGIN(ibm); 917 918 /* Get old values if this is a get request. */ 919 if (req->newptr == NULL) { 920 for (int i = 0; i < 8 * sizeof(sc->handler_events); i++) 921 if (sc->handler_events & (1 << i)) 922 sbuf_printf(&sb, "0x%02x ", i + 1); 923 if (sbuf_len(&sb) == 0) 924 sbuf_printf(&sb, "NONE"); 925 } 926 927 sbuf_trim(&sb); 928 sbuf_finish(&sb); 929 930 /* Copy out the old values to the user. */ 931 error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb)); 932 sbuf_delete(&sb); 933 934 if (error != 0 || req->newptr == NULL) 935 goto out; 936 937 /* If the user is setting a string, parse it. */ 938 handler_events = 0; 939 cp = (char *)req->newptr; 940 while (*cp) { 941 if (isspace(*cp)) { 942 cp++; 943 continue; 944 } 945 946 ep = cp; 947 948 while (*ep && !isspace(*ep)) 949 ep++; 950 951 l = ep - cp; 952 if (l == 0) 953 break; 954 955 if (strncmp(cp, "NONE", 4) == 0) { 956 cp = ep; 957 continue; 958 } 959 960 if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x')) 961 val = strtoul(cp, &ep, 16); 962 else 963 val = strtoul(cp, &ep, 10); 964 965 if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) { 966 cp[l] = '\0'; 967 device_printf(sc->dev, "invalid event code: %s\n", cp); 968 error = EINVAL; 969 goto out; 970 } 971 972 handler_events |= 1 << (val - 1); 973 974 cp = ep; 975 } 976 977 sc->handler_events = handler_events; 978 out: 979 ACPI_SERIAL_END(ibm); 980 return (error); 981 } 982 983 static int 984 acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg) 985 { 986 int val, step; 987 UINT64 val_ec; 988 ACPI_OBJECT Arg; 989 ACPI_OBJECT_LIST Args; 990 ACPI_STATUS status; 991 992 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 993 ACPI_SERIAL_ASSERT(ibm); 994 995 if (arg < 0 || arg > 7) 996 return (EINVAL); 997 998 /* Read the current brightness */ 999 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 1000 if (ACPI_FAILURE(status)) 1001 return (status); 1002 1003 if (sc->cmos_handle) { 1004 val = val_ec & IBM_EC_MASK_BRI; 1005 1006 Args.Count = 1; 1007 Args.Pointer = &Arg; 1008 Arg.Type = ACPI_TYPE_INTEGER; 1009 Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : 1010 IBM_CMOS_BRIGHTNESS_DOWN; 1011 1012 step = (arg > val) ? 1 : -1; 1013 for (int i = val; i != arg; i += step) { 1014 status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1015 &Args, NULL); 1016 if (ACPI_FAILURE(status)) { 1017 /* Record the last value */ 1018 if (i != val) { 1019 ACPI_EC_WRITE(sc->ec_dev, 1020 IBM_EC_BRIGHTNESS, i - step, 1); 1021 } 1022 return (status); 1023 } 1024 } 1025 } 1026 1027 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); 1028 } 1029 1030 static int 1031 acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg) 1032 { 1033 int val; 1034 1035 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1036 ACPI_SERIAL_ASSERT(ibm); 1037 1038 if (arg < 0 || arg > 1) 1039 return (EINVAL); 1040 1041 val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : 1042 sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); 1043 return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); 1044 } 1045 1046 static int 1047 acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg) 1048 { 1049 ACPI_OBJECT Arg; 1050 ACPI_OBJECT_LIST Args; 1051 ACPI_STATUS status; 1052 1053 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1054 ACPI_SERIAL_ASSERT(ibm); 1055 1056 if (arg < 0 || arg > 1) 1057 return (EINVAL); 1058 1059 if (sc->light_set_supported) { 1060 Args.Count = 1; 1061 Args.Pointer = &Arg; 1062 Arg.Type = ACPI_TYPE_INTEGER; 1063 Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; 1064 1065 status = AcpiEvaluateObject(sc->light_handle, NULL, 1066 &Args, NULL); 1067 if (ACPI_SUCCESS(status)) 1068 sc->light_val = arg; 1069 return (status); 1070 } 1071 1072 return (0); 1073 } 1074 1075 static int 1076 acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg) 1077 { 1078 int val, step; 1079 UINT64 val_ec; 1080 ACPI_OBJECT Arg; 1081 ACPI_OBJECT_LIST Args; 1082 ACPI_STATUS status; 1083 1084 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1085 ACPI_SERIAL_ASSERT(ibm); 1086 1087 if (arg < 0 || arg > 14) 1088 return (EINVAL); 1089 1090 /* Read the current volume */ 1091 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1092 if (ACPI_FAILURE(status)) 1093 return (status); 1094 1095 if (sc->cmos_handle) { 1096 val = val_ec & IBM_EC_MASK_VOL; 1097 1098 Args.Count = 1; 1099 Args.Pointer = &Arg; 1100 Arg.Type = ACPI_TYPE_INTEGER; 1101 Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : 1102 IBM_CMOS_VOLUME_DOWN; 1103 1104 step = (arg > val) ? 1 : -1; 1105 for (int i = val; i != arg; i += step) { 1106 status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1107 &Args, NULL); 1108 if (ACPI_FAILURE(status)) { 1109 /* Record the last value */ 1110 if (i != val) { 1111 val_ec = i - step + 1112 (val_ec & (~IBM_EC_MASK_VOL)); 1113 ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, 1114 val_ec, 1); 1115 } 1116 return (status); 1117 } 1118 } 1119 } 1120 1121 val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL)); 1122 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1123 } 1124 1125 static int 1126 acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg) 1127 { 1128 UINT64 val_ec; 1129 ACPI_OBJECT Arg; 1130 ACPI_OBJECT_LIST Args; 1131 ACPI_STATUS status; 1132 1133 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1134 ACPI_SERIAL_ASSERT(ibm); 1135 1136 if (arg < 0 || arg > 1) 1137 return (EINVAL); 1138 1139 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1140 if (ACPI_FAILURE(status)) 1141 return (status); 1142 1143 if (sc->cmos_handle) { 1144 Args.Count = 1; 1145 Args.Pointer = &Arg; 1146 Arg.Type = ACPI_TYPE_INTEGER; 1147 Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; 1148 1149 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 1150 if (ACPI_FAILURE(status)) 1151 return (status); 1152 } 1153 1154 val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE : 1155 val_ec & (~IBM_EC_MASK_MUTE); 1156 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1157 } 1158 1159 static void 1160 acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg) 1161 { 1162 int val; 1163 UINT64 val_ec; 1164 ACPI_STATUS status; 1165 1166 ACPI_SERIAL_BEGIN(ibm); 1167 switch (arg) { 1168 case IBM_EVENT_SUSPEND_TO_RAM: 1169 power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); 1170 break; 1171 1172 case IBM_EVENT_BLUETOOTH: 1173 acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0)); 1174 break; 1175 1176 case IBM_EVENT_BRIGHTNESS_UP: 1177 case IBM_EVENT_BRIGHTNESS_DOWN: 1178 /* Read the current brightness */ 1179 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, 1180 &val_ec, 1); 1181 if (ACPI_FAILURE(status)) 1182 return; 1183 1184 val = val_ec & IBM_EC_MASK_BRI; 1185 val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1; 1186 acpi_ibm_brightness_set(sc, val); 1187 break; 1188 1189 case IBM_EVENT_THINKLIGHT: 1190 acpi_ibm_thinklight_set(sc, (sc->light_val == 0)); 1191 break; 1192 1193 case IBM_EVENT_VOLUME_UP: 1194 case IBM_EVENT_VOLUME_DOWN: 1195 /* Read the current volume */ 1196 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1197 if (ACPI_FAILURE(status)) 1198 return; 1199 1200 val = val_ec & IBM_EC_MASK_VOL; 1201 val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1; 1202 acpi_ibm_volume_set(sc, val); 1203 break; 1204 1205 case IBM_EVENT_MUTE: 1206 /* Read the current value */ 1207 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1208 if (ACPI_FAILURE(status)) 1209 return; 1210 1211 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 1212 acpi_ibm_mute_set(sc, (val == 0)); 1213 break; 1214 1215 default: 1216 break; 1217 } 1218 ACPI_SERIAL_END(ibm); 1219 } 1220 1221 static void 1222 acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1223 { 1224 int event, arg, type; 1225 device_t dev = context; 1226 struct acpi_ibm_softc *sc = device_get_softc(dev); 1227 1228 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 1229 1230 if (notify != 0x80) 1231 device_printf(dev, "Unknown notify\n"); 1232 1233 for (;;) { 1234 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); 1235 1236 if (event == 0) 1237 break; 1238 1239 1240 type = (event >> 12) & 0xf; 1241 arg = event & 0xfff; 1242 switch (type) { 1243 case 1: 1244 if (!(sc->events_availmask & (1 << (arg - 1)))) { 1245 device_printf(dev, "Unknown key %d\n", arg); 1246 break; 1247 } 1248 1249 /* Execute event handler */ 1250 if (sc->handler_events & (1 << (arg - 1))) 1251 acpi_ibm_eventhandler(sc, (arg & 0xff)); 1252 1253 /* Notify devd(8) */ 1254 acpi_UserNotify("IBM", h, (arg & 0xff)); 1255 break; 1256 default: 1257 break; 1258 } 1259 } 1260 } 1261