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