1 /* $NetBSD: wmi_hp.c,v 1.1 2010/04/08 17:32:30 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jukka Ruohonen. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /*- 34 * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de> 35 * All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 49 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 */ 58 59 #include <sys/cdefs.h> 60 __KERNEL_RCSID(0, "$NetBSD: wmi_hp.c,v 1.1 2010/04/08 17:32:30 jruoho Exp $"); 61 62 #include <sys/param.h> 63 #include <sys/device.h> 64 #include <sys/kmem.h> 65 66 #include <dev/acpi/acpireg.h> 67 #include <dev/acpi/acpivar.h> 68 #include <dev/acpi/wmi/wmi_acpivar.h> 69 70 #include <dev/sysmon/sysmonvar.h> 71 72 #define _COMPONENT ACPI_RESOURCE_COMPONENT 73 ACPI_MODULE_NAME ("wmi_hp") 74 75 #define WMI_HP_METHOD_ARG_READ 0x01 76 #define WMI_HP_METHOD_ARG_WRITE 0x02 77 #define WMI_HP_METHOD_ARG_WRITE_SIZE 0x04 78 #define WMI_HP_METHOD_ARG_MAGIC 0x55434553 79 #define WMI_HP_METHOD_ARG_SIZE 0x05 * sizeof(uint32_t) 80 81 #define WMI_HP_METHOD_CMD_DISPLAY 0x01 82 #define WMI_HP_METHOD_CMD_HDDTEMP 0x02 83 #define WMI_HP_METHOD_CMD_ALS 0x03 84 #define WMI_HP_METHOD_CMD_DOCK 0x04 85 #define WMI_HP_METHOD_CMD_SWITCH 0x05 86 #define WMI_HP_METHOD_CMD_HOTKEY 0x0C 87 88 #define WMI_HP_EVENT_DOCK 0x01 89 #define WMI_HP_EVENT_HOTKEY 0x04 90 #define WMI_HP_EVENT_SWITCH 0x05 91 /* WMI_HP_EVENT_UNKNOWN 0xXX */ 92 93 #define WMI_HP_HOTKEY_BRIGHTNESS_UP 0x02 94 #define WMI_HP_HOTKEY_BRIGHTNESS_DOWN 0x03 95 /* WMI_HP_HOTKEY_UNKNOWN 0xXX */ 96 97 #define WMI_HP_SWITCH_WLAN 0x01 98 #define WMI_HP_SWITCH_BT 0x02 99 #define WMI_HP_SWITCH_WWAN 0x04 100 101 #define WMI_HP_SWITCH_ARG_WLAN_OFF 0x100 102 #define WMI_HP_SWITCH_ARG_WLAN_ON 0x101 103 #define WMI_HP_SWITCH_ARG_BT_OFF 0x200 104 #define WMI_HP_SWITCH_ARG_BT_ON 0x202 105 #define WMI_HP_SWITCH_ARG_WWAN_OFF 0x400 106 #define WMI_HP_SWITCH_ARG_WWAN_ON 0x404 107 108 #define WMI_HP_SWITCH_MASK_WLAN_ONAIR __BIT(8) 109 #define WMI_HP_SWITCH_MASK_WLAN_ENABLED __BIT(9) 110 #define WMI_HP_SWITCH_MASK_WLAN_RADIO __BIT(11) 111 #define WMI_HP_SWITCH_MASK_BT_ONAIR __BIT(16) 112 #define WMI_HP_SWITCH_MASK_BT_ENABLED __BIT(17) 113 #define WMI_HP_SWITCH_MASK_BT_RADIO __BIT(19) 114 #define WMI_HP_SWITCH_MASK_WWAN_ONAIR __BIT(24) 115 #define WMI_HP_SWITCH_MASK_WWAN_ENABLED __BIT(25) 116 #define WMI_HP_SWITCH_MASK_WWAN_RADIO __BIT(27) 117 118 #define WMI_HP_GUID_EVENT "95F24279-4D7B-4334-9387-ACCDC67EF61C" 119 #define WMI_HP_GUID_METHOD "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" 120 121 #define WMI_HP_SENSOR_WLAN 0 122 #define WMI_HP_SENSOR_BT 1 123 #define WMI_HP_SENSOR_WWAN 2 124 #define WMI_HP_SENSOR_COUNT 3 125 #define WMI_HP_SENSOR_SIZE 3 * sizeof(envsys_data_t) 126 127 struct wmi_hp_softc { 128 device_t sc_dev; 129 device_t sc_parent; 130 struct sysmon_envsys *sc_sme; 131 envsys_data_t *sc_sensor; 132 uint32_t *sc_arg; 133 uint32_t sc_val; 134 }; 135 136 static int wmi_hp_match(device_t, cfdata_t, void *); 137 static void wmi_hp_attach(device_t, device_t, void *); 138 static int wmi_hp_detach(device_t, int); 139 static bool wmi_hp_suspend(device_t, const pmf_qual_t *); 140 static bool wmi_hp_resume(device_t, const pmf_qual_t *); 141 static void wmi_hp_notify_handler(ACPI_HANDLE, uint32_t, void *); 142 static void wmi_hp_hotkey(void *); 143 static bool wmi_hp_method(struct wmi_hp_softc *); 144 static bool wmi_hp_method_read(struct wmi_hp_softc *, uint8_t); 145 146 #if 0 147 static bool wmi_hp_method_write(struct wmi_hp_softc *, uint8_t, uint32_t); 148 #endif 149 150 static void wmi_hp_sensor_init(struct wmi_hp_softc *); 151 static void wmi_hp_sensor_update(void *); 152 153 CFATTACH_DECL_NEW(wmihp, sizeof(struct wmi_hp_softc), 154 wmi_hp_match, wmi_hp_attach, wmi_hp_detach, NULL); 155 156 static int 157 wmi_hp_match(device_t parent, cfdata_t match, void *aux) 158 { 159 return acpi_wmi_guid_match(parent, WMI_HP_GUID_METHOD); 160 } 161 162 static void 163 wmi_hp_attach(device_t parent, device_t self, void *aux) 164 { 165 struct wmi_hp_softc *sc = device_private(self); 166 ACPI_STATUS rv = AE_ERROR; 167 168 sc->sc_dev = self; 169 sc->sc_parent = parent; 170 171 sc->sc_sme = NULL; 172 sc->sc_sensor = NULL; 173 174 sc->sc_arg = kmem_alloc(WMI_HP_METHOD_ARG_SIZE, KM_SLEEP); 175 176 if (sc->sc_arg == NULL) 177 return; 178 179 aprint_naive("\n"); 180 aprint_normal(": HP WMI mappings\n"); 181 182 (void)pmf_device_register(sc->sc_dev, wmi_hp_suspend, wmi_hp_resume); 183 184 if (acpi_wmi_guid_match(parent, WMI_HP_GUID_EVENT) != 0) 185 rv = acpi_wmi_event_register(parent, wmi_hp_notify_handler); 186 187 if (ACPI_FAILURE(rv)) 188 return; 189 190 sc->sc_sensor = kmem_alloc(WMI_HP_SENSOR_SIZE, KM_SLEEP); 191 192 if (sc->sc_sensor == NULL) 193 return; 194 195 wmi_hp_sensor_init(sc); 196 } 197 198 static int 199 wmi_hp_detach(device_t self, int flags) 200 { 201 struct wmi_hp_softc *sc = device_private(self); 202 device_t parent = sc->sc_parent; 203 204 (void)acpi_wmi_event_deregister(parent); 205 206 if (sc->sc_sme != NULL) 207 sysmon_envsys_unregister(sc->sc_sme); 208 209 if (sc->sc_sensor != NULL) 210 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 211 212 if (sc->sc_arg != NULL) 213 kmem_free(sc->sc_arg, WMI_HP_METHOD_ARG_SIZE); 214 215 pmf_device_deregister(self); 216 217 return 0; 218 } 219 220 static bool 221 wmi_hp_suspend(device_t self, const pmf_qual_t *qual) 222 { 223 struct wmi_hp_softc *sc = device_private(self); 224 device_t parent = sc->sc_parent; 225 226 if (sc->sc_sensor != NULL) 227 (void)acpi_wmi_event_deregister(parent); 228 229 return true; 230 } 231 232 static bool 233 wmi_hp_resume(device_t self, const pmf_qual_t *qual) 234 { 235 struct wmi_hp_softc *sc = device_private(self); 236 device_t parent = sc->sc_parent; 237 238 if (sc->sc_sensor != NULL) 239 (void)acpi_wmi_event_register(parent, wmi_hp_notify_handler); 240 241 return true; 242 } 243 244 static void 245 wmi_hp_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 246 { 247 static const int handler = OSL_NOTIFY_HANDLER; 248 struct wmi_hp_softc *sc; 249 device_t self = aux; 250 ACPI_OBJECT *obj; 251 ACPI_BUFFER buf; 252 ACPI_STATUS rv; 253 uint32_t val; 254 255 sc = device_private(self); 256 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 257 258 if (ACPI_FAILURE(rv)) 259 goto out; 260 261 obj = buf.Pointer; 262 263 if (obj->Type != ACPI_TYPE_BUFFER) { 264 rv = AE_TYPE; 265 goto out; 266 } 267 268 if (obj->Buffer.Length != 8) { 269 rv = AE_LIMIT; 270 goto out; 271 } 272 273 val = *((uint8_t *)obj->Buffer.Pointer); 274 275 if (val == 0x00) { 276 rv = AE_BAD_DATA; 277 goto out; 278 } 279 280 switch (val) { 281 282 case WMI_HP_EVENT_SWITCH: 283 rv = AcpiOsExecute(handler, wmi_hp_sensor_update, self); 284 break; 285 286 case WMI_HP_EVENT_HOTKEY: 287 rv = AcpiOsExecute(handler, wmi_hp_hotkey, self); 288 break; 289 290 case WMI_HP_EVENT_DOCK: /* FALLTHROUGH */ 291 292 default: 293 aprint_debug_dev(sc->sc_dev, "unknown event 0x%02X\n", evt); 294 break; 295 } 296 297 out: 298 if (buf.Pointer != NULL) 299 ACPI_FREE(buf.Pointer); 300 301 if (ACPI_FAILURE(rv)) 302 aprint_error_dev(sc->sc_dev, "failed to get data for " 303 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 304 } 305 306 static void 307 wmi_hp_hotkey(void *aux) 308 { 309 struct wmi_hp_softc *sc; 310 device_t self = aux; 311 312 sc = device_private(self); 313 314 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HOTKEY) != true) 315 return; 316 317 switch (sc->sc_val) { 318 319 case WMI_HP_HOTKEY_BRIGHTNESS_UP: 320 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 321 break; 322 323 case WMI_HP_HOTKEY_BRIGHTNESS_DOWN: 324 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 325 break; 326 327 default: 328 aprint_debug_dev(self, "unknown hotkey 0x%02x\n", sc->sc_val); 329 break; 330 } 331 } 332 333 static bool 334 wmi_hp_method(struct wmi_hp_softc *sc) 335 { 336 ACPI_BUFFER ibuf, obuf; 337 ACPI_STATUS rv = AE_OK; 338 ACPI_OBJECT *obj; 339 uint32_t cmd, *val; 340 341 cmd = sc->sc_arg[2]; 342 343 KDASSERT(cmd != 0); 344 KDASSERT(sc->sc_arg[0] == WMI_HP_METHOD_ARG_MAGIC); 345 346 ibuf.Pointer = sc->sc_arg; 347 ibuf.Length = WMI_HP_METHOD_ARG_SIZE; 348 349 rv = acpi_wmi_method(sc->sc_parent, 350 WMI_HP_GUID_METHOD, 0, 3, &ibuf, &obuf); 351 352 if (ACPI_FAILURE(rv)) 353 goto out; 354 355 obj = obuf.Pointer; 356 357 if (obj->Type != ACPI_TYPE_BUFFER) { 358 rv = AE_TYPE; 359 goto out; 360 } 361 362 /* 363 * val[0] unknown 364 * val[1] error code 365 * val[2] return value 366 */ 367 val = (uint32_t *)obj->Buffer.Pointer; 368 369 if (val[1] != 0) { 370 rv = AE_ERROR; 371 goto out; 372 } 373 374 sc->sc_val = val[2]; 375 376 out: 377 if (obuf.Pointer != NULL) 378 ACPI_FREE(obuf.Pointer); 379 380 if (ACPI_FAILURE(rv)) { 381 aprint_debug_dev(sc->sc_dev, "failed to evaluate method " 382 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 383 return false; 384 } 385 386 return true; 387 } 388 389 static bool 390 wmi_hp_method_read(struct wmi_hp_softc *sc, uint8_t cmd) 391 { 392 393 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 394 sc->sc_arg[1] = WMI_HP_METHOD_ARG_READ; 395 sc->sc_arg[2] = cmd; 396 sc->sc_arg[3] = 0; 397 sc->sc_arg[4] = 0; 398 399 return wmi_hp_method(sc); 400 } 401 402 #if 0 403 static bool 404 wmi_hp_method_write(struct wmi_hp_softc *sc, uint8_t cmd, uint32_t val) 405 { 406 407 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 408 sc->sc_arg[1] = WMI_HP_METHOD_ARG_WRITE; 409 sc->sc_arg[2] = cmd; 410 sc->sc_arg[3] = WMI_HP_METHOD_ARG_WRITE_SIZE; 411 sc->sc_arg[4] = val; 412 413 return wmi_hp_method(sc); 414 } 415 #endif 416 417 static void 418 wmi_hp_sensor_init(struct wmi_hp_softc *sc) 419 { 420 int i, j, sensor[3]; 421 422 const char desc[][ENVSYS_DESCLEN] = { 423 "wireless", "bluetooth", "mobile" 424 }; 425 426 KDASSERT(sc->sc_sme == NULL); 427 KDASSERT(sc->sc_sensor != NULL); 428 429 (void)memset(sc->sc_sensor, 0, WMI_HP_SENSOR_SIZE); 430 431 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) 432 return; 433 434 sc->sc_sme = sysmon_envsys_create(); 435 436 sensor[0] = WMI_HP_SWITCH_WLAN; 437 sensor[1] = WMI_HP_SWITCH_BT; 438 sensor[2] = WMI_HP_SWITCH_WWAN; 439 440 CTASSERT(WMI_HP_SENSOR_WLAN == 0); 441 CTASSERT(WMI_HP_SENSOR_BT == 1); 442 CTASSERT(WMI_HP_SENSOR_WWAN == 2); 443 444 for (i = j = 0; i < 3; i++) { 445 446 if ((sc->sc_val & sensor[i]) == 0) 447 continue; 448 449 (void)strlcpy(sc->sc_sensor[i].desc, desc[i], ENVSYS_DESCLEN); 450 451 sc->sc_sensor[i].state = ENVSYS_SINVALID; 452 sc->sc_sensor[i].units = ENVSYS_INDICATOR; 453 454 if (sysmon_envsys_sensor_attach(sc->sc_sme, 455 &sc->sc_sensor[i]) != 0) 456 goto fail; 457 458 j++; 459 } 460 461 if (j == 0) 462 goto fail; 463 464 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; 465 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 466 467 if (sysmon_envsys_register(sc->sc_sme) != 0) 468 goto fail; 469 470 wmi_hp_sensor_update(sc->sc_dev); 471 472 return; 473 474 fail: 475 aprint_debug_dev(sc->sc_dev, "failed to initialize sysmon\n"); 476 477 sysmon_envsys_destroy(sc->sc_sme); 478 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 479 480 sc->sc_sme = NULL; 481 sc->sc_sensor = NULL; 482 } 483 484 static void 485 wmi_hp_sensor_update(void *aux) 486 { 487 struct wmi_hp_softc *sc; 488 device_t self = aux; 489 490 sc = device_private(self); 491 492 if (sc->sc_sme == NULL || sc->sc_sensor == NULL) 493 return; 494 495 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) { 496 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SINVALID; 497 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SINVALID; 498 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SINVALID; 499 return; 500 } 501 502 if ((sc->sc_val & WMI_HP_SWITCH_WLAN) != 0) { 503 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 0; 504 505 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WLAN_ONAIR) != 0) 506 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 1; 507 508 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SVALID; 509 } 510 511 if ((sc->sc_val & WMI_HP_SWITCH_BT) != 0) { 512 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 0; 513 514 if ((sc->sc_val & WMI_HP_SWITCH_MASK_BT_ONAIR) != 0) 515 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 1; 516 517 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SVALID; 518 } 519 520 if ((sc->sc_val & WMI_HP_SWITCH_WWAN) != 0) { 521 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 0; 522 523 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WWAN_ONAIR) != 0) 524 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 1; 525 526 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SVALID; 527 } 528 } 529