1 /* $OpenBSD: acpithinkpad.c,v 1.18 2009/03/11 20:52:11 jordan 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 21 #include <dev/acpi/acpireg.h> 22 #include <dev/acpi/acpivar.h> 23 #include <dev/acpi/acpidev.h> 24 #include <dev/acpi/amltypes.h> 25 #include <dev/acpi/dsdt.h> 26 27 #define THINKPAD_HKEY_VERSION 0x0100 28 29 #define THINKPAD_CMOS_VOLUME_DOWN 0x00 30 #define THINKPAD_CMOS_VOLUME_UP 0x01 31 #define THINKPAD_CMOS_VOLUME_MUTE 0x02 32 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 33 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 34 35 #define THINKPAD_BLUETOOTH_PRESENT 0x01 36 #define THINKPAD_BLUETOOTH_ENABLED 0x02 37 38 /* wan (not wifi) card */ 39 #define THINKPAD_WAN_PRESENT 0x01 40 #define THINKPAD_WAN_ENABLED 0x02 41 42 /* type 1 events */ 43 #define THINKPAD_BUTTON_FN_F1 0x001 44 #define THINKPAD_BUTTON_LOCK_SCREEN 0x002 45 #define THINKPAD_BUTTON_BATTERY_INFO 0x003 46 #define THINKPAD_BUTTON_SUSPEND 0x004 47 #define THINKPAD_BUTTON_WIRELESS 0x005 48 #define THINKPAD_BUTTON_FN_F6 0x006 49 #define THINKPAD_BUTTON_EXTERNAL_SCREEN 0x007 50 #define THINKPAD_BUTTON_POINTER_SWITCH 0x008 51 #define THINKPAD_BUTTON_EJECT 0x009 52 #define THINKPAD_BUTTON_BRIGHTNESS_UP 0x010 53 #define THINKPAD_BUTTON_BRIGHTNESS_DOWN 0x011 54 #define THINKPAD_BUTTON_THINKLIGHT 0x012 55 #define THINKPAD_BUTTON_FN_SPACE 0x014 56 #define THINKPAD_BUTTON_VOLUME_UP 0x015 57 #define THINKPAD_BUTTON_VOLUME_DOWN 0x016 58 #define THINKPAD_BUTTON_VOLUME_MUTE 0x017 59 #define THINKPAD_BUTTON_THINKVANTAGE 0x018 60 #define THINKPAD_BUTTON_FN_F11 0x00b 61 #define THINKPAD_BUTTON_HIBERNATE 0x00c 62 63 /* type 5 events */ 64 #define THINKPAD_LID_OPEN 0x001 65 #define THINKPAD_LID_CLOSED 0x002 66 #define THINKPAD_TABLET_SCREEN_NORMAL 0x00a 67 #define THINKPAD_TABLET_SCREEN_ROTATED 0x009 68 #define THINKPAD_BRIGHTNESS_CHANGED 0x010 69 #define THINKPAD_TABLET_PEN_INSERTED 0x00b 70 #define THINKPAD_TABLET_PEN_REMOVED 0x00c 71 72 /* type 6 events */ 73 #define THINKPAD_POWER_CHANGED 0x030 74 75 /* type 7 events */ 76 #define THINKPAD_SWITCH_WIRELESS 0x000 77 78 #define THINKPAD_NSENSORS 9 79 #define THINKPAD_NTEMPSENSORS 8 80 81 #define THINKPAD_ECOFFSET_FANLO 0x84 82 #define THINKPAD_ECOFFSET_FANHI 0x85 83 84 struct acpithinkpad_softc { 85 struct device sc_dev; 86 87 struct acpiec_softc *sc_ec; 88 struct acpi_softc *sc_acpi; 89 struct aml_node *sc_devnode; 90 91 struct ksensor sc_sens[THINKPAD_NSENSORS]; 92 struct ksensordev sc_sensdev; 93 }; 94 95 extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 96 97 int thinkpad_match(struct device *, void *, void *); 98 void thinkpad_attach(struct device *, struct device *, void *); 99 int thinkpad_hotkey(struct aml_node *, int, void *); 100 int thinkpad_enable_events(struct acpithinkpad_softc *); 101 int thinkpad_toggle_bluetooth(struct acpithinkpad_softc *); 102 int thinkpad_toggle_wan(struct acpithinkpad_softc *); 103 int thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t); 104 int thinkpad_volume_down(struct acpithinkpad_softc *); 105 int thinkpad_volume_up(struct acpithinkpad_softc *); 106 int thinkpad_volume_mute(struct acpithinkpad_softc *); 107 int thinkpad_brightness_up(struct acpithinkpad_softc *); 108 int thinkpad_brightness_down(struct acpithinkpad_softc *); 109 110 void thinkpad_sensor_attach(struct acpithinkpad_softc *sc); 111 void thinkpad_sensor_refresh(void *); 112 113 struct cfattach acpithinkpad_ca = { 114 sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach 115 }; 116 117 struct cfdriver acpithinkpad_cd = { 118 NULL, "acpithinkpad", DV_DULL 119 }; 120 121 const char *acpithinkpad_hids[] = { ACPI_DEV_THINKPAD, 0 }; 122 123 int 124 thinkpad_match(struct device *parent, void *match, void *aux) 125 { 126 struct acpi_attach_args *aa = aux; 127 struct cfdata *cf = match; 128 int64_t res; 129 int rv = 0; 130 131 if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name)) 132 return (0); 133 134 if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node, 135 "MHKV", 0, NULL, &res)) 136 return (0); 137 138 if (res == THINKPAD_HKEY_VERSION) 139 rv = 1; 140 141 return (rv); 142 } 143 144 void 145 thinkpad_sensor_attach(struct acpithinkpad_softc *sc) 146 { 147 int i; 148 149 if (sc->sc_acpi->sc_ec == NULL) 150 return; 151 sc->sc_ec = sc->sc_acpi->sc_ec; 152 153 /* Add temperature probes */ 154 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 155 sizeof(sc->sc_sensdev.xname)); 156 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 157 snprintf(sc->sc_sens[i].desc, sizeof(sc->sc_sens[i].desc), 158 "TMP%d", i); 159 sc->sc_sens[i].type = SENSOR_TEMP; 160 sc->sc_sens[i].value = 0; 161 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); 162 } 163 164 /* Add fan probe */ 165 strlcpy(sc->sc_sens[i].desc, "fan", 166 sizeof(sc->sc_sens[i].desc)); 167 sc->sc_sens[i].type = SENSOR_FANRPM; 168 sc->sc_sens[i].value = 0; 169 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); 170 171 sensordev_install(&sc->sc_sensdev); 172 } 173 174 void 175 thinkpad_sensor_refresh(void *arg) 176 { 177 struct acpithinkpad_softc *sc = arg; 178 u_int8_t lo, hi, i; 179 int64_t tmp; 180 181 /* Refresh sensor readings */ 182 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 183 aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, 184 sc->sc_sens[i].desc, 0, 0, &tmp); 185 sc->sc_sens[i].value = (tmp * 1000000) + 273150000; 186 if (tmp > 127 || tmp < -127) 187 sc->sc_sens[i].flags = SENSOR_FINVALID; 188 } 189 190 /* Read fan RPM */ 191 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo); 192 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi); 193 sc->sc_sens[i].value = ((hi << 8L) + lo); 194 } 195 196 void 197 thinkpad_attach(struct device *parent, struct device *self, void *aux) 198 { 199 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self; 200 struct acpi_attach_args *aa = aux; 201 202 sc->sc_acpi = (struct acpi_softc *)parent; 203 sc->sc_devnode = aa->aaa_node; 204 205 printf("\n"); 206 207 /* set event mask to receive everything */ 208 thinkpad_enable_events(sc); 209 thinkpad_sensor_attach(sc); 210 211 /* run thinkpad_hotkey on button presses */ 212 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 213 thinkpad_hotkey, sc, ACPIDEV_POLL); 214 } 215 216 int 217 thinkpad_enable_events(struct acpithinkpad_softc *sc) 218 { 219 struct aml_value arg, args[2]; 220 int64_t mask; 221 int i, rv = 1; 222 223 /* get the supported event mask */ 224 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA", 0, NULL, &mask)) { 225 printf("%s: no MHKA\n", DEVNAME(sc)); 226 goto fail; 227 } 228 229 /* update hotkey mask */ 230 bzero(args, sizeof(args)); 231 args[0].type = args[1].type = AML_OBJTYPE_INTEGER; 232 for (i = 0; i < 32; i++) { 233 args[0].v_integer = i + 1; 234 args[1].v_integer = (((1 << i) & mask) != 0); 235 236 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM", 2, args, 237 NULL)) { 238 printf("%s: couldn't toggle MHKM\n", DEVNAME(sc)); 239 goto fail; 240 } 241 } 242 243 /* enable hotkeys */ 244 bzero(&arg, sizeof(arg)); 245 arg.type = AML_OBJTYPE_INTEGER; 246 arg.v_integer = 1; 247 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC", 1, &arg, NULL)) { 248 printf("%s: couldn't enable hotkeys\n", DEVNAME(sc)); 249 goto fail; 250 } 251 252 rv = 0; 253 fail: 254 return (rv); 255 } 256 257 int 258 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg) 259 { 260 struct acpithinkpad_softc *sc = arg; 261 int type, event, handled, rv = 1, tot = 0; 262 int64_t val; 263 264 if (notify_type == 0x00) { 265 /* poll sensors */ 266 thinkpad_sensor_refresh(sc); 267 return 0; 268 } 269 if (notify_type != 0x80) 270 goto fail; 271 272 for (;;) { 273 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP", 0, NULL, 274 &val)) 275 goto done; 276 if (val == 0) 277 goto done; 278 279 type = (val & 0xf000) >> 12; 280 event = val & 0x0fff; 281 handled = 0; 282 283 switch (type) { 284 case 1: 285 switch (event) { 286 case THINKPAD_BUTTON_BRIGHTNESS_UP: 287 thinkpad_brightness_up(sc); 288 handled = 1; 289 break; 290 case THINKPAD_BUTTON_BRIGHTNESS_DOWN: 291 thinkpad_brightness_down(sc); 292 handled = 1; 293 break; 294 case THINKPAD_BUTTON_WIRELESS: 295 thinkpad_toggle_bluetooth(sc); 296 handled = 1; 297 break; 298 case THINKPAD_BUTTON_SUSPEND: 299 handled = 1; 300 /* 301 acpi_enter_sleep_state(sc->sc_acpi, 302 ACPI_STATE_S3); 303 */ 304 break; 305 case THINKPAD_BUTTON_HIBERNATE: 306 case THINKPAD_BUTTON_FN_F1: 307 case THINKPAD_BUTTON_LOCK_SCREEN: 308 case THINKPAD_BUTTON_BATTERY_INFO: 309 case THINKPAD_BUTTON_FN_F6: 310 case THINKPAD_BUTTON_EXTERNAL_SCREEN: 311 case THINKPAD_BUTTON_POINTER_SWITCH: 312 case THINKPAD_BUTTON_EJECT: 313 case THINKPAD_BUTTON_THINKLIGHT: 314 case THINKPAD_BUTTON_FN_SPACE: 315 handled = 1; 316 break; 317 case THINKPAD_BUTTON_VOLUME_DOWN: 318 thinkpad_volume_down(sc); 319 handled = 1; 320 break; 321 case THINKPAD_BUTTON_VOLUME_UP: 322 thinkpad_volume_up(sc); 323 handled = 1; 324 break; 325 case THINKPAD_BUTTON_VOLUME_MUTE: 326 thinkpad_volume_mute(sc); 327 handled = 1; 328 break; 329 case THINKPAD_BUTTON_THINKVANTAGE: 330 case THINKPAD_BUTTON_FN_F11: 331 handled = 1; 332 break; 333 } 334 break; 335 case 5: 336 switch (event) { 337 case THINKPAD_LID_OPEN: 338 case THINKPAD_LID_CLOSED: 339 case THINKPAD_TABLET_SCREEN_NORMAL: 340 case THINKPAD_TABLET_SCREEN_ROTATED: 341 case THINKPAD_BRIGHTNESS_CHANGED: 342 case THINKPAD_TABLET_PEN_INSERTED: 343 case THINKPAD_TABLET_PEN_REMOVED: 344 handled = 1; 345 break; 346 } 347 break; 348 case 6: 349 switch (event) { 350 case THINKPAD_POWER_CHANGED: 351 handled = 1; 352 break; 353 } 354 break; 355 case 7: 356 switch (event) { 357 case THINKPAD_SWITCH_WIRELESS: 358 handled = 1; 359 break; 360 } 361 break; 362 } 363 364 if (handled) 365 tot++; 366 else 367 printf("%s: unknown type %d event 0x%03x\n", 368 DEVNAME(sc), type, event); 369 } 370 done: 371 if (tot) 372 rv = 0; 373 fail: 374 return (rv); 375 } 376 377 int 378 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc) 379 { 380 struct aml_value arg; 381 int rv = 1; 382 int64_t bluetooth; 383 384 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC", 0, NULL, &bluetooth)) 385 goto fail; 386 387 if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT)) 388 goto fail; 389 390 bzero(&arg, sizeof(arg)); 391 arg.type = AML_OBJTYPE_INTEGER; 392 arg.v_integer = bluetooth ^= THINKPAD_BLUETOOTH_ENABLED; 393 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC", 1, &arg, NULL)) { 394 printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc)); 395 goto fail; 396 } 397 398 rv = 0; 399 fail: 400 return (rv); 401 } 402 403 int 404 thinkpad_toggle_wan(struct acpithinkpad_softc *sc) 405 { 406 struct aml_value arg; 407 int rv = 1; 408 int64_t wan; 409 410 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN", 0, NULL, &wan)) 411 goto fail; 412 413 if (!(wan & THINKPAD_WAN_PRESENT)) 414 goto fail; 415 416 bzero(&arg, sizeof(arg)); 417 arg.type = AML_OBJTYPE_INTEGER; 418 arg.v_integer = (wan ^= THINKPAD_WAN_ENABLED); 419 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN", 1, &arg, NULL)) { 420 printf("%s: couldn't toggle wan\n", DEVNAME(sc)); 421 goto fail; 422 } 423 424 rv = 0; 425 fail: 426 return (rv); 427 } 428 429 int 430 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd) 431 { 432 struct aml_value arg; 433 434 bzero(&arg, sizeof(arg)); 435 arg.type = AML_OBJTYPE_INTEGER; 436 arg.v_integer = cmd; 437 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, 438 NULL)) { 439 printf("%s: cmos command 0x%x failed\n", DEVNAME(sc), cmd); 440 return (1); 441 } 442 443 return (0); 444 } 445 446 int 447 thinkpad_volume_down(struct acpithinkpad_softc *sc) 448 { 449 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN)); 450 } 451 452 int 453 thinkpad_volume_up(struct acpithinkpad_softc *sc) 454 { 455 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP)); 456 } 457 458 int 459 thinkpad_volume_mute(struct acpithinkpad_softc *sc) 460 { 461 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE)); 462 } 463 464 int 465 thinkpad_brightness_up(struct acpithinkpad_softc *sc) 466 { 467 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP)); 468 } 469 470 int 471 thinkpad_brightness_down(struct acpithinkpad_softc *sc) 472 { 473 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN)); 474 } 475