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