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: src/sys/dev/acpi_support/acpi_ibm.c,v 1.15 2007/10/25 17:30:18 jhb Exp $ 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/sysctl.h> 47 #include <sys/lock.h> 48 #include <sys/thread2.h> 49 #include <machine/clock.h> 50 51 #include "acpi.h" 52 #include "accommon.h" 53 #include "acpivar.h" 54 #include "acpi_if.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 74 /* Hotkeys/Buttons */ 75 #define THINKPAD_RTC_HOTKEY1 0x64 76 #define THINKPAD_RTC_MASK_HOME (1 << 0) 77 #define THINKPAD_RTC_MASK_SEARCH (1 << 1) 78 #define THINKPAD_RTC_MASK_MAIL (1 << 2) 79 #define THINKPAD_RTC_MASK_WLAN (1 << 5) 80 #define THINKPAD_RTC_HOTKEY2 0x65 81 #define THINKPAD_RTC_MASK_THINKPAD (1 << 3) 82 #define THINKPAD_RTC_MASK_ZOOM (1 << 5) 83 #define THINKPAD_RTC_MASK_VIDEO (1 << 6) 84 #define THINKPAD_RTC_MASK_HIBERNATE (1 << 7) 85 #define THINKPAD_RTC_THINKLIGHT 0x66 86 #define THINKPAD_RTC_MASK_THINKLIGHT (1 << 4) 87 #define THINKPAD_RTC_SCREENEXPAND 0x67 88 #define THINKPAD_RTC_MASK_SCREENEXPAND (1 << 5) 89 #define THINKPAD_RTC_BRIGHTNESS 0x6c 90 #define THINKPAD_RTC_MASK_BRIGHTNESS (1 << 5) 91 #define THINKPAD_RTC_VOLUME 0x6e 92 #define THINKPAD_RTC_MASK_VOLUME (1 << 7) 93 94 /* Embedded Controller registers */ 95 #define THINKPAD_EC_BRIGHTNESS 0x31 96 #define THINKPAD_EC_MASK_BRI 0x7 97 #define THINKPAD_EC_VOLUME 0x30 98 #define THINKPAD_EC_MASK_VOL 0xf 99 #define THINKPAD_EC_MASK_MUTE (1 << 6) 100 #define THINKPAD_EC_FANSTATUS 0x2F 101 #define THINKPAD_EC_MASK_FANLEVEL 0x3f 102 #define THINKPAD_EC_MASK_FANDISENGAGED (1 << 6) 103 #define THINKPAD_EC_MASK_FANSTATUS (1 << 7) 104 #define THINKPAD_EC_FANSPEED 0x84 105 106 /* CMOS Commands */ 107 #define THINKPAD_CMOS_VOLUME_DOWN 0 108 #define THINKPAD_CMOS_VOLUME_UP 1 109 #define THINKPAD_CMOS_VOLUME_MUTE 2 110 #define THINKPAD_CMOS_BRIGHTNESS_UP 4 111 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 5 112 113 /* ACPI methods */ 114 #define THINKPAD_NAME_KEYLIGHT "KBLT" 115 #define THINKPAD_NAME_WLAN_BT_GET "GBDC" 116 #define THINKPAD_NAME_WLAN_BT_SET "SBDC" 117 #define THINKPAD_NAME_MASK_BT (1 << 1) 118 #define THINKPAD_NAME_MASK_WLAN (1 << 2) 119 #define THINKPAD_NAME_THERMAL_GET "TMP7" 120 #define THINKPAD_NAME_THERMAL_UPDT "UPDT" 121 122 #define THINKPAD_NAME_EVENTS_STATUS_GET "DHKC" 123 #define THINKPAD_NAME_EVENTS_MASK_GET "DHKN" 124 #define THINKPAD_NAME_EVENTS_STATUS_SET "MHKC" 125 #define THINKPAD_NAME_EVENTS_MASK_SET "MHKM" 126 #define THINKPAD_NAME_EVENTS_GET "MHKP" 127 #define THINKPAD_NAME_EVENTS_AVAILMASK "MHKA" 128 129 #define THINKPAD_NUM_SENSORS 9 130 #define THINKPAD_TEMP_SENSORS 8 131 132 #define ABS(x) (((x) < 0)? -(x) : (x)) 133 134 struct acpi_thinkpad_softc { 135 device_t dev; 136 ACPI_HANDLE handle; 137 138 /* Embedded controller */ 139 device_t ec_dev; 140 ACPI_HANDLE ec_handle; 141 142 /* CMOS */ 143 ACPI_HANDLE cmos_handle; 144 145 /* Fan status */ 146 ACPI_HANDLE fan_handle; 147 int fan_levels; 148 149 /* Keylight commands and states */ 150 ACPI_HANDLE light_handle; 151 int light_cmd_on; 152 int light_cmd_off; 153 int light_val; 154 int light_get_supported; 155 int light_set_supported; 156 157 /* led(4) interface */ 158 struct cdev *led_dev; 159 int led_busy; 160 int led_state; 161 162 int wlan_bt_flags; 163 int thermal_updt_supported; 164 165 unsigned int events_availmask; 166 unsigned int events_initialmask; 167 int events_mask_supported; 168 int events_enable; 169 170 /* sensors(9) related */ 171 struct ksensordev sensordev; 172 struct ksensor sensors[THINKPAD_NUM_SENSORS]; 173 174 struct sysctl_ctx_list sysctl_ctx; 175 struct sysctl_oid *sysctl_tree; 176 }; 177 178 static struct { 179 char *name; 180 int method; 181 char *description; 182 int access; 183 } acpi_thinkpad_sysctls[] = { 184 { 185 .name = "events", 186 .method = ACPI_THINKPAD_METHOD_EVENTS, 187 .description = "ACPI events enable", 188 .access = CTLTYPE_INT | CTLFLAG_RW 189 }, 190 { 191 .name = "eventmask", 192 .method = ACPI_THINKPAD_METHOD_EVENTMASK, 193 .description = "ACPI eventmask", 194 .access = CTLTYPE_INT | CTLFLAG_RW 195 }, 196 { 197 .name = "hotkey", 198 .method = ACPI_THINKPAD_METHOD_HOTKEY, 199 .description = "Key Status", 200 .access = CTLTYPE_INT | CTLFLAG_RD 201 }, 202 { 203 .name = "lcd_brightness", 204 .method = ACPI_THINKPAD_METHOD_BRIGHTNESS, 205 .description = "LCD Brightness", 206 .access = CTLTYPE_INT | CTLFLAG_RW 207 }, 208 { 209 .name = "volume", 210 .method = ACPI_THINKPAD_METHOD_VOLUME, 211 .description = "Volume", 212 .access = CTLTYPE_INT | CTLFLAG_RW 213 }, 214 { 215 .name = "mute", 216 .method = ACPI_THINKPAD_METHOD_MUTE, 217 .description = "Mute", 218 .access = CTLTYPE_INT | CTLFLAG_RW 219 }, 220 { 221 .name = "thinklight", 222 .method = ACPI_THINKPAD_METHOD_THINKLIGHT, 223 .description = "Thinklight enable", 224 .access = CTLTYPE_INT | CTLFLAG_RW 225 }, 226 { 227 .name = "bluetooth", 228 .method = ACPI_THINKPAD_METHOD_BLUETOOTH, 229 .description = "Bluetooth enable", 230 .access = CTLTYPE_INT | CTLFLAG_RW 231 }, 232 { 233 .name = "wlan", 234 .method = ACPI_THINKPAD_METHOD_WLAN, 235 .description = "WLAN enable", 236 .access = CTLTYPE_INT | CTLFLAG_RD 237 }, 238 { 239 .name = "fan_level", 240 .method = ACPI_THINKPAD_METHOD_FANLEVEL, 241 .description = "Fan level", 242 .access = CTLTYPE_INT | CTLFLAG_RW 243 }, 244 { 245 .name = "fan", 246 .method = ACPI_THINKPAD_METHOD_FANSTATUS, 247 .description = "Fan enable", 248 .access = CTLTYPE_INT | CTLFLAG_RW 249 }, 250 251 { NULL, 0, NULL, 0 } 252 }; 253 254 static struct lock tplock; 255 256 static int acpi_thinkpad_probe(device_t dev); 257 static int acpi_thinkpad_attach(device_t dev); 258 static int acpi_thinkpad_detach(device_t dev); 259 260 static int acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS); 261 static int acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, 262 int method); 263 static int acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, 264 int method); 265 static int acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, 266 int method, int val); 267 268 static int acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, 269 int val); 270 static void acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, 271 void *context); 272 static void acpi_thinkpad_refresh(void *); 273 274 static device_method_t acpi_thinkpad_methods[] = { 275 /* Device interface */ 276 DEVMETHOD(device_probe, acpi_thinkpad_probe), 277 DEVMETHOD(device_attach, acpi_thinkpad_attach), 278 DEVMETHOD(device_detach, acpi_thinkpad_detach), 279 DEVMETHOD_END 280 }; 281 282 static driver_t acpi_thinkpad_driver = { 283 "acpi_thinkpad", 284 acpi_thinkpad_methods, 285 sizeof(struct acpi_thinkpad_softc), 286 }; 287 288 static devclass_t acpi_thinkpad_devclass; 289 290 DRIVER_MODULE(acpi_thinkpad, acpi, acpi_thinkpad_driver, 291 acpi_thinkpad_devclass, 0, 0); 292 MODULE_DEPEND(acpi_thinkpad, acpi, 1, 1, 1); 293 static char *thinkpad_ids[] = {"IBM0068", NULL}; 294 295 static int 296 acpi_thinkpad_probe(device_t dev) 297 { 298 if (acpi_disabled("thinkpad") || 299 ACPI_ID_PROBE(device_get_parent(dev), dev, thinkpad_ids) == NULL || 300 device_get_unit(dev) != 0) 301 return (ENXIO); 302 303 device_set_desc(dev, "IBM/Lenovo ThinkPad ACPI Extras"); 304 return (0); 305 } 306 307 static int 308 acpi_thinkpad_attach(device_t dev) 309 { 310 struct acpi_thinkpad_softc *sc; 311 struct acpi_softc *acpi_sc; 312 devclass_t ec_devclass; 313 int i; 314 315 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 316 317 sc = device_get_softc(dev); 318 sc->dev = dev; 319 sc->handle = acpi_get_handle(dev); 320 321 acpi_sc = acpi_device_get_parent_softc(dev); 322 323 /* Look for the first embedded controller */ 324 if (!(ec_devclass = devclass_find ("acpi_ec"))) { 325 if (bootverbose) 326 device_printf(dev, "Couldn't find acpi_ec devclass\n"); 327 return (EINVAL); 328 } 329 if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { 330 if (bootverbose) 331 device_printf(dev, "Couldn't find acpi_ec device\n"); 332 return (EINVAL); 333 } 334 sc->ec_handle = acpi_get_handle(sc->ec_dev); 335 336 lockinit(&tplock, "thinkpad", 0, 0); 337 lockmgr(&tplock, LK_EXCLUSIVE); 338 339 sysctl_ctx_init(&sc->sysctl_ctx); 340 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 341 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 342 "thinkpad", CTLFLAG_RD, 0, ""); 343 344 /* Look for event mask and hook up the nodes */ 345 sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, 346 THINKPAD_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); 347 348 if (sc->events_mask_supported) { 349 SYSCTL_ADD_INT(&sc->sysctl_ctx, 350 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 351 "initialmask", CTLFLAG_RD, 352 &sc->events_initialmask, 0, "Initial eventmask"); 353 354 /* The availmask is the bitmask of supported events */ 355 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 356 THINKPAD_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) 357 sc->events_availmask = 0xffffffff; 358 359 SYSCTL_ADD_INT(&sc->sysctl_ctx, 360 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 361 "availmask", CTLFLAG_RD, 362 &sc->events_availmask, 0, "Mask of supported events"); 363 } 364 365 /* Hook up proc nodes */ 366 for (i = 0; acpi_thinkpad_sysctls[i].name != NULL; i++) { 367 if (!acpi_thinkpad_sysctl_init(sc, 368 acpi_thinkpad_sysctls[i].method)) 369 continue; 370 371 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 372 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 373 acpi_thinkpad_sysctls[i].name, 374 acpi_thinkpad_sysctls[i].access, 375 sc, i, acpi_thinkpad_sysctl, "I", 376 acpi_thinkpad_sysctls[i].description); 377 } 378 379 lockmgr(&tplock, LK_RELEASE); 380 381 /* Handle notifies */ 382 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 383 acpi_thinkpad_notify, dev); 384 385 /* Attach sensors(9). */ 386 if (sensor_task_register(sc, acpi_thinkpad_refresh, 5)) { 387 device_printf(sc->dev, "unable to register update task\n"); 388 return 1; 389 } 390 391 strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev), 392 sizeof(sc->sensordev.xname)); 393 394 for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) { 395 sc->sensors[i].type = SENSOR_TEMP; 396 sensor_attach(&sc->sensordev, &sc->sensors[i]); 397 } 398 399 sc->sensors[i].type = SENSOR_FANRPM; 400 sensor_attach(&sc->sensordev, &sc->sensors[i]); 401 402 sensordev_install(&sc->sensordev); 403 404 return (0); 405 } 406 407 static int 408 acpi_thinkpad_detach(device_t dev) 409 { 410 int i; 411 412 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 413 414 struct acpi_thinkpad_softc *sc = device_get_softc(dev); 415 416 /* Disable events and restore eventmask */ 417 lockmgr(&tplock, LK_EXCLUSIVE); 418 acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTS, 0); 419 acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTMASK, 420 sc->events_initialmask); 421 lockmgr(&tplock, LK_RELEASE); 422 423 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 424 acpi_thinkpad_notify); 425 426 if (sc->sysctl_tree != NULL) 427 sysctl_ctx_free(&sc->sysctl_ctx); 428 429 sensordev_deinstall(&sc->sensordev); 430 for (i = 0; i < THINKPAD_NUM_SENSORS; i++) 431 sensor_detach(&sc->sensordev, &sc->sensors[i]); 432 sensor_task_unregister(sc); 433 434 return (0); 435 } 436 437 static int 438 acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, int val) 439 { 440 int i; 441 ACPI_OBJECT arg[2]; 442 ACPI_OBJECT_LIST args; 443 ACPI_STATUS status; 444 445 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 446 KKASSERT(lockstatus(&tplock, curthread) != 0); 447 448 args.Count = 2; 449 args.Pointer = arg; 450 arg[0].Type = ACPI_TYPE_INTEGER; 451 arg[1].Type = ACPI_TYPE_INTEGER; 452 453 for (i = 0; i < 32; ++i) { 454 arg[0].Integer.Value = i+1; 455 arg[1].Integer.Value = (((1 << i) & val) != 0); 456 status = AcpiEvaluateObject(sc->handle, 457 THINKPAD_NAME_EVENTS_MASK_SET, &args, NULL); 458 459 if (ACPI_FAILURE(status)) 460 return (status); 461 } 462 463 return (0); 464 } 465 466 static int 467 acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS) 468 { 469 struct acpi_thinkpad_softc *sc; 470 int arg; 471 int error = 0; 472 int function; 473 int method; 474 475 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 476 477 sc = (struct acpi_thinkpad_softc *)oidp->oid_arg1; 478 function = oidp->oid_arg2; 479 method = acpi_thinkpad_sysctls[function].method; 480 481 lockmgr(&tplock, LK_EXCLUSIVE); 482 arg = acpi_thinkpad_sysctl_get(sc, method); 483 error = sysctl_handle_int(oidp, &arg, 0, req); 484 485 /* Sanity check */ 486 if (error != 0 || req->newptr == NULL) 487 goto out; 488 489 /* Update */ 490 error = acpi_thinkpad_sysctl_set(sc, method, arg); 491 492 out: 493 lockmgr(&tplock, LK_RELEASE); 494 return (error); 495 } 496 497 static int 498 acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method) 499 { 500 ACPI_INTEGER val_ec; 501 int val = 0, key; 502 503 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 504 KKASSERT(lockstatus(&tplock, curthread) != 0); 505 506 switch (method) { 507 case ACPI_THINKPAD_METHOD_EVENTS: 508 acpi_GetInteger(sc->handle, THINKPAD_NAME_EVENTS_STATUS_GET, 509 &val); 510 break; 511 512 case ACPI_THINKPAD_METHOD_EVENTMASK: 513 if (sc->events_mask_supported) 514 acpi_GetInteger(sc->handle, 515 THINKPAD_NAME_EVENTS_MASK_GET, &val); 516 break; 517 518 case ACPI_THINKPAD_METHOD_HOTKEY: 519 /* 520 * Construct the hotkey as a bitmask as illustrated below. 521 * Note that whenever a key was pressed, the respecting bit 522 * toggles and nothing else changes. 523 * +--+--+-+-+-+-+-+-+-+-+-+-+ 524 * |11|10|9|8|7|6|5|4|3|2|1|0| 525 * +--+--+-+-+-+-+-+-+-+-+-+-+ 526 * | | | | | | | | | | | | 527 * | | | | | | | | | | | +- Home Button 528 * | | | | | | | | | | +--- Search Button 529 * | | | | | | | | | +----- Mail Button 530 * | | | | | | | | +------- Thinkpad Button 531 * | | | | | | | +--------- Zoom (Fn + Space) 532 * | | | | | | +----------- WLAN Button 533 * | | | | | +------------- Video Button 534 * | | | | +--------------- Hibernate Button 535 * | | | +----------------- Thinklight Button 536 * | | +------------------- Screen expand (Fn + F8) 537 * | +--------------------- Brightness 538 * +------------------------ Volume/Mute 539 */ 540 key = rtcin(THINKPAD_RTC_HOTKEY1); 541 val = (THINKPAD_RTC_MASK_HOME | THINKPAD_RTC_MASK_SEARCH | 542 THINKPAD_RTC_MASK_MAIL | THINKPAD_RTC_MASK_WLAN) & key; 543 key = rtcin(THINKPAD_RTC_HOTKEY2); 544 val |= (THINKPAD_RTC_MASK_THINKPAD | THINKPAD_RTC_MASK_VIDEO | 545 THINKPAD_RTC_MASK_HIBERNATE) & key; 546 val |= (THINKPAD_RTC_MASK_ZOOM & key) >> 1; 547 key = rtcin(THINKPAD_RTC_THINKLIGHT); 548 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4; 549 key = rtcin(THINKPAD_RTC_SCREENEXPAND); 550 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4; 551 key = rtcin(THINKPAD_RTC_BRIGHTNESS); 552 val |= (THINKPAD_RTC_MASK_BRIGHTNESS & key) << 5; 553 key = rtcin(THINKPAD_RTC_VOLUME); 554 val |= (THINKPAD_RTC_MASK_VOLUME & key) << 4; 555 break; 556 557 case ACPI_THINKPAD_METHOD_BRIGHTNESS: 558 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, &val_ec, 1); 559 val = val_ec & THINKPAD_EC_MASK_BRI; 560 break; 561 562 case ACPI_THINKPAD_METHOD_VOLUME: 563 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1); 564 val = val_ec & THINKPAD_EC_MASK_VOL; 565 break; 566 567 case ACPI_THINKPAD_METHOD_MUTE: 568 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1); 569 val = ((val_ec & THINKPAD_EC_MASK_MUTE) == 570 THINKPAD_EC_MASK_MUTE); 571 break; 572 573 case ACPI_THINKPAD_METHOD_THINKLIGHT: 574 if (sc->light_get_supported) 575 acpi_GetInteger(sc->ec_handle, THINKPAD_NAME_KEYLIGHT, 576 &val); 577 else 578 val = sc->light_val; 579 break; 580 581 case ACPI_THINKPAD_METHOD_BLUETOOTH: 582 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val); 583 sc->wlan_bt_flags = val; 584 val = ((val & THINKPAD_NAME_MASK_BT) != 0); 585 break; 586 587 case ACPI_THINKPAD_METHOD_WLAN: 588 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val); 589 sc->wlan_bt_flags = val; 590 val = ((val & THINKPAD_NAME_MASK_WLAN) != 0); 591 break; 592 593 case ACPI_THINKPAD_METHOD_FANSPEED: 594 if (sc->fan_handle) { 595 if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, 596 NULL, &val))) 597 val = -1; 598 } 599 else { 600 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, 601 &val_ec, 2); 602 val = val_ec; 603 } 604 break; 605 606 case ACPI_THINKPAD_METHOD_FANLEVEL: 607 /* 608 * The THINKPAD_EC_FANSTATUS register works as follows: 609 * Bit 0-5 indicate the level at which the fan operates. Only 610 * values between 0 and 7 have an effect. Everything 611 * above 7 is treated the same as level 7 612 * Bit 6 overrides the fan speed limit if set to 1 613 * Bit 7 indicates at which mode the fan operates: 614 * manual (0) or automatic (1) 615 */ 616 if (!sc->fan_handle) { 617 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, 618 &val_ec, 1); 619 val = val_ec & THINKPAD_EC_MASK_FANLEVEL; 620 } 621 break; 622 623 case ACPI_THINKPAD_METHOD_FANSTATUS: 624 if (!sc->fan_handle) { 625 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, 626 &val_ec, 1); 627 val = (val_ec & THINKPAD_EC_MASK_FANSTATUS) == 628 THINKPAD_EC_MASK_FANSTATUS; 629 } 630 else 631 val = -1; 632 break; 633 } 634 635 return (val); 636 } 637 638 static int 639 acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg) 640 { 641 int val, step, i; 642 ACPI_INTEGER val_ec; 643 ACPI_OBJECT Arg; 644 ACPI_OBJECT_LIST Args; 645 ACPI_STATUS status; 646 647 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 648 KKASSERT(lockstatus(&tplock, curthread) != 0); 649 650 switch (method) { 651 case ACPI_THINKPAD_METHOD_EVENTS: 652 if (arg < 0 || arg > 1) 653 return (EINVAL); 654 655 status = acpi_SetInteger(sc->handle, 656 THINKPAD_NAME_EVENTS_STATUS_SET, arg); 657 if (ACPI_FAILURE(status)) 658 return (status); 659 if (sc->events_mask_supported) 660 return acpi_thinkpad_eventmask_set(sc, 661 sc->events_availmask); 662 break; 663 664 case ACPI_THINKPAD_METHOD_EVENTMASK: 665 if (sc->events_mask_supported) 666 return acpi_thinkpad_eventmask_set(sc, arg); 667 break; 668 669 case ACPI_THINKPAD_METHOD_BRIGHTNESS: 670 if (arg < 0 || arg > 7) 671 return (EINVAL); 672 673 if (sc->cmos_handle) { 674 /* Read the current brightness */ 675 status = ACPI_EC_READ(sc->ec_dev, 676 THINKPAD_EC_BRIGHTNESS, &val_ec, 1); 677 if (ACPI_FAILURE(status)) 678 return (status); 679 val = val_ec & THINKPAD_EC_MASK_BRI; 680 681 Args.Count = 1; 682 Args.Pointer = &Arg; 683 Arg.Type = ACPI_TYPE_INTEGER; 684 Arg.Integer.Value = (arg > val) ? 685 THINKPAD_CMOS_BRIGHTNESS_UP : 686 THINKPAD_CMOS_BRIGHTNESS_DOWN; 687 688 step = (arg > val) ? 1 : -1; 689 for (i = val; i != arg; i += step) { 690 status = AcpiEvaluateObject(sc->cmos_handle, 691 NULL, &Args, NULL); 692 if (ACPI_FAILURE(status)) 693 break; 694 } 695 } 696 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, 697 arg, 1); 698 break; 699 700 case ACPI_THINKPAD_METHOD_VOLUME: 701 if (arg < 0 || arg > 14) 702 return (EINVAL); 703 704 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, 705 &val_ec, 1); 706 if (ACPI_FAILURE(status)) 707 return (status); 708 709 if (sc->cmos_handle) { 710 val = val_ec & THINKPAD_EC_MASK_VOL; 711 712 Args.Count = 1; 713 Args.Pointer = &Arg; 714 Arg.Type = ACPI_TYPE_INTEGER; 715 Arg.Integer.Value = (arg > val) ? 716 THINKPAD_CMOS_VOLUME_UP : THINKPAD_CMOS_VOLUME_DOWN; 717 718 step = (arg > val) ? 1 : -1; 719 for (i = val; i != arg; i += step) { 720 status = AcpiEvaluateObject(sc->cmos_handle, 721 NULL, &Args, NULL); 722 if (ACPI_FAILURE(status)) 723 break; 724 } 725 } 726 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, arg + 727 (val_ec & (~THINKPAD_EC_MASK_VOL)), 1); 728 break; 729 730 case ACPI_THINKPAD_METHOD_MUTE: 731 if (arg < 0 || arg > 1) 732 return (EINVAL); 733 734 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, 735 &val_ec, 1); 736 if (ACPI_FAILURE(status)) 737 return (status); 738 739 if (sc->cmos_handle) { 740 val = val_ec & THINKPAD_EC_MASK_VOL; 741 742 Args.Count = 1; 743 Args.Pointer = &Arg; 744 Arg.Type = ACPI_TYPE_INTEGER; 745 Arg.Integer.Value = THINKPAD_CMOS_VOLUME_MUTE; 746 747 status = AcpiEvaluateObject(sc->cmos_handle, NULL, 748 &Args, NULL); 749 if (ACPI_FAILURE(status)) 750 break; 751 } 752 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, (arg==1) ? 753 val_ec | THINKPAD_EC_MASK_MUTE : 754 val_ec & (~THINKPAD_EC_MASK_MUTE), 1); 755 break; 756 757 case ACPI_THINKPAD_METHOD_THINKLIGHT: 758 if (arg < 0 || arg > 1) 759 return (EINVAL); 760 761 if (sc->light_set_supported) { 762 Args.Count = 1; 763 Args.Pointer = &Arg; 764 Arg.Type = ACPI_TYPE_INTEGER; 765 Arg.Integer.Value = arg ? 766 sc->light_cmd_on : sc->light_cmd_off; 767 768 status = AcpiEvaluateObject(sc->light_handle, NULL, 769 &Args, NULL); 770 if (ACPI_SUCCESS(status)) 771 sc->light_val = arg; 772 return (status); 773 } 774 break; 775 776 case ACPI_THINKPAD_METHOD_BLUETOOTH: 777 if (arg < 0 || arg > 1) 778 return (EINVAL); 779 780 val = (arg == 1) ? sc->wlan_bt_flags | 781 THINKPAD_NAME_MASK_BT : 782 sc->wlan_bt_flags & (~THINKPAD_NAME_MASK_BT); 783 return acpi_SetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_SET, 784 val); 785 break; 786 787 case ACPI_THINKPAD_METHOD_FANLEVEL: 788 if (arg < 0 || arg > 7) 789 return (EINVAL); 790 791 if (!sc->fan_handle) { 792 /* Read the current fanstatus */ 793 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, 794 &val_ec, 1); 795 val = val_ec & (~THINKPAD_EC_MASK_FANLEVEL); 796 797 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS, 798 val | arg, 1); 799 } 800 break; 801 802 case ACPI_THINKPAD_METHOD_FANSTATUS: 803 if (arg < 0 || arg > 1) 804 return (EINVAL); 805 806 if (!sc->fan_handle) { 807 /* Read the current fanstatus */ 808 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS, 809 &val_ec, 1); 810 811 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS, 812 (arg == 1) ? (val_ec | THINKPAD_EC_MASK_FANSTATUS) : 813 (val_ec & (~THINKPAD_EC_MASK_FANSTATUS)), 1); 814 } 815 break; 816 } 817 818 return (0); 819 } 820 821 static int 822 acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method) 823 { 824 int dummy; 825 ACPI_OBJECT_TYPE cmos_t; 826 ACPI_HANDLE ledb_handle; 827 828 switch (method) { 829 case ACPI_THINKPAD_METHOD_EVENTS: 830 /* Events are disabled by default */ 831 return (TRUE); 832 833 case ACPI_THINKPAD_METHOD_EVENTMASK: 834 return (sc->events_mask_supported); 835 836 case ACPI_THINKPAD_METHOD_HOTKEY: 837 case ACPI_THINKPAD_METHOD_BRIGHTNESS: 838 case ACPI_THINKPAD_METHOD_VOLUME: 839 case ACPI_THINKPAD_METHOD_MUTE: 840 /* EC is required here, which was already checked before */ 841 return (TRUE); 842 843 case ACPI_THINKPAD_METHOD_THINKLIGHT: 844 sc->cmos_handle = NULL; 845 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 846 sc->ec_handle, THINKPAD_NAME_KEYLIGHT, &sc->light_val)); 847 848 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", 849 &sc->light_handle)) || 850 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", 851 &sc->light_handle)) || 852 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", 853 &sc->light_handle))) && 854 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 855 cmos_t == ACPI_TYPE_METHOD) { 856 sc->light_cmd_on = 0x0c; 857 sc->light_cmd_off = 0x0d; 858 sc->cmos_handle = sc->light_handle; 859 } 860 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", 861 &sc->light_handle))) { 862 sc->light_cmd_on = 1; 863 sc->light_cmd_off = 0; 864 } 865 else 866 sc->light_handle = NULL; 867 868 sc->light_set_supported = (sc->light_handle && 869 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", 870 &ledb_handle))); 871 872 if (sc->light_get_supported) 873 return (TRUE); 874 875 if (sc->light_set_supported) { 876 sc->light_val = 0; 877 return (TRUE); 878 } 879 880 return (FALSE); 881 882 case ACPI_THINKPAD_METHOD_BLUETOOTH: 883 case ACPI_THINKPAD_METHOD_WLAN: 884 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, 885 THINKPAD_NAME_WLAN_BT_GET, &dummy))) 886 return (TRUE); 887 return (FALSE); 888 889 case ACPI_THINKPAD_METHOD_FANSPEED: 890 /* 891 * Some models report the fan speed in levels from 0-7 892 * Newer models report it contiguously 893 */ 894 sc->fan_levels = (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", 895 &sc->fan_handle)) || 896 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", 897 &sc->fan_handle))); 898 return (TRUE); 899 900 case ACPI_THINKPAD_METHOD_FANLEVEL: 901 case ACPI_THINKPAD_METHOD_FANSTATUS: 902 /* 903 * Fan status is only supported on those models, 904 * which report fan RPM contiguously, not in levels 905 */ 906 if (sc->fan_levels) 907 return (FALSE); 908 return (TRUE); 909 910 case ACPI_THINKPAD_METHOD_THERMAL: 911 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, 912 THINKPAD_NAME_THERMAL_GET, &dummy))) { 913 sc->thermal_updt_supported = 914 ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, 915 THINKPAD_NAME_THERMAL_UPDT, &dummy)); 916 return (TRUE); 917 } 918 return (FALSE); 919 } 920 return (FALSE); 921 } 922 923 static void 924 acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context) 925 { 926 int event, arg, type; 927 device_t dev = context; 928 struct acpi_thinkpad_softc *sc = device_get_softc(dev); 929 930 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 931 932 if (notify != 0x80) 933 device_printf(dev, "Unknown notify\n"); 934 935 for (;;) { 936 acpi_GetInteger(acpi_get_handle(dev), THINKPAD_NAME_EVENTS_GET, 937 &event); 938 939 if (event == 0) 940 break; 941 942 type = (event >> 12) & 0xf; 943 arg = event & 0xfff; 944 switch (type) { 945 case 1: 946 if (!(sc->events_availmask & (1 << (arg - 1)))) { 947 device_printf(dev, "Unknown key %d\n", arg); 948 break; 949 } 950 951 /* Notify devd(8) */ 952 acpi_UserNotify("THINKPAD", h, (arg & 0xff)); 953 break; 954 default: 955 break; 956 } 957 } 958 } 959 960 static void 961 acpi_thinkpad_refresh(void *arg) 962 { 963 struct acpi_thinkpad_softc *sc = (struct acpi_thinkpad_softc *)arg; 964 int i, data; 965 966 for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) { 967 char temp_cmd[] = "TMP0"; 968 969 temp_cmd[3] = '0' + i; 970 /* 971 * The TMPx methods seem to return +/- 128 or 0 972 * when the respecting sensor is not available 973 */ 974 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 975 &data)) || ABS(data) == 128 || data == 0) { 976 sc->sensors[i].flags |= SENSOR_FINVALID; 977 continue; 978 } 979 if (sc->thermal_updt_supported) 980 /* Temperature is reported in tenth of Kelvin */ 981 sc->sensors[i].value = data * 100000 - 50000; 982 else 983 sc->sensors[i].value = data * 1000000 + 273150000; 984 sc->sensors[i].flags &= ~SENSOR_FINVALID; 985 } 986 987 if (sc->fan_handle) { 988 if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, 989 NULL, &data))) 990 sc->sensors[i].flags |= SENSOR_FINVALID; 991 sc->sensors[i].value = data; 992 sc->sensors[i].flags &= ~SENSOR_FINVALID; 993 } else { 994 ACPI_INTEGER speed; 995 996 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, &speed, 2); 997 sc->sensors[i].value = speed; 998 sc->sensors[i].flags &= ~SENSOR_FINVALID; 999 } 1000 } 1001