1 /* $OpenBSD: acpithinkpad.c,v 1.28 2011/06/06 06:13:46 deraadt 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 #include <sys/workq.h> 22 23 #include <dev/acpi/acpireg.h> 24 #include <dev/acpi/acpivar.h> 25 #include <dev/acpi/acpidev.h> 26 #include <dev/acpi/amltypes.h> 27 #include <dev/acpi/dsdt.h> 28 29 #include <machine/apmvar.h> 30 31 #include "audio.h" 32 #include "wskbd.h" 33 34 #define THINKPAD_HKEY_VERSION 0x0100 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_MICROPHONE_MUTE 0x101b 67 #define THINKPAD_BUTTON_FN_F11 0x100b 68 #define THINKPAD_BUTTON_HIBERNATE 0x100c 69 #define THINKPAD_LID_OPEN 0x5001 70 #define THINKPAD_LID_CLOSED 0x5002 71 #define THINKPAD_TABLET_SCREEN_NORMAL 0x500a 72 #define THINKPAD_TABLET_SCREEN_ROTATED 0x5009 73 #define THINKPAD_BRIGHTNESS_CHANGED 0x5010 74 #define THINKPAD_TABLET_PEN_INSERTED 0x500b 75 #define THINKPAD_TABLET_PEN_REMOVED 0x500c 76 #define THINKPAD_POWER_CHANGED 0x6030 77 #define THINKPAD_SWITCH_WIRELESS 0x7000 78 79 #define THINKPAD_NSENSORS 9 80 #define THINKPAD_NTEMPSENSORS 8 81 82 #define THINKPAD_ECOFFSET_FANLO 0x84 83 #define THINKPAD_ECOFFSET_FANHI 0x85 84 85 struct acpithinkpad_softc { 86 struct device sc_dev; 87 88 struct acpiec_softc *sc_ec; 89 struct acpi_softc *sc_acpi; 90 struct aml_node *sc_devnode; 91 92 struct ksensor sc_sens[THINKPAD_NSENSORS]; 93 struct ksensordev sc_sensdev; 94 }; 95 96 extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 97 98 int thinkpad_match(struct device *, void *, void *); 99 void thinkpad_attach(struct device *, struct device *, void *); 100 int thinkpad_hotkey(struct aml_node *, int, void *); 101 int thinkpad_enable_events(struct acpithinkpad_softc *); 102 int thinkpad_toggle_bluetooth(struct acpithinkpad_softc *); 103 int thinkpad_toggle_wan(struct acpithinkpad_softc *); 104 int thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t); 105 int thinkpad_volume_down(struct acpithinkpad_softc *); 106 int thinkpad_volume_up(struct acpithinkpad_softc *); 107 int thinkpad_volume_mute(struct acpithinkpad_softc *); 108 int thinkpad_brightness_up(struct acpithinkpad_softc *); 109 int thinkpad_brightness_down(struct acpithinkpad_softc *); 110 111 void thinkpad_sensor_attach(struct acpithinkpad_softc *sc); 112 void thinkpad_sensor_refresh(void *); 113 114 #if NAUDIO > 0 && NWSKBD > 0 115 extern int wskbd_set_mixervolume(long dir, int out); 116 #endif 117 118 struct cfattach acpithinkpad_ca = { 119 sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach 120 }; 121 122 struct cfdriver acpithinkpad_cd = { 123 NULL, "acpithinkpad", DV_DULL 124 }; 125 126 const char *acpithinkpad_hids[] = { 127 ACPI_DEV_IBM, ACPI_DEV_LENOVO, 0 128 }; 129 130 int 131 thinkpad_match(struct device *parent, void *match, void *aux) 132 { 133 struct acpi_attach_args *aa = aux; 134 struct cfdata *cf = match; 135 int64_t res; 136 137 if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name)) 138 return (0); 139 140 if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node, 141 "MHKV", 0, NULL, &res)) 142 return (0); 143 144 if (res != THINKPAD_HKEY_VERSION) 145 return (0); 146 147 return (1); 148 } 149 150 void 151 thinkpad_sensor_attach(struct acpithinkpad_softc *sc) 152 { 153 int i; 154 155 if (sc->sc_acpi->sc_ec == NULL) 156 return; 157 sc->sc_ec = sc->sc_acpi->sc_ec; 158 159 /* Add temperature probes */ 160 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 161 sizeof(sc->sc_sensdev.xname)); 162 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 163 sc->sc_sens[i].type = SENSOR_TEMP; 164 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); 165 } 166 167 /* Add fan probe */ 168 sc->sc_sens[i].type = SENSOR_FANRPM; 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 char sname[5]; 181 182 /* Refresh sensor readings */ 183 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 184 snprintf(sname, sizeof(sname), "TMP%d", i); 185 aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, 186 sname, 0, 0, &tmp); 187 sc->sc_sens[i].value = (tmp * 1000000) + 273150000; 188 if (tmp > 127 || tmp < -127) 189 sc->sc_sens[i].flags = SENSOR_FINVALID; 190 } 191 192 /* Read fan RPM */ 193 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo); 194 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi); 195 sc->sc_sens[i].value = ((hi << 8L) + lo); 196 } 197 198 void 199 thinkpad_attach(struct device *parent, struct device *self, void *aux) 200 { 201 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self; 202 struct acpi_attach_args *aa = aux; 203 204 sc->sc_acpi = (struct acpi_softc *)parent; 205 sc->sc_devnode = aa->aaa_node; 206 207 printf("\n"); 208 209 /* Set event mask to receive everything */ 210 thinkpad_enable_events(sc); 211 thinkpad_sensor_attach(sc); 212 213 /* Run thinkpad_hotkey on button presses */ 214 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 215 thinkpad_hotkey, sc, ACPIDEV_POLL); 216 } 217 218 int 219 thinkpad_enable_events(struct acpithinkpad_softc *sc) 220 { 221 struct aml_value arg, args[2]; 222 int64_t mask; 223 int i; 224 225 /* Get the supported event mask */ 226 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA", 227 0, NULL, &mask)) { 228 printf("%s: no MHKA\n", DEVNAME(sc)); 229 return (1); 230 } 231 232 /* Update hotkey mask */ 233 bzero(args, sizeof(args)); 234 args[0].type = args[1].type = AML_OBJTYPE_INTEGER; 235 for (i = 0; i < 32; i++) { 236 args[0].v_integer = i + 1; 237 args[1].v_integer = (((1 << i) & mask) != 0); 238 239 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM", 240 2, args, NULL)) { 241 printf("%s: couldn't toggle MHKM\n", DEVNAME(sc)); 242 return (1); 243 } 244 } 245 246 /* Enable hotkeys */ 247 bzero(&arg, sizeof(arg)); 248 arg.type = AML_OBJTYPE_INTEGER; 249 arg.v_integer = 1; 250 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC", 251 1, &arg, NULL)) { 252 printf("%s: couldn't enable hotkeys\n", DEVNAME(sc)); 253 return (1); 254 } 255 256 return (0); 257 } 258 259 int 260 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg) 261 { 262 struct acpithinkpad_softc *sc = arg; 263 int handled = 0; 264 int64_t event; 265 266 if (notify_type == 0x00) { 267 /* Poll sensors */ 268 thinkpad_sensor_refresh(sc); 269 return (0); 270 } 271 272 if (notify_type != 0x80) 273 return (1); 274 275 for (;;) { 276 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP", 277 0, NULL, &event)) 278 break; 279 if (event == 0) 280 break; 281 282 switch (event) { 283 case THINKPAD_BUTTON_BRIGHTNESS_UP: 284 thinkpad_brightness_up(sc); 285 handled = 1; 286 break; 287 case THINKPAD_BUTTON_BRIGHTNESS_DOWN: 288 thinkpad_brightness_down(sc); 289 handled = 1; 290 break; 291 case THINKPAD_BUTTON_WIRELESS: 292 thinkpad_toggle_bluetooth(sc); 293 handled = 1; 294 break; 295 case THINKPAD_BUTTON_SUSPEND: 296 #ifndef SMALL_KERNEL 297 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) 298 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 299 sc->sc_acpi, ACPI_STATE_S3); 300 #endif 301 handled = 1; 302 break; 303 case THINKPAD_BUTTON_HIBERNATE: 304 case THINKPAD_BUTTON_FN_F1: 305 case THINKPAD_BUTTON_LOCK_SCREEN: 306 case THINKPAD_BUTTON_BATTERY_INFO: 307 case THINKPAD_BUTTON_FN_F6: 308 case THINKPAD_BUTTON_EXTERNAL_SCREEN: 309 case THINKPAD_BUTTON_POINTER_SWITCH: 310 case THINKPAD_BUTTON_EJECT: 311 case THINKPAD_BUTTON_THINKLIGHT: 312 case THINKPAD_BUTTON_FN_SPACE: 313 handled = 1; 314 break; 315 case THINKPAD_BUTTON_VOLUME_MUTE: 316 thinkpad_volume_mute(sc); 317 handled = 1; 318 break; 319 case THINKPAD_BUTTON_VOLUME_DOWN: 320 thinkpad_volume_down(sc); 321 handled = 1; 322 break; 323 case THINKPAD_BUTTON_VOLUME_UP: 324 thinkpad_volume_up(sc); 325 handled = 1; 326 break; 327 case THINKPAD_BUTTON_MICROPHONE_MUTE: 328 #if NAUDIO > 0 && NWSKBD > 0 329 workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume, 330 (void *)(long)0, (void *)(int)0); 331 #endif 332 handled = 1; 333 break; 334 case THINKPAD_BUTTON_THINKVANTAGE: 335 case THINKPAD_BUTTON_FN_F11: 336 handled = 1; 337 break; 338 case THINKPAD_LID_OPEN: 339 case THINKPAD_LID_CLOSED: 340 case THINKPAD_TABLET_SCREEN_NORMAL: 341 case THINKPAD_TABLET_SCREEN_ROTATED: 342 case THINKPAD_BRIGHTNESS_CHANGED: 343 case THINKPAD_TABLET_PEN_INSERTED: 344 case THINKPAD_TABLET_PEN_REMOVED: 345 handled = 1; 346 break; 347 case THINKPAD_POWER_CHANGED: 348 handled = 1; 349 break; 350 case THINKPAD_SWITCH_WIRELESS: 351 handled = 1; 352 break; 353 default: 354 printf("%s: unknown event 0x%03x\n", 355 DEVNAME(sc), event); 356 } 357 } 358 359 return (handled); 360 } 361 362 int 363 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc) 364 { 365 struct aml_value arg; 366 int64_t bluetooth; 367 368 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC", 369 0, NULL, &bluetooth)) 370 return (1); 371 372 if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT)) 373 return (1); 374 375 bzero(&arg, sizeof(arg)); 376 arg.type = AML_OBJTYPE_INTEGER; 377 arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED; 378 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC", 379 1, &arg, NULL)) { 380 printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc)); 381 return (1); 382 } 383 384 return (0); 385 } 386 387 int 388 thinkpad_toggle_wan(struct acpithinkpad_softc *sc) 389 { 390 struct aml_value arg; 391 int64_t wan; 392 393 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN", 394 0, NULL, &wan)) 395 return (1); 396 397 if (!(wan & THINKPAD_WAN_PRESENT)) 398 return (1); 399 400 bzero(&arg, sizeof(arg)); 401 arg.type = AML_OBJTYPE_INTEGER; 402 arg.v_integer = wan ^ THINKPAD_WAN_ENABLED; 403 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN", 404 1, &arg, NULL)) { 405 printf("%s: couldn't toggle wan\n", DEVNAME(sc)); 406 return (1); 407 } 408 409 return (0); 410 } 411 412 int 413 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd) 414 { 415 struct aml_value arg; 416 417 bzero(&arg, sizeof(arg)); 418 arg.type = AML_OBJTYPE_INTEGER; 419 arg.v_integer = cmd; 420 aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL); 421 return (0); 422 } 423 424 int 425 thinkpad_volume_down(struct acpithinkpad_softc *sc) 426 { 427 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN)); 428 } 429 430 int 431 thinkpad_volume_up(struct acpithinkpad_softc *sc) 432 { 433 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP)); 434 } 435 436 int 437 thinkpad_volume_mute(struct acpithinkpad_softc *sc) 438 { 439 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE)); 440 } 441 442 int 443 thinkpad_brightness_up(struct acpithinkpad_softc *sc) 444 { 445 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP)); 446 } 447 448 int 449 thinkpad_brightness_down(struct acpithinkpad_softc *sc) 450 { 451 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN)); 452 } 453