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