1 /* $OpenBSD: acpithinkpad.c,v 1.36 2014/07/08 21:32:15 miod Exp $ */ 2 /* 3 * Copyright (c) 2008 joshua stein <jcs@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/proc.h> 21 22 #include <dev/acpi/acpireg.h> 23 #include <dev/acpi/acpivar.h> 24 #include <dev/acpi/acpidev.h> 25 #include <dev/acpi/amltypes.h> 26 #include <dev/acpi/dsdt.h> 27 28 #include <machine/apmvar.h> 29 30 #include "audio.h" 31 #include "wskbd.h" 32 33 #define THINKPAD_HKEY_VERSION1 0x0100 34 #define THINKPAD_HKEY_VERSION2 0x0200 35 36 #define THINKPAD_CMOS_VOLUME_DOWN 0x00 37 #define THINKPAD_CMOS_VOLUME_UP 0x01 38 #define THINKPAD_CMOS_VOLUME_MUTE 0x02 39 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 40 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 41 42 #define THINKPAD_BLUETOOTH_PRESENT 0x01 43 #define THINKPAD_BLUETOOTH_ENABLED 0x02 44 45 /* wan (not wifi) card */ 46 #define THINKPAD_WAN_PRESENT 0x01 47 #define THINKPAD_WAN_ENABLED 0x02 48 49 #define THINKPAD_BUTTON_FN_F1 0x1001 50 #define THINKPAD_BUTTON_LOCK_SCREEN 0x1002 51 #define THINKPAD_BUTTON_BATTERY_INFO 0x1003 52 #define THINKPAD_BUTTON_SUSPEND 0x1004 53 #define THINKPAD_BUTTON_WIRELESS 0x1005 54 #define THINKPAD_BUTTON_FN_F6 0x1006 55 #define THINKPAD_BUTTON_EXTERNAL_SCREEN 0x1007 56 #define THINKPAD_BUTTON_POINTER_SWITCH 0x1008 57 #define THINKPAD_BUTTON_EJECT 0x1009 58 #define THINKPAD_BUTTON_BRIGHTNESS_UP 0x1010 59 #define THINKPAD_BUTTON_BRIGHTNESS_DOWN 0x1011 60 #define THINKPAD_BUTTON_THINKLIGHT 0x1012 61 #define THINKPAD_BUTTON_FN_SPACE 0x1014 62 #define THINKPAD_BUTTON_VOLUME_UP 0x1015 63 #define THINKPAD_BUTTON_VOLUME_DOWN 0x1016 64 #define THINKPAD_BUTTON_VOLUME_MUTE 0x1017 65 #define THINKPAD_BUTTON_THINKVANTAGE 0x1018 66 #define THINKPAD_BUTTON_BLACK 0x101a 67 #define THINKPAD_BUTTON_MICROPHONE_MUTE 0x101b 68 #define THINKPAD_BUTTON_FN_F11 0x100b 69 #define THINKPAD_BUTTON_HIBERNATE 0x100c 70 #define THINKPAD_ADAPTIVE_NEXT 0x1101 71 #define THINKPAD_ADAPTIVE_QUICK 0x1102 72 #define THINKPAD_ADAPTIVE_SNIP 0x1105 73 #define THINKPAD_ADAPTIVE_VOICE 0x1108 74 #define THINKPAD_ADAPTIVE_GESTURES 0x110a 75 #define THINKPAD_ADAPTIVE_SETTINGS 0x110e 76 #define THINKPAD_ADAPTIVE_TAB 0x110f 77 #define THINKPAD_ADAPTIVE_REFRESH 0x1110 78 #define THINKPAD_ADAPTIVE_BACK 0x1111 79 #define THINKPAD_PORT_REPL_DOCKED 0x4010 80 #define THINKPAD_PORT_REPL_UNDOCKED 0x4011 81 #define THINKPAD_LID_OPEN 0x5001 82 #define THINKPAD_LID_CLOSED 0x5002 83 #define THINKPAD_TABLET_SCREEN_NORMAL 0x500a 84 #define THINKPAD_TABLET_SCREEN_ROTATED 0x5009 85 #define THINKPAD_BRIGHTNESS_CHANGED 0x5010 86 #define THINKPAD_TABLET_PEN_INSERTED 0x500b 87 #define THINKPAD_TABLET_PEN_REMOVED 0x500c 88 #define THINKPAD_THERMAL_TABLE_CHANGED 0x6030 89 #define THINKPAD_POWER_CHANGED 0x6040 90 #define THINKPAD_BACKLIGHT_CHANGED 0x6050 91 #define THINKPAD_SWITCH_WIRELESS 0x7000 92 93 #define THINKPAD_NSENSORS 9 94 #define THINKPAD_NTEMPSENSORS 8 95 96 #define THINKPAD_ECOFFSET_FANLO 0x84 97 #define THINKPAD_ECOFFSET_FANHI 0x85 98 99 #define THINKPAD_ADAPTIVE_MODE_HOME 1 100 #define THINKPAD_ADAPTIVE_MODE_FUNCTION 3 101 102 struct acpithinkpad_softc { 103 struct device sc_dev; 104 105 struct acpiec_softc *sc_ec; 106 struct acpi_softc *sc_acpi; 107 struct aml_node *sc_devnode; 108 109 struct ksensor sc_sens[THINKPAD_NSENSORS]; 110 struct ksensordev sc_sensdev; 111 }; 112 113 extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 114 115 int thinkpad_match(struct device *, void *, void *); 116 void thinkpad_attach(struct device *, struct device *, void *); 117 int thinkpad_hotkey(struct aml_node *, int, void *); 118 int thinkpad_enable_events(struct acpithinkpad_softc *); 119 int thinkpad_toggle_bluetooth(struct acpithinkpad_softc *); 120 int thinkpad_toggle_wan(struct acpithinkpad_softc *); 121 int thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t); 122 int thinkpad_volume_down(struct acpithinkpad_softc *); 123 int thinkpad_volume_up(struct acpithinkpad_softc *); 124 int thinkpad_volume_mute(struct acpithinkpad_softc *); 125 int thinkpad_brightness_up(struct acpithinkpad_softc *); 126 int thinkpad_brightness_down(struct acpithinkpad_softc *); 127 int thinkpad_adaptive_change(struct acpithinkpad_softc *); 128 int thinkpad_activate(struct device *, int); 129 130 void thinkpad_sensor_attach(struct acpithinkpad_softc *sc); 131 void thinkpad_sensor_refresh(void *); 132 133 #if NAUDIO > 0 && NWSKBD > 0 134 extern int wskbd_set_mixervolume(long, long); 135 #endif 136 137 struct cfattach acpithinkpad_ca = { 138 sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach, 139 NULL, thinkpad_activate 140 }; 141 142 struct cfdriver acpithinkpad_cd = { 143 NULL, "acpithinkpad", DV_DULL 144 }; 145 146 const char *acpithinkpad_hids[] = { 147 ACPI_DEV_IBM, ACPI_DEV_LENOVO, 0 148 }; 149 150 int 151 thinkpad_match(struct device *parent, void *match, void *aux) 152 { 153 struct acpi_attach_args *aa = aux; 154 struct cfdata *cf = match; 155 int64_t res; 156 157 if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name)) 158 return (0); 159 160 if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node, 161 "MHKV", 0, NULL, &res)) 162 return (0); 163 164 if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2)) 165 return (0); 166 167 return (1); 168 } 169 170 void 171 thinkpad_sensor_attach(struct acpithinkpad_softc *sc) 172 { 173 int i; 174 175 if (sc->sc_acpi->sc_ec == NULL) 176 return; 177 sc->sc_ec = sc->sc_acpi->sc_ec; 178 179 /* Add temperature probes */ 180 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 181 sizeof(sc->sc_sensdev.xname)); 182 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 183 sc->sc_sens[i].type = SENSOR_TEMP; 184 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); 185 } 186 187 /* Add fan probe */ 188 sc->sc_sens[i].type = SENSOR_FANRPM; 189 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); 190 191 sensordev_install(&sc->sc_sensdev); 192 } 193 194 void 195 thinkpad_sensor_refresh(void *arg) 196 { 197 struct acpithinkpad_softc *sc = arg; 198 u_int8_t lo, hi, i; 199 int64_t tmp; 200 char sname[5]; 201 202 /* Refresh sensor readings */ 203 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 204 snprintf(sname, sizeof(sname), "TMP%d", i); 205 aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, 206 sname, 0, 0, &tmp); 207 sc->sc_sens[i].value = (tmp * 1000000) + 273150000; 208 if (tmp > 127 || tmp < -127) 209 sc->sc_sens[i].flags = SENSOR_FINVALID; 210 } 211 212 /* Read fan RPM */ 213 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo); 214 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi); 215 sc->sc_sens[i].value = ((hi << 8L) + lo); 216 } 217 218 void 219 thinkpad_attach(struct device *parent, struct device *self, void *aux) 220 { 221 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self; 222 struct acpi_attach_args *aa = aux; 223 224 sc->sc_acpi = (struct acpi_softc *)parent; 225 sc->sc_devnode = aa->aaa_node; 226 227 printf("\n"); 228 229 /* Set event mask to receive everything */ 230 thinkpad_enable_events(sc); 231 thinkpad_sensor_attach(sc); 232 233 /* Run thinkpad_hotkey on button presses */ 234 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 235 thinkpad_hotkey, sc, ACPIDEV_POLL); 236 } 237 238 int 239 thinkpad_enable_events(struct acpithinkpad_softc *sc) 240 { 241 struct aml_value arg, args[2]; 242 int64_t mask; 243 int i; 244 245 /* Get the supported event mask */ 246 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA", 247 0, NULL, &mask)) { 248 printf("%s: no MHKA\n", DEVNAME(sc)); 249 return (1); 250 } 251 252 /* Update hotkey mask */ 253 bzero(args, sizeof(args)); 254 args[0].type = args[1].type = AML_OBJTYPE_INTEGER; 255 for (i = 0; i < 32; i++) { 256 args[0].v_integer = i + 1; 257 args[1].v_integer = (((1 << i) & mask) != 0); 258 259 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM", 260 2, args, NULL)) { 261 printf("%s: couldn't toggle MHKM\n", DEVNAME(sc)); 262 return (1); 263 } 264 } 265 266 /* Enable hotkeys */ 267 bzero(&arg, sizeof(arg)); 268 arg.type = AML_OBJTYPE_INTEGER; 269 arg.v_integer = 1; 270 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC", 271 1, &arg, NULL)) { 272 printf("%s: couldn't enable hotkeys\n", DEVNAME(sc)); 273 return (1); 274 } 275 276 return (0); 277 } 278 279 int 280 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg) 281 { 282 struct acpithinkpad_softc *sc = arg; 283 int handled = 0; 284 int64_t event; 285 286 if (notify_type == 0x00) { 287 /* Poll sensors */ 288 thinkpad_sensor_refresh(sc); 289 return (0); 290 } 291 292 if (notify_type != 0x80) 293 return (1); 294 295 for (;;) { 296 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP", 297 0, NULL, &event)) 298 break; 299 if (event == 0) 300 break; 301 302 switch (event) { 303 case THINKPAD_BUTTON_BRIGHTNESS_UP: 304 thinkpad_brightness_up(sc); 305 handled = 1; 306 break; 307 case THINKPAD_BUTTON_BRIGHTNESS_DOWN: 308 thinkpad_brightness_down(sc); 309 handled = 1; 310 break; 311 case THINKPAD_BUTTON_WIRELESS: 312 thinkpad_toggle_bluetooth(sc); 313 handled = 1; 314 break; 315 case THINKPAD_BUTTON_SUSPEND: 316 #ifndef SMALL_KERNEL 317 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) 318 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 319 sc->sc_acpi, ACPI_STATE_S3); 320 #endif 321 handled = 1; 322 break; 323 case THINKPAD_BUTTON_VOLUME_MUTE: 324 thinkpad_volume_mute(sc); 325 handled = 1; 326 break; 327 case THINKPAD_BUTTON_VOLUME_DOWN: 328 thinkpad_volume_down(sc); 329 handled = 1; 330 break; 331 case THINKPAD_BUTTON_VOLUME_UP: 332 thinkpad_volume_up(sc); 333 handled = 1; 334 break; 335 case THINKPAD_BUTTON_MICROPHONE_MUTE: 336 #if NAUDIO > 0 && NWSKBD > 0 337 wskbd_set_mixervolume(0, 0); 338 #endif 339 handled = 1; 340 break; 341 case THINKPAD_BUTTON_HIBERNATE: 342 #ifndef SMALL_KERNEL 343 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 344 sc->sc_acpi, ACPI_STATE_S4); 345 #endif 346 handled = 1; 347 break; 348 case THINKPAD_ADAPTIVE_NEXT: 349 case THINKPAD_ADAPTIVE_QUICK: 350 thinkpad_adaptive_change(sc); 351 handled = 1; 352 break; 353 case THINKPAD_ADAPTIVE_BACK: 354 case THINKPAD_ADAPTIVE_GESTURES: 355 case THINKPAD_ADAPTIVE_REFRESH: 356 case THINKPAD_ADAPTIVE_SETTINGS: 357 case THINKPAD_ADAPTIVE_SNIP: 358 case THINKPAD_ADAPTIVE_TAB: 359 case THINKPAD_ADAPTIVE_VOICE: 360 case THINKPAD_BACKLIGHT_CHANGED: 361 case THINKPAD_BRIGHTNESS_CHANGED: 362 case THINKPAD_BUTTON_BATTERY_INFO: 363 case THINKPAD_BUTTON_EJECT: 364 case THINKPAD_BUTTON_EXTERNAL_SCREEN: 365 case THINKPAD_BUTTON_FN_F11: 366 case THINKPAD_BUTTON_FN_F1: 367 case THINKPAD_BUTTON_FN_F6: 368 case THINKPAD_BUTTON_FN_SPACE: 369 case THINKPAD_BUTTON_LOCK_SCREEN: 370 case THINKPAD_BUTTON_POINTER_SWITCH: 371 case THINKPAD_BUTTON_THINKLIGHT: 372 case THINKPAD_BUTTON_THINKVANTAGE: 373 case THINKPAD_BUTTON_BLACK: 374 case THINKPAD_LID_CLOSED: 375 case THINKPAD_LID_OPEN: 376 case THINKPAD_PORT_REPL_DOCKED: 377 case THINKPAD_PORT_REPL_UNDOCKED: 378 case THINKPAD_POWER_CHANGED: 379 case THINKPAD_SWITCH_WIRELESS: 380 case THINKPAD_TABLET_PEN_INSERTED: 381 case THINKPAD_TABLET_PEN_REMOVED: 382 case THINKPAD_TABLET_SCREEN_NORMAL: 383 case THINKPAD_TABLET_SCREEN_ROTATED: 384 case THINKPAD_THERMAL_TABLE_CHANGED: 385 handled = 1; 386 break; 387 default: 388 printf("%s: unknown event 0x%03llx\n", 389 DEVNAME(sc), event); 390 } 391 } 392 393 return (handled); 394 } 395 396 int 397 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc) 398 { 399 struct aml_value arg; 400 int64_t bluetooth; 401 402 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC", 403 0, NULL, &bluetooth)) 404 return (1); 405 406 if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT)) 407 return (1); 408 409 bzero(&arg, sizeof(arg)); 410 arg.type = AML_OBJTYPE_INTEGER; 411 arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED; 412 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC", 413 1, &arg, NULL)) { 414 printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc)); 415 return (1); 416 } 417 418 return (0); 419 } 420 421 int 422 thinkpad_toggle_wan(struct acpithinkpad_softc *sc) 423 { 424 struct aml_value arg; 425 int64_t wan; 426 427 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN", 428 0, NULL, &wan)) 429 return (1); 430 431 if (!(wan & THINKPAD_WAN_PRESENT)) 432 return (1); 433 434 bzero(&arg, sizeof(arg)); 435 arg.type = AML_OBJTYPE_INTEGER; 436 arg.v_integer = wan ^ THINKPAD_WAN_ENABLED; 437 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN", 438 1, &arg, NULL)) { 439 printf("%s: couldn't toggle wan\n", DEVNAME(sc)); 440 return (1); 441 } 442 443 return (0); 444 } 445 446 int 447 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd) 448 { 449 struct aml_value arg; 450 451 bzero(&arg, sizeof(arg)); 452 arg.type = AML_OBJTYPE_INTEGER; 453 arg.v_integer = cmd; 454 aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL); 455 return (0); 456 } 457 458 int 459 thinkpad_volume_down(struct acpithinkpad_softc *sc) 460 { 461 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN)); 462 } 463 464 int 465 thinkpad_volume_up(struct acpithinkpad_softc *sc) 466 { 467 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP)); 468 } 469 470 int 471 thinkpad_volume_mute(struct acpithinkpad_softc *sc) 472 { 473 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE)); 474 } 475 476 int 477 thinkpad_brightness_up(struct acpithinkpad_softc *sc) 478 { 479 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP)); 480 } 481 482 int 483 thinkpad_brightness_down(struct acpithinkpad_softc *sc) 484 { 485 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN)); 486 } 487 488 int 489 thinkpad_adaptive_change(struct acpithinkpad_softc *sc) 490 { 491 struct aml_value arg; 492 int64_t mode; 493 494 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW", 495 0, NULL, &mode)) { 496 printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc)); 497 return (1); 498 } 499 500 bzero(&arg, sizeof(arg)); 501 arg.type = AML_OBJTYPE_INTEGER; 502 503 if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION) 504 arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME; 505 else 506 arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION; 507 508 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW", 509 1, &arg, NULL)) { 510 printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc)); 511 return (1); 512 } 513 514 return (0); 515 } 516 517 int 518 thinkpad_activate(struct device *self, int act) 519 { 520 521 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self; 522 int64_t res; 523 524 switch(act) { 525 case DVACT_WAKEUP: 526 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW", 527 0, NULL, &res) == 0) 528 thinkpad_adaptive_change(sc); 529 break; 530 } 531 return (0); 532 } 533