1 /* $NetBSD: wmi_hp.c,v 1.2 2010/06/30 07:58:11 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.2 2010/06/30 07:58:11 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 buf.Pointer = NULL; 256 257 sc = device_private(self); 258 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 259 260 if (ACPI_FAILURE(rv)) 261 goto out; 262 263 obj = buf.Pointer; 264 265 if (obj->Type != ACPI_TYPE_BUFFER) { 266 rv = AE_TYPE; 267 goto out; 268 } 269 270 if (obj->Buffer.Length != 8) { 271 rv = AE_LIMIT; 272 goto out; 273 } 274 275 val = *((uint8_t *)obj->Buffer.Pointer); 276 277 if (val == 0x00) { 278 rv = AE_BAD_DATA; 279 goto out; 280 } 281 282 switch (val) { 283 284 case WMI_HP_EVENT_SWITCH: 285 rv = AcpiOsExecute(handler, wmi_hp_sensor_update, self); 286 break; 287 288 case WMI_HP_EVENT_HOTKEY: 289 rv = AcpiOsExecute(handler, wmi_hp_hotkey, self); 290 break; 291 292 case WMI_HP_EVENT_DOCK: /* FALLTHROUGH */ 293 294 default: 295 aprint_debug_dev(sc->sc_dev, "unknown event 0x%02X\n", evt); 296 break; 297 } 298 299 out: 300 if (buf.Pointer != NULL) 301 ACPI_FREE(buf.Pointer); 302 303 if (ACPI_FAILURE(rv)) 304 aprint_error_dev(sc->sc_dev, "failed to get data for " 305 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 306 } 307 308 static void 309 wmi_hp_hotkey(void *aux) 310 { 311 struct wmi_hp_softc *sc; 312 device_t self = aux; 313 314 sc = device_private(self); 315 316 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HOTKEY) != true) 317 return; 318 319 switch (sc->sc_val) { 320 321 case WMI_HP_HOTKEY_BRIGHTNESS_UP: 322 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 323 break; 324 325 case WMI_HP_HOTKEY_BRIGHTNESS_DOWN: 326 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 327 break; 328 329 default: 330 aprint_debug_dev(self, "unknown hotkey 0x%02x\n", sc->sc_val); 331 break; 332 } 333 } 334 335 static bool 336 wmi_hp_method(struct wmi_hp_softc *sc) 337 { 338 ACPI_BUFFER ibuf, obuf; 339 ACPI_STATUS rv = AE_OK; 340 ACPI_OBJECT *obj; 341 uint32_t cmd, *val; 342 343 cmd = sc->sc_arg[2]; 344 345 KDASSERT(cmd != 0); 346 KDASSERT(sc->sc_arg[0] == WMI_HP_METHOD_ARG_MAGIC); 347 348 obuf.Pointer = NULL; 349 ibuf.Pointer = sc->sc_arg; 350 ibuf.Length = WMI_HP_METHOD_ARG_SIZE; 351 352 rv = acpi_wmi_method(sc->sc_parent, 353 WMI_HP_GUID_METHOD, 0, 3, &ibuf, &obuf); 354 355 if (ACPI_FAILURE(rv)) 356 goto out; 357 358 obj = obuf.Pointer; 359 360 if (obj->Type != ACPI_TYPE_BUFFER) { 361 rv = AE_TYPE; 362 goto out; 363 } 364 365 /* 366 * val[0] unknown 367 * val[1] error code 368 * val[2] return value 369 */ 370 val = (uint32_t *)obj->Buffer.Pointer; 371 372 if (val[1] != 0) { 373 rv = AE_ERROR; 374 goto out; 375 } 376 377 sc->sc_val = val[2]; 378 379 out: 380 if (obuf.Pointer != NULL) 381 ACPI_FREE(obuf.Pointer); 382 383 if (ACPI_FAILURE(rv)) { 384 aprint_debug_dev(sc->sc_dev, "failed to evaluate method " 385 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 386 return false; 387 } 388 389 return true; 390 } 391 392 static bool 393 wmi_hp_method_read(struct wmi_hp_softc *sc, uint8_t cmd) 394 { 395 396 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 397 sc->sc_arg[1] = WMI_HP_METHOD_ARG_READ; 398 sc->sc_arg[2] = cmd; 399 sc->sc_arg[3] = 0; 400 sc->sc_arg[4] = 0; 401 402 return wmi_hp_method(sc); 403 } 404 405 #if 0 406 static bool 407 wmi_hp_method_write(struct wmi_hp_softc *sc, uint8_t cmd, uint32_t val) 408 { 409 410 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 411 sc->sc_arg[1] = WMI_HP_METHOD_ARG_WRITE; 412 sc->sc_arg[2] = cmd; 413 sc->sc_arg[3] = WMI_HP_METHOD_ARG_WRITE_SIZE; 414 sc->sc_arg[4] = val; 415 416 return wmi_hp_method(sc); 417 } 418 #endif 419 420 static void 421 wmi_hp_sensor_init(struct wmi_hp_softc *sc) 422 { 423 int i, j, sensor[3]; 424 425 const char desc[][ENVSYS_DESCLEN] = { 426 "wireless", "bluetooth", "mobile" 427 }; 428 429 KDASSERT(sc->sc_sme == NULL); 430 KDASSERT(sc->sc_sensor != NULL); 431 432 (void)memset(sc->sc_sensor, 0, WMI_HP_SENSOR_SIZE); 433 434 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) 435 return; 436 437 sc->sc_sme = sysmon_envsys_create(); 438 439 sensor[0] = WMI_HP_SWITCH_WLAN; 440 sensor[1] = WMI_HP_SWITCH_BT; 441 sensor[2] = WMI_HP_SWITCH_WWAN; 442 443 CTASSERT(WMI_HP_SENSOR_WLAN == 0); 444 CTASSERT(WMI_HP_SENSOR_BT == 1); 445 CTASSERT(WMI_HP_SENSOR_WWAN == 2); 446 447 for (i = j = 0; i < 3; i++) { 448 449 if ((sc->sc_val & sensor[i]) == 0) 450 continue; 451 452 (void)strlcpy(sc->sc_sensor[i].desc, desc[i], ENVSYS_DESCLEN); 453 454 sc->sc_sensor[i].state = ENVSYS_SINVALID; 455 sc->sc_sensor[i].units = ENVSYS_INDICATOR; 456 457 if (sysmon_envsys_sensor_attach(sc->sc_sme, 458 &sc->sc_sensor[i]) != 0) 459 goto fail; 460 461 j++; 462 } 463 464 if (j == 0) 465 goto fail; 466 467 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; 468 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 469 470 if (sysmon_envsys_register(sc->sc_sme) != 0) 471 goto fail; 472 473 wmi_hp_sensor_update(sc->sc_dev); 474 475 return; 476 477 fail: 478 aprint_debug_dev(sc->sc_dev, "failed to initialize sysmon\n"); 479 480 sysmon_envsys_destroy(sc->sc_sme); 481 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 482 483 sc->sc_sme = NULL; 484 sc->sc_sensor = NULL; 485 } 486 487 static void 488 wmi_hp_sensor_update(void *aux) 489 { 490 struct wmi_hp_softc *sc; 491 device_t self = aux; 492 493 sc = device_private(self); 494 495 if (sc->sc_sme == NULL || sc->sc_sensor == NULL) 496 return; 497 498 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) { 499 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SINVALID; 500 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SINVALID; 501 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SINVALID; 502 return; 503 } 504 505 if ((sc->sc_val & WMI_HP_SWITCH_WLAN) != 0) { 506 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 0; 507 508 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WLAN_ONAIR) != 0) 509 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 1; 510 511 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SVALID; 512 } 513 514 if ((sc->sc_val & WMI_HP_SWITCH_BT) != 0) { 515 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 0; 516 517 if ((sc->sc_val & WMI_HP_SWITCH_MASK_BT_ONAIR) != 0) 518 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 1; 519 520 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SVALID; 521 } 522 523 if ((sc->sc_val & WMI_HP_SWITCH_WWAN) != 0) { 524 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 0; 525 526 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WWAN_ONAIR) != 0) 527 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 1; 528 529 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SVALID; 530 } 531 } 532