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