1 /*- 2 * Copyright (c) 2004 Takanori Watanabe 3 * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org> 4 * All rights reserved. 5 * Copyright (c) 2020 Ali Abdallah <ali.abdallah@suse.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 /* 33 * Driver for extra ACPI-controlled gadgets found on ThinkPad laptops. 34 * Inspired by the ibm-acpi and tpb projects which implement these features 35 * on Linux. 36 * 37 * acpi-ibm: <http://ibm-acpi.sourceforge.net/> 38 * tpb: <http://www.nongnu.org/tpb/> 39 */ 40 41 #include "opt_acpi.h" 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/bus.h> 46 #include <machine/cpufunc.h> 47 48 #include <contrib/dev/acpica/include/acpi.h> 49 #include <contrib/dev/acpica/include/accommon.h> 50 51 #include "acpi_if.h" 52 #include <sys/module.h> 53 #include <dev/acpica/acpivar.h> 54 #include <dev/led/led.h> 55 #include <sys/power.h> 56 #include <sys/sbuf.h> 57 #include <sys/sysctl.h> 58 #include <isa/rtc.h> 59 60 #define _COMPONENT ACPI_OEM 61 ACPI_MODULE_NAME("IBM") 62 63 /* Internal methods */ 64 #define ACPI_IBM_METHOD_EVENTS 1 65 #define ACPI_IBM_METHOD_EVENTMASK 2 66 #define ACPI_IBM_METHOD_HOTKEY 3 67 #define ACPI_IBM_METHOD_BRIGHTNESS 4 68 #define ACPI_IBM_METHOD_VOLUME 5 69 #define ACPI_IBM_METHOD_MUTE 6 70 #define ACPI_IBM_METHOD_THINKLIGHT 7 71 #define ACPI_IBM_METHOD_BLUETOOTH 8 72 #define ACPI_IBM_METHOD_WLAN 9 73 #define ACPI_IBM_METHOD_FANSPEED 10 74 #define ACPI_IBM_METHOD_FANLEVEL 11 75 #define ACPI_IBM_METHOD_FANSTATUS 12 76 #define ACPI_IBM_METHOD_THERMAL 13 77 #define ACPI_IBM_METHOD_HANDLEREVENTS 14 78 #define ACPI_IBM_METHOD_MIC_LED 15 79 #define ACPI_IBM_METHOD_PRIVACYGUARD 16 80 81 /* Hotkeys/Buttons */ 82 #define IBM_RTC_HOTKEY1 0x64 83 #define IBM_RTC_MASK_HOME (1 << 0) 84 #define IBM_RTC_MASK_SEARCH (1 << 1) 85 #define IBM_RTC_MASK_MAIL (1 << 2) 86 #define IBM_RTC_MASK_WLAN (1 << 5) 87 #define IBM_RTC_HOTKEY2 0x65 88 #define IBM_RTC_MASK_THINKPAD (1 << 3) 89 #define IBM_RTC_MASK_ZOOM (1 << 5) 90 #define IBM_RTC_MASK_VIDEO (1 << 6) 91 #define IBM_RTC_MASK_HIBERNATE (1 << 7) 92 #define IBM_RTC_THINKLIGHT 0x66 93 #define IBM_RTC_MASK_THINKLIGHT (1 << 4) 94 #define IBM_RTC_SCREENEXPAND 0x67 95 #define IBM_RTC_MASK_SCREENEXPAND (1 << 5) 96 #define IBM_RTC_BRIGHTNESS 0x6c 97 #define IBM_RTC_MASK_BRIGHTNESS (1 << 5) 98 #define IBM_RTC_VOLUME 0x6e 99 #define IBM_RTC_MASK_VOLUME (1 << 7) 100 101 /* Embedded Controller registers */ 102 #define IBM_EC_BRIGHTNESS 0x31 103 #define IBM_EC_MASK_BRI 0x7 104 #define IBM_EC_VOLUME 0x30 105 #define IBM_EC_MASK_VOL 0xf 106 #define IBM_EC_MASK_MUTE (1 << 6) 107 #define IBM_EC_FANSTATUS 0x2F 108 #define IBM_EC_MASK_FANLEVEL 0x3f 109 #define IBM_EC_MASK_FANUNTHROTTLED (1 << 6) 110 #define IBM_EC_MASK_FANSTATUS (1 << 7) 111 #define IBM_EC_FANSPEED 0x84 112 113 /* CMOS Commands */ 114 #define IBM_CMOS_VOLUME_DOWN 0 115 #define IBM_CMOS_VOLUME_UP 1 116 #define IBM_CMOS_VOLUME_MUTE 2 117 #define IBM_CMOS_BRIGHTNESS_UP 4 118 #define IBM_CMOS_BRIGHTNESS_DOWN 5 119 120 /* ACPI methods */ 121 #define IBM_NAME_KEYLIGHT "KBLT" 122 #define IBM_NAME_WLAN_BT_GET "GBDC" 123 #define IBM_NAME_WLAN_BT_SET "SBDC" 124 #define IBM_NAME_MASK_BT (1 << 1) 125 #define IBM_NAME_MASK_WLAN (1 << 2) 126 #define IBM_NAME_THERMAL_GET "TMP7" 127 #define IBM_NAME_THERMAL_UPDT "UPDT" 128 #define IBM_NAME_PRIVACYGUARD_GET "GSSS" 129 #define IBM_NAME_PRIVACYGUARD_SET "SSSS" 130 131 #define IBM_NAME_EVENTS_STATUS_GET "DHKC" 132 #define IBM_NAME_EVENTS_MASK_GET "DHKN" 133 #define IBM_NAME_EVENTS_STATUS_SET "MHKC" 134 #define IBM_NAME_EVENTS_MASK_SET "MHKM" 135 #define IBM_NAME_EVENTS_GET "MHKP" 136 #define IBM_NAME_EVENTS_AVAILMASK "MHKA" 137 138 /* Event Code */ 139 #define IBM_EVENT_LCD_BACKLIGHT 0x03 140 #define IBM_EVENT_SUSPEND_TO_RAM 0x04 141 #define IBM_EVENT_BLUETOOTH 0x05 142 #define IBM_EVENT_SCREEN_EXPAND 0x07 143 #define IBM_EVENT_SUSPEND_TO_DISK 0x0c 144 #define IBM_EVENT_BRIGHTNESS_UP 0x10 145 #define IBM_EVENT_BRIGHTNESS_DOWN 0x11 146 #define IBM_EVENT_THINKLIGHT 0x12 147 #define IBM_EVENT_ZOOM 0x14 148 #define IBM_EVENT_VOLUME_UP 0x15 149 #define IBM_EVENT_VOLUME_DOWN 0x16 150 #define IBM_EVENT_MUTE 0x17 151 #define IBM_EVENT_ACCESS_IBM_BUTTON 0x18 152 153 /* Device-specific register flags */ 154 #define IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT 0x10000 155 #define IBM_FLAG_PRIVACYGUARD_ON 0x1 156 157 #define ABS(x) (((x) < 0)? -(x) : (x)) 158 159 struct acpi_ibm_softc { 160 device_t dev; 161 ACPI_HANDLE handle; 162 163 /* Embedded controller */ 164 device_t ec_dev; 165 ACPI_HANDLE ec_handle; 166 167 /* CMOS */ 168 ACPI_HANDLE cmos_handle; 169 170 /* Fan status */ 171 ACPI_HANDLE fan_handle; 172 int fan_levels; 173 174 /* Keylight commands and states */ 175 ACPI_HANDLE light_handle; 176 int light_cmd_on; 177 int light_cmd_off; 178 int light_val; 179 int light_get_supported; 180 int light_set_supported; 181 182 /* led(4) interface */ 183 struct cdev *led_dev; 184 int led_busy; 185 int led_state; 186 187 /* Mic led handle */ 188 ACPI_HANDLE mic_led_handle; 189 int mic_led_state; 190 191 int wlan_bt_flags; 192 int thermal_updt_supported; 193 194 unsigned int events_availmask; 195 unsigned int events_initialmask; 196 int events_mask_supported; 197 int events_enable; 198 199 unsigned int handler_events; 200 201 struct sysctl_ctx_list *sysctl_ctx; 202 struct sysctl_oid *sysctl_tree; 203 }; 204 205 static struct { 206 char *name; 207 int method; 208 char *description; 209 int flag_rdonly; 210 } acpi_ibm_sysctls[] = { 211 { 212 .name = "events", 213 .method = ACPI_IBM_METHOD_EVENTS, 214 .description = "ACPI events enable", 215 }, 216 { 217 .name = "eventmask", 218 .method = ACPI_IBM_METHOD_EVENTMASK, 219 .description = "ACPI eventmask", 220 }, 221 { 222 .name = "hotkey", 223 .method = ACPI_IBM_METHOD_HOTKEY, 224 .description = "Key Status", 225 .flag_rdonly = 1 226 }, 227 { 228 .name = "lcd_brightness", 229 .method = ACPI_IBM_METHOD_BRIGHTNESS, 230 .description = "LCD Brightness", 231 }, 232 { 233 .name = "volume", 234 .method = ACPI_IBM_METHOD_VOLUME, 235 .description = "Volume", 236 }, 237 { 238 .name = "mute", 239 .method = ACPI_IBM_METHOD_MUTE, 240 .description = "Mute", 241 }, 242 { 243 .name = "thinklight", 244 .method = ACPI_IBM_METHOD_THINKLIGHT, 245 .description = "Thinklight enable", 246 }, 247 { 248 .name = "bluetooth", 249 .method = ACPI_IBM_METHOD_BLUETOOTH, 250 .description = "Bluetooth enable", 251 }, 252 { 253 .name = "wlan", 254 .method = ACPI_IBM_METHOD_WLAN, 255 .description = "WLAN enable", 256 .flag_rdonly = 1 257 }, 258 { 259 .name = "fan_speed", 260 .method = ACPI_IBM_METHOD_FANSPEED, 261 .description = "Fan speed", 262 .flag_rdonly = 1 263 }, 264 { 265 .name = "fan_level", 266 .method = ACPI_IBM_METHOD_FANLEVEL, 267 .description = "Fan level, 0-7 (recommended max), " 268 "8 (unthrottled, full-speed)", 269 }, 270 { 271 .name = "fan", 272 .method = ACPI_IBM_METHOD_FANSTATUS, 273 .description = "Fan enable", 274 }, 275 { 276 .name = "mic_led", 277 .method = ACPI_IBM_METHOD_MIC_LED, 278 .description = "Mic led", 279 }, 280 { 281 .name = "privacyguard", 282 .method = ACPI_IBM_METHOD_PRIVACYGUARD, 283 .description = "PrivacyGuard enable", 284 }, 285 { NULL, 0, NULL, 0 } 286 }; 287 288 /* 289 * Per-model default list of event mask. 290 */ 291 #define ACPI_IBM_HKEY_RFKILL_MASK (1 << 4) 292 #define ACPI_IBM_HKEY_DSWITCH_MASK (1 << 6) 293 #define ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK (1 << 15) 294 #define ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK (1 << 16) 295 #define ACPI_IBM_HKEY_SEARCH_MASK (1 << 18) 296 #define ACPI_IBM_HKEY_MICMUTE_MASK (1 << 26) 297 #define ACPI_IBM_HKEY_SETTINGS_MASK (1 << 28) 298 #define ACPI_IBM_HKEY_VIEWOPEN_MASK (1 << 30) 299 #define ACPI_IBM_HKEY_VIEWALL_MASK (1 << 31) 300 301 struct acpi_ibm_models { 302 const char *maker; 303 const char *product; 304 uint32_t eventmask; 305 } acpi_ibm_models[] = { 306 { "LENOVO", "20BSCTO1WW", 307 ACPI_IBM_HKEY_RFKILL_MASK | 308 ACPI_IBM_HKEY_DSWITCH_MASK | 309 ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK | 310 ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK | 311 ACPI_IBM_HKEY_SEARCH_MASK | 312 ACPI_IBM_HKEY_MICMUTE_MASK | 313 ACPI_IBM_HKEY_SETTINGS_MASK | 314 ACPI_IBM_HKEY_VIEWOPEN_MASK | 315 ACPI_IBM_HKEY_VIEWALL_MASK 316 } 317 }; 318 319 ACPI_SERIAL_DECL(ibm, "ThinkPad ACPI Extras"); 320 321 static int acpi_ibm_probe(device_t dev); 322 static int acpi_ibm_attach(device_t dev); 323 static int acpi_ibm_detach(device_t dev); 324 static int acpi_ibm_resume(device_t dev); 325 326 static void ibm_led(void *softc, int onoff); 327 static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); 328 329 static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); 330 static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); 331 static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); 332 static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); 333 334 static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); 335 static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); 336 static int acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS); 337 static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); 338 339 static int acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg); 340 static int acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg); 341 static int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg); 342 static int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg); 343 static int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg); 344 static int acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc); 345 static ACPI_STATUS acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg); 346 static ACPI_STATUS acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg); 347 348 static int acpi_status_to_errno(ACPI_STATUS status); 349 350 static device_method_t acpi_ibm_methods[] = { 351 /* Device interface */ 352 DEVMETHOD(device_probe, acpi_ibm_probe), 353 DEVMETHOD(device_attach, acpi_ibm_attach), 354 DEVMETHOD(device_detach, acpi_ibm_detach), 355 DEVMETHOD(device_resume, acpi_ibm_resume), 356 357 DEVMETHOD_END 358 }; 359 360 static driver_t acpi_ibm_driver = { 361 "acpi_ibm", 362 acpi_ibm_methods, 363 sizeof(struct acpi_ibm_softc), 364 }; 365 366 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, 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_MPSAFE, 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_MPSAFE, 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_MPSAFE, 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_MPSAFE, 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 if (val_ec & IBM_EC_MASK_FANUNTHROTTLED) 833 val = 8; 834 else 835 val = val_ec & IBM_EC_MASK_FANLEVEL; 836 } 837 break; 838 839 case ACPI_IBM_METHOD_FANSTATUS: 840 if (!sc->fan_handle) { 841 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 842 val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; 843 } else 844 val = -1; 845 break; 846 847 case ACPI_IBM_METHOD_MIC_LED: 848 if (sc->mic_led_handle) 849 return sc->mic_led_state; 850 else 851 val = -1; 852 break; 853 854 case ACPI_IBM_METHOD_PRIVACYGUARD: 855 val = acpi_ibm_privacyguard_get(sc); 856 break; 857 } 858 859 return (val); 860 } 861 862 static int 863 acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) 864 { 865 int val; 866 UINT64 val_ec; 867 ACPI_STATUS status; 868 869 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 870 ACPI_SERIAL_ASSERT(ibm); 871 872 switch (method) { 873 case ACPI_IBM_METHOD_EVENTS: 874 if (arg < 0 || arg > 1) 875 return (EINVAL); 876 877 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); 878 if (ACPI_FAILURE(status)) 879 return (status); 880 if (sc->events_mask_supported) 881 return acpi_ibm_eventmask_set(sc, sc->events_availmask); 882 break; 883 884 case ACPI_IBM_METHOD_EVENTMASK: 885 if (sc->events_mask_supported) 886 return acpi_ibm_eventmask_set(sc, arg); 887 break; 888 889 case ACPI_IBM_METHOD_BRIGHTNESS: 890 return acpi_ibm_brightness_set(sc, arg); 891 break; 892 893 case ACPI_IBM_METHOD_VOLUME: 894 return acpi_ibm_volume_set(sc, arg); 895 break; 896 897 case ACPI_IBM_METHOD_MUTE: 898 return acpi_ibm_mute_set(sc, arg); 899 break; 900 901 case ACPI_IBM_METHOD_MIC_LED: 902 return acpi_ibm_mic_led_set(sc, arg); 903 break; 904 905 case ACPI_IBM_METHOD_THINKLIGHT: 906 return acpi_ibm_thinklight_set(sc, arg); 907 break; 908 909 case ACPI_IBM_METHOD_BLUETOOTH: 910 return acpi_ibm_bluetooth_set(sc, arg); 911 break; 912 913 case ACPI_IBM_METHOD_PRIVACYGUARD: 914 return (acpi_status_to_errno(acpi_ibm_privacyguard_set(sc, arg))); 915 break; 916 917 case ACPI_IBM_METHOD_FANLEVEL: 918 if (arg < 0 || arg > 8) 919 return (EINVAL); 920 921 if (!sc->fan_handle) { 922 /* Read the current fan status. */ 923 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 924 val = val_ec & ~(IBM_EC_MASK_FANLEVEL | 925 IBM_EC_MASK_FANUNTHROTTLED); 926 927 if (arg == 8) 928 /* Full speed, set the unthrottled bit. */ 929 val |= 7 | IBM_EC_MASK_FANUNTHROTTLED; 930 else 931 val |= arg; 932 933 return (ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val, 934 1)); 935 } 936 break; 937 938 case ACPI_IBM_METHOD_FANSTATUS: 939 if (arg < 0 || arg > 1) 940 return (EINVAL); 941 942 if (!sc->fan_handle) { 943 /* Read the current fanstatus */ 944 ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 945 946 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, 947 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); 948 } 949 break; 950 } 951 952 return (0); 953 } 954 955 static int 956 acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) 957 { 958 int dummy; 959 ACPI_OBJECT_TYPE cmos_t; 960 ACPI_HANDLE ledb_handle; 961 962 switch (method) { 963 case ACPI_IBM_METHOD_EVENTS: 964 return (TRUE); 965 966 case ACPI_IBM_METHOD_EVENTMASK: 967 return (sc->events_mask_supported); 968 969 case ACPI_IBM_METHOD_HOTKEY: 970 case ACPI_IBM_METHOD_BRIGHTNESS: 971 case ACPI_IBM_METHOD_VOLUME: 972 case ACPI_IBM_METHOD_MUTE: 973 /* EC is required here, which was already checked before */ 974 return (TRUE); 975 976 case ACPI_IBM_METHOD_MIC_LED: 977 if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "MMTS", &sc->mic_led_handle))) 978 { 979 /* Turn off mic led by default */ 980 acpi_ibm_mic_led_set(sc, 0); 981 return (TRUE); 982 } else 983 sc->mic_led_handle = NULL; 984 return (FALSE); 985 986 case ACPI_IBM_METHOD_THINKLIGHT: 987 sc->cmos_handle = NULL; 988 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 989 sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); 990 991 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || 992 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || 993 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && 994 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 995 cmos_t == ACPI_TYPE_METHOD) { 996 sc->light_cmd_on = 0x0c; 997 sc->light_cmd_off = 0x0d; 998 sc->cmos_handle = sc->light_handle; 999 } 1000 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { 1001 sc->light_cmd_on = 1; 1002 sc->light_cmd_off = 0; 1003 } else 1004 sc->light_handle = NULL; 1005 1006 sc->light_set_supported = (sc->light_handle && 1007 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); 1008 1009 if (sc->light_get_supported) 1010 return (TRUE); 1011 1012 if (sc->light_set_supported) { 1013 sc->light_val = 0; 1014 return (TRUE); 1015 } 1016 1017 return (FALSE); 1018 1019 case ACPI_IBM_METHOD_BLUETOOTH: 1020 case ACPI_IBM_METHOD_WLAN: 1021 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) 1022 return (TRUE); 1023 return (FALSE); 1024 1025 case ACPI_IBM_METHOD_FANSPEED: 1026 /* 1027 * Some models report the fan speed in levels from 0-7 1028 * Newer models report it contiguously 1029 */ 1030 sc->fan_levels = 1031 (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || 1032 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); 1033 return (TRUE); 1034 1035 case ACPI_IBM_METHOD_FANLEVEL: 1036 case ACPI_IBM_METHOD_FANSTATUS: 1037 /* 1038 * Fan status is only supported on those models, 1039 * which report fan RPM contiguously, not in levels 1040 */ 1041 if (sc->fan_levels) 1042 return (FALSE); 1043 return (TRUE); 1044 1045 case ACPI_IBM_METHOD_THERMAL: 1046 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { 1047 sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); 1048 return (TRUE); 1049 } 1050 return (FALSE); 1051 1052 case ACPI_IBM_METHOD_HANDLEREVENTS: 1053 return (TRUE); 1054 1055 case ACPI_IBM_METHOD_PRIVACYGUARD: 1056 return (acpi_ibm_privacyguard_get(sc) != -1); 1057 } 1058 return (FALSE); 1059 } 1060 1061 static int 1062 acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) 1063 { 1064 struct acpi_ibm_softc *sc; 1065 int error = 0; 1066 char temp_cmd[] = "TMP0"; 1067 int temp[8]; 1068 1069 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1070 1071 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 1072 1073 ACPI_SERIAL_BEGIN(ibm); 1074 1075 for (int i = 0; i < 8; ++i) { 1076 temp_cmd[3] = '0' + i; 1077 1078 /* 1079 * The TMPx methods seem to return +/- 128 or 0 1080 * when the respecting sensor is not available 1081 */ 1082 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 1083 &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) 1084 temp[i] = -1; 1085 else if (sc->thermal_updt_supported) 1086 /* Temperature is reported in tenth of Kelvin */ 1087 temp[i] = (temp[i] - 2731 + 5) / 10; 1088 } 1089 1090 error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); 1091 1092 ACPI_SERIAL_END(ibm); 1093 return (error); 1094 } 1095 1096 static int 1097 acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS) 1098 { 1099 struct acpi_ibm_softc *sc; 1100 int error = 0; 1101 struct sbuf sb; 1102 char *cp, *ep; 1103 int l, val; 1104 unsigned int handler_events; 1105 char temp[128]; 1106 1107 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1108 1109 sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 1110 1111 if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) 1112 return (ENOMEM); 1113 1114 ACPI_SERIAL_BEGIN(ibm); 1115 1116 /* Get old values if this is a get request. */ 1117 if (req->newptr == NULL) { 1118 for (int i = 0; i < 8 * sizeof(sc->handler_events); i++) 1119 if (sc->handler_events & (1 << i)) 1120 sbuf_printf(&sb, "0x%02x ", i + 1); 1121 if (sbuf_len(&sb) == 0) 1122 sbuf_printf(&sb, "NONE"); 1123 } 1124 1125 sbuf_trim(&sb); 1126 sbuf_finish(&sb); 1127 strlcpy(temp, sbuf_data(&sb), sizeof(temp)); 1128 sbuf_delete(&sb); 1129 1130 error = sysctl_handle_string(oidp, temp, sizeof(temp), req); 1131 1132 /* Check for error or no change */ 1133 if (error != 0 || req->newptr == NULL) 1134 goto out; 1135 1136 /* If the user is setting a string, parse it. */ 1137 handler_events = 0; 1138 cp = temp; 1139 while (*cp) { 1140 if (isspace(*cp)) { 1141 cp++; 1142 continue; 1143 } 1144 1145 ep = cp; 1146 1147 while (*ep && !isspace(*ep)) 1148 ep++; 1149 1150 l = ep - cp; 1151 if (l == 0) 1152 break; 1153 1154 if (strncmp(cp, "NONE", 4) == 0) { 1155 cp = ep; 1156 continue; 1157 } 1158 1159 if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x')) 1160 val = strtoul(cp, &ep, 16); 1161 else 1162 val = strtoul(cp, &ep, 10); 1163 1164 if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) { 1165 cp[l] = '\0'; 1166 device_printf(sc->dev, "invalid event code: %s\n", cp); 1167 error = EINVAL; 1168 goto out; 1169 } 1170 1171 handler_events |= 1 << (val - 1); 1172 1173 cp = ep; 1174 } 1175 1176 sc->handler_events = handler_events; 1177 out: 1178 ACPI_SERIAL_END(ibm); 1179 return (error); 1180 } 1181 1182 static int 1183 acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg) 1184 { 1185 int val, step; 1186 UINT64 val_ec; 1187 ACPI_OBJECT Arg; 1188 ACPI_OBJECT_LIST Args; 1189 ACPI_STATUS status; 1190 1191 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1192 ACPI_SERIAL_ASSERT(ibm); 1193 1194 if (arg < 0 || arg > 7) 1195 return (EINVAL); 1196 1197 /* Read the current brightness */ 1198 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 1199 if (ACPI_FAILURE(status)) 1200 return (status); 1201 1202 if (sc->cmos_handle) { 1203 val = val_ec & IBM_EC_MASK_BRI; 1204 1205 Args.Count = 1; 1206 Args.Pointer = &Arg; 1207 Arg.Type = ACPI_TYPE_INTEGER; 1208 Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : 1209 IBM_CMOS_BRIGHTNESS_DOWN; 1210 1211 step = (arg > val) ? 1 : -1; 1212 for (int i = val; i != arg; i += step) { 1213 status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1214 &Args, NULL); 1215 if (ACPI_FAILURE(status)) { 1216 /* Record the last value */ 1217 if (i != val) { 1218 ACPI_EC_WRITE(sc->ec_dev, 1219 IBM_EC_BRIGHTNESS, i - step, 1); 1220 } 1221 return (status); 1222 } 1223 } 1224 } 1225 1226 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); 1227 } 1228 1229 static int 1230 acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg) 1231 { 1232 int val; 1233 1234 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1235 ACPI_SERIAL_ASSERT(ibm); 1236 1237 if (arg < 0 || arg > 1) 1238 return (EINVAL); 1239 1240 val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : 1241 sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); 1242 return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); 1243 } 1244 1245 static int 1246 acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg) 1247 { 1248 ACPI_OBJECT Arg; 1249 ACPI_OBJECT_LIST Args; 1250 ACPI_STATUS status; 1251 1252 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1253 ACPI_SERIAL_ASSERT(ibm); 1254 1255 if (arg < 0 || arg > 1) 1256 return (EINVAL); 1257 1258 if (sc->light_set_supported) { 1259 Args.Count = 1; 1260 Args.Pointer = &Arg; 1261 Arg.Type = ACPI_TYPE_INTEGER; 1262 Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; 1263 1264 status = AcpiEvaluateObject(sc->light_handle, NULL, 1265 &Args, NULL); 1266 if (ACPI_SUCCESS(status)) 1267 sc->light_val = arg; 1268 return (status); 1269 } 1270 1271 return (0); 1272 } 1273 1274 /* 1275 * Helper function to make a get or set ACPI call to the PrivacyGuard handle. 1276 * Only meant to be used internally by the get/set functions below. 1277 */ 1278 static ACPI_STATUS 1279 acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg) 1280 { 1281 ACPI_OBJECT Arg; 1282 ACPI_OBJECT_LIST Args; 1283 ACPI_STATUS status; 1284 ACPI_OBJECT out_obj; 1285 ACPI_BUFFER result; 1286 1287 Arg.Type = ACPI_TYPE_INTEGER; 1288 Arg.Integer.Value = (write ? *arg : 0); 1289 Args.Count = 1; 1290 Args.Pointer = &Arg; 1291 result.Length = sizeof(out_obj); 1292 result.Pointer = &out_obj; 1293 1294 status = AcpiEvaluateObject(sc->handle, 1295 (write ? IBM_NAME_PRIVACYGUARD_SET : IBM_NAME_PRIVACYGUARD_GET), 1296 &Args, &result); 1297 if (ACPI_SUCCESS(status) && !write) 1298 *arg = out_obj.Integer.Value; 1299 1300 return (status); 1301 } 1302 1303 /* 1304 * Returns -1 if the device is not present. 1305 */ 1306 static int 1307 acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc) 1308 { 1309 ACPI_STATUS status; 1310 int val; 1311 1312 status = acpi_ibm_privacyguard_acpi_call(sc, false, &val); 1313 if (ACPI_SUCCESS(status) && 1314 (val & IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT)) 1315 return (val & IBM_FLAG_PRIVACYGUARD_ON); 1316 1317 return (-1); 1318 } 1319 1320 static ACPI_STATUS 1321 acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg) 1322 { 1323 if (arg < 0 || arg > 1) 1324 return (AE_BAD_PARAMETER); 1325 1326 return (acpi_ibm_privacyguard_acpi_call(sc, true, &arg)); 1327 } 1328 1329 static int 1330 acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg) 1331 { 1332 int val, step; 1333 UINT64 val_ec; 1334 ACPI_OBJECT Arg; 1335 ACPI_OBJECT_LIST Args; 1336 ACPI_STATUS status; 1337 1338 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1339 ACPI_SERIAL_ASSERT(ibm); 1340 1341 if (arg < 0 || arg > 14) 1342 return (EINVAL); 1343 1344 /* Read the current volume */ 1345 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1346 if (ACPI_FAILURE(status)) 1347 return (status); 1348 1349 if (sc->cmos_handle) { 1350 val = val_ec & IBM_EC_MASK_VOL; 1351 1352 Args.Count = 1; 1353 Args.Pointer = &Arg; 1354 Arg.Type = ACPI_TYPE_INTEGER; 1355 Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : 1356 IBM_CMOS_VOLUME_DOWN; 1357 1358 step = (arg > val) ? 1 : -1; 1359 for (int i = val; i != arg; i += step) { 1360 status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1361 &Args, NULL); 1362 if (ACPI_FAILURE(status)) { 1363 /* Record the last value */ 1364 if (i != val) { 1365 val_ec = i - step + 1366 (val_ec & (~IBM_EC_MASK_VOL)); 1367 ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, 1368 val_ec, 1); 1369 } 1370 return (status); 1371 } 1372 } 1373 } 1374 1375 val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL)); 1376 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1377 } 1378 1379 static int 1380 acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg) 1381 { 1382 UINT64 val_ec; 1383 ACPI_OBJECT Arg; 1384 ACPI_OBJECT_LIST Args; 1385 ACPI_STATUS status; 1386 1387 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1388 ACPI_SERIAL_ASSERT(ibm); 1389 1390 if (arg < 0 || arg > 1) 1391 return (EINVAL); 1392 1393 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1394 if (ACPI_FAILURE(status)) 1395 return (status); 1396 1397 if (sc->cmos_handle) { 1398 Args.Count = 1; 1399 Args.Pointer = &Arg; 1400 Arg.Type = ACPI_TYPE_INTEGER; 1401 Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; 1402 1403 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 1404 if (ACPI_FAILURE(status)) 1405 return (status); 1406 } 1407 1408 val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE : 1409 val_ec & (~IBM_EC_MASK_MUTE); 1410 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1411 } 1412 1413 static void 1414 acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg) 1415 { 1416 int val; 1417 UINT64 val_ec; 1418 ACPI_STATUS status; 1419 1420 ACPI_SERIAL_BEGIN(ibm); 1421 switch (arg) { 1422 case IBM_EVENT_SUSPEND_TO_RAM: 1423 power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); 1424 break; 1425 1426 case IBM_EVENT_BLUETOOTH: 1427 acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0)); 1428 break; 1429 1430 case IBM_EVENT_BRIGHTNESS_UP: 1431 case IBM_EVENT_BRIGHTNESS_DOWN: 1432 /* Read the current brightness */ 1433 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, 1434 &val_ec, 1); 1435 if (ACPI_FAILURE(status)) 1436 return; 1437 1438 val = val_ec & IBM_EC_MASK_BRI; 1439 val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1; 1440 acpi_ibm_brightness_set(sc, val); 1441 break; 1442 1443 case IBM_EVENT_THINKLIGHT: 1444 acpi_ibm_thinklight_set(sc, (sc->light_val == 0)); 1445 break; 1446 1447 case IBM_EVENT_VOLUME_UP: 1448 case IBM_EVENT_VOLUME_DOWN: 1449 /* Read the current volume */ 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_VOL; 1455 val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1; 1456 acpi_ibm_volume_set(sc, val); 1457 break; 1458 1459 case IBM_EVENT_MUTE: 1460 /* Read the current value */ 1461 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1462 if (ACPI_FAILURE(status)) 1463 return; 1464 1465 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 1466 acpi_ibm_mute_set(sc, (val == 0)); 1467 break; 1468 1469 default: 1470 break; 1471 } 1472 ACPI_SERIAL_END(ibm); 1473 } 1474 1475 static void 1476 acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1477 { 1478 int event, arg, type; 1479 device_t dev = context; 1480 struct acpi_ibm_softc *sc = device_get_softc(dev); 1481 1482 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 1483 1484 if (notify != 0x80) 1485 device_printf(dev, "Unknown notify\n"); 1486 1487 for (;;) { 1488 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); 1489 if (event == 0) 1490 break; 1491 1492 type = (event >> 12) & 0xf; 1493 arg = event & 0xfff; 1494 switch (type) { 1495 case 1: 1496 if (!(sc->events_availmask & (1 << (arg - 1)))) { 1497 device_printf(dev, "Unknown key %d\n", arg); 1498 break; 1499 } 1500 1501 /* Execute event handler */ 1502 if (sc->handler_events & (1 << (arg - 1))) 1503 acpi_ibm_eventhandler(sc, (arg & 0xff)); 1504 1505 /* Notify devd(8) */ 1506 acpi_UserNotify("IBM", h, (arg & 0xff)); 1507 break; 1508 default: 1509 break; 1510 } 1511 } 1512 } 1513