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