1 /* $NetBSD: wmi_hp.c,v 1.9 2015/04/23 23:23:00 pgoyette 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.9 2015/04/23 23:23:00 pgoyette Exp $"); 61 62 #include <sys/param.h> 63 #include <sys/device.h> 64 #include <sys/kmem.h> 65 #include <sys/module.h> 66 67 #include <dev/acpi/acpireg.h> 68 #include <dev/acpi/acpivar.h> 69 #include <dev/acpi/wmi/wmi_acpivar.h> 70 71 #include <dev/sysmon/sysmonvar.h> 72 73 #include <sys/sysctl.h> 74 75 /* 76 * HP CMI whitepaper: 77 * http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf 78 */ 79 80 #define _COMPONENT ACPI_RESOURCE_COMPONENT 81 ACPI_MODULE_NAME ("wmi_hp") 82 83 #define WMI_HP_METHOD_ARG_READ 0x01 84 #define WMI_HP_METHOD_ARG_WRITE 0x02 85 #define WMI_HP_METHOD_ARG_WRITE_SIZE 0x04 86 #define WMI_HP_METHOD_ARG_MAGIC 0x55434553 87 #define WMI_HP_METHOD_ARG_SIZE 0x05 * sizeof(uint32_t) 88 89 #define WMI_HP_METHOD_CMD_DISPLAY 0x01 90 #define WMI_HP_METHOD_CMD_HDDTEMP 0x02 91 #define WMI_HP_METHOD_CMD_ALS 0x03 92 #define WMI_HP_METHOD_CMD_DOCK 0x04 93 #define WMI_HP_METHOD_CMD_SWITCH 0x05 94 #define WMI_HP_METHOD_CMD_HOTKEY 0x0C 95 96 #define WMI_HP_EVENT_DOCK 0x01 97 #define WMI_HP_EVENT_HOTKEY 0x04 98 #define WMI_HP_EVENT_SWITCH 0x05 99 /* WMI_HP_EVENT_UNKNOWN 0xXX */ 100 101 #define WMI_HP_HOTKEY_BRIGHTNESS_UP 0x02 102 #define WMI_HP_HOTKEY_BRIGHTNESS_DOWN 0x03 103 #define WMI_HP_HOTKEY_PROG1 0x20e6 104 #define WMI_HP_HOTKEY_MEDIA1 0x20e8 105 #define WMI_HP_HOTKEY_MEDIA2 0x2142 106 #define WMI_HP_HOTKEY_INFO 0x213b 107 #define WMI_HP_HOTKEY_DIRECTION 0x2169 108 #define WMI_HP_HOTKEY_HELP 0x231b 109 /* WMI_HP_HOTKEY_UNKNOWN 0xXX */ 110 111 #define WMI_HP_SWITCH_WLAN 0x01 112 #define WMI_HP_SWITCH_BT 0x02 113 #define WMI_HP_SWITCH_WWAN 0x04 114 115 #define WMI_HP_SWITCH_ARG_WLAN_OFF 0x100 116 #define WMI_HP_SWITCH_ARG_WLAN_ON 0x101 117 #define WMI_HP_SWITCH_ARG_BT_OFF 0x200 118 #define WMI_HP_SWITCH_ARG_BT_ON 0x202 119 #define WMI_HP_SWITCH_ARG_WWAN_OFF 0x400 120 #define WMI_HP_SWITCH_ARG_WWAN_ON 0x404 121 122 #define WMI_HP_SWITCH_MASK_WLAN_ONAIR __BIT(8) 123 #define WMI_HP_SWITCH_MASK_WLAN_ENABLED __BIT(9) 124 #define WMI_HP_SWITCH_MASK_WLAN_RADIO __BIT(11) 125 #define WMI_HP_SWITCH_MASK_BT_ONAIR __BIT(16) 126 #define WMI_HP_SWITCH_MASK_BT_ENABLED __BIT(17) 127 #define WMI_HP_SWITCH_MASK_BT_RADIO __BIT(19) 128 #define WMI_HP_SWITCH_MASK_WWAN_ONAIR __BIT(24) 129 #define WMI_HP_SWITCH_MASK_WWAN_ENABLED __BIT(25) 130 #define WMI_HP_SWITCH_MASK_WWAN_RADIO __BIT(27) 131 132 #define WMI_HP_GUID_EVENT "95F24279-4D7B-4334-9387-ACCDC67EF61C" 133 #define WMI_HP_GUID_METHOD "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" 134 #define WMI_HP_GUID_CMI "2D114B49-2DFB-4130-B8FE-4A3C09E75133" 135 136 #define WMI_HP_SENSOR_WLAN 0 137 #define WMI_HP_SENSOR_BT 1 138 #define WMI_HP_SENSOR_WWAN 2 139 #define WMI_HP_SENSOR_HDDTEMP 3 140 #define WMI_HP_SENSOR_DISPLAY 4 141 #define WMI_HP_SENSOR_DOCK 5 142 #define WMI_HP_SENSOR_COUNT 6 143 #define WMI_HP_SENSOR_SIZE WMI_HP_SENSOR_COUNT * sizeof(envsys_data_t) 144 145 #define ACPI_HP_CMI_PATHS 0x01 146 #define ACPI_HP_CMI_ENUMS 0x02 147 #define ACPI_HP_CMI_FLAGS 0x04 148 #define ACPI_HP_CMI_MAX_INSTANCE 0x08 149 150 struct wmi_hp_softc { 151 device_t sc_dev; 152 device_t sc_parent; 153 struct sysmon_envsys *sc_sme; 154 envsys_data_t *sc_sensor; 155 uint32_t *sc_arg; 156 uint32_t sc_val; 157 }; 158 159 static int wmi_hp_match(device_t, cfdata_t, void *); 160 static void wmi_hp_attach(device_t, device_t, void *); 161 static int wmi_hp_detach(device_t, int); 162 static bool wmi_hp_suspend(device_t, const pmf_qual_t *); 163 static bool wmi_hp_resume(device_t, const pmf_qual_t *); 164 static void wmi_hp_notify_handler(ACPI_HANDLE, uint32_t, void *); 165 static void wmi_hp_hotkey(void *); 166 static bool wmi_hp_method(struct wmi_hp_softc *); 167 static bool wmi_hp_method_read(struct wmi_hp_softc *, uint8_t); 168 static bool wmi_hp_method_write(struct wmi_hp_softc *, uint8_t, uint32_t); 169 170 static void wmi_hp_sensor_init(struct wmi_hp_softc *); 171 static void wmi_hp_sensor_switch_update(void *); 172 static void wmi_hp_sensor_refresh(struct sysmon_envsys *, envsys_data_t *); 173 174 static void sysctl_wmi_hp_setup(struct wmi_hp_softc *); 175 static int sysctl_wmi_hp_set_als(SYSCTLFN_PROTO); 176 static struct sysctllog *wmihp_sysctllog = NULL; 177 static int wmihp_als = 0; 178 static struct wmi_hp_softc *wmi_hp_sc = NULL; /* XXX */ 179 180 CFATTACH_DECL_NEW(wmihp, sizeof(struct wmi_hp_softc), 181 wmi_hp_match, wmi_hp_attach, wmi_hp_detach, NULL); 182 183 static int 184 wmi_hp_match(device_t parent, cfdata_t match, void *aux) 185 { 186 return acpi_wmi_guid_match(parent, WMI_HP_GUID_METHOD); 187 } 188 189 static void 190 wmi_hp_attach(device_t parent, device_t self, void *aux) 191 { 192 struct wmi_hp_softc *sc = device_private(self); 193 ACPI_STATUS rv = AE_ERROR; 194 195 sc->sc_dev = self; 196 sc->sc_parent = parent; 197 198 sc->sc_sme = NULL; 199 sc->sc_sensor = NULL; 200 201 sc->sc_arg = kmem_alloc(WMI_HP_METHOD_ARG_SIZE, KM_SLEEP); 202 203 if (sc->sc_arg == NULL) 204 return; 205 206 aprint_naive("\n"); 207 aprint_normal(": HP WMI mappings\n"); 208 209 (void)pmf_device_register(sc->sc_dev, wmi_hp_suspend, wmi_hp_resume); 210 211 if (acpi_wmi_guid_match(parent, WMI_HP_GUID_EVENT) != 0) 212 rv = acpi_wmi_event_register(parent, wmi_hp_notify_handler); 213 214 if (ACPI_FAILURE(rv)) 215 return; 216 217 sc->sc_sensor = kmem_alloc(WMI_HP_SENSOR_SIZE, KM_SLEEP); 218 219 if (sc->sc_sensor == NULL) 220 return; 221 222 wmi_hp_sc = sc; /* XXX Can I pass sc as a cookie to sysctl? */ 223 wmi_hp_sensor_init(sc); 224 sysctl_wmi_hp_setup(sc); 225 } 226 227 static int 228 wmi_hp_detach(device_t self, int flags) 229 { 230 struct wmi_hp_softc *sc = device_private(self); 231 device_t parent = sc->sc_parent; 232 233 (void)acpi_wmi_event_deregister(parent); 234 235 if (sc->sc_sme != NULL) 236 sysmon_envsys_unregister(sc->sc_sme); 237 238 if (sc->sc_sensor != NULL) 239 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 240 241 if (sc->sc_arg != NULL) 242 kmem_free(sc->sc_arg, WMI_HP_METHOD_ARG_SIZE); 243 244 pmf_device_deregister(self); 245 246 if (wmihp_sysctllog != NULL) 247 sysctl_teardown(&wmihp_sysctllog); 248 wmihp_sysctllog = NULL; 249 wmi_hp_sc = NULL; 250 251 return 0; 252 } 253 254 static bool 255 wmi_hp_suspend(device_t self, const pmf_qual_t *qual) 256 { 257 struct wmi_hp_softc *sc = device_private(self); 258 device_t parent = sc->sc_parent; 259 260 if (sc->sc_sensor != NULL) 261 (void)acpi_wmi_event_deregister(parent); 262 263 return true; 264 } 265 266 static bool 267 wmi_hp_resume(device_t self, const pmf_qual_t *qual) 268 { 269 struct wmi_hp_softc *sc = device_private(self); 270 device_t parent = sc->sc_parent; 271 272 if (sc->sc_sensor != NULL) 273 (void)acpi_wmi_event_register(parent, wmi_hp_notify_handler); 274 275 return true; 276 } 277 278 static void 279 wmi_hp_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 280 { 281 static const int handler = OSL_NOTIFY_HANDLER; 282 struct wmi_hp_softc *sc; 283 device_t self = aux; 284 ACPI_OBJECT *obj; 285 ACPI_BUFFER buf; 286 ACPI_STATUS rv; 287 uint32_t val; 288 289 buf.Pointer = NULL; 290 291 sc = device_private(self); 292 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 293 294 if (ACPI_FAILURE(rv)) 295 goto out; 296 297 obj = buf.Pointer; 298 299 if (obj->Type != ACPI_TYPE_BUFFER) { 300 rv = AE_TYPE; 301 goto out; 302 } 303 304 if (obj->Buffer.Length != 8) { 305 rv = AE_LIMIT; 306 goto out; 307 } 308 309 val = *((uint8_t *)obj->Buffer.Pointer); 310 311 if (val == 0x00) { 312 rv = AE_BAD_DATA; 313 goto out; 314 } 315 316 switch (val) { 317 318 case WMI_HP_EVENT_SWITCH: 319 rv = AcpiOsExecute(handler, wmi_hp_sensor_switch_update, self); 320 break; 321 322 case WMI_HP_EVENT_HOTKEY: 323 rv = AcpiOsExecute(handler, wmi_hp_hotkey, self); 324 break; 325 326 case WMI_HP_EVENT_DOCK: /* FALLTHROUGH */ 327 328 default: 329 aprint_debug_dev(sc->sc_dev, "unknown event 0x%02X\n", evt); 330 break; 331 } 332 333 out: 334 if (buf.Pointer != NULL) 335 ACPI_FREE(buf.Pointer); 336 337 if (ACPI_FAILURE(rv)) 338 aprint_error_dev(sc->sc_dev, "failed to get data for " 339 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 340 } 341 342 static void 343 wmi_hp_hotkey(void *aux) 344 { 345 struct wmi_hp_softc *sc; 346 device_t self = aux; 347 348 sc = device_private(self); 349 350 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HOTKEY) != true) 351 return; 352 353 switch (sc->sc_val) { 354 355 case WMI_HP_HOTKEY_BRIGHTNESS_UP: 356 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 357 break; 358 359 case WMI_HP_HOTKEY_BRIGHTNESS_DOWN: 360 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 361 break; 362 363 case WMI_HP_HOTKEY_PROG1: 364 aprint_debug_dev(self, "PROG1 hotkey pressed\n"); 365 break; 366 case WMI_HP_HOTKEY_MEDIA1: 367 aprint_debug_dev(self, "MEDIA1 hotkey pressed\n"); 368 break; 369 case WMI_HP_HOTKEY_MEDIA2: 370 aprint_debug_dev(self, "MEDIA2 hotkey pressed\n"); 371 break; 372 case WMI_HP_HOTKEY_INFO: 373 aprint_debug_dev(self, "INFO hotkey pressed\n"); 374 break; 375 case WMI_HP_HOTKEY_DIRECTION: 376 aprint_debug_dev(self, "DIRECTION hotkey pressed\n"); 377 break; 378 case WMI_HP_HOTKEY_HELP: 379 aprint_debug_dev(self, "HELP hotkey pressed\n"); 380 break; 381 default: 382 aprint_debug_dev(self, "unknown hotkey 0x%02x\n", sc->sc_val); 383 break; 384 } 385 } 386 387 static bool 388 wmi_hp_method(struct wmi_hp_softc *sc) 389 { 390 ACPI_BUFFER ibuf, obuf; 391 ACPI_STATUS rv = AE_OK; 392 ACPI_OBJECT *obj; 393 uint32_t cmd, *val; 394 395 cmd = sc->sc_arg[2]; 396 397 KDASSERT(cmd != 0); 398 KDASSERT(sc->sc_arg[0] == WMI_HP_METHOD_ARG_MAGIC); 399 400 obuf.Pointer = NULL; 401 ibuf.Pointer = sc->sc_arg; 402 ibuf.Length = WMI_HP_METHOD_ARG_SIZE; 403 404 rv = acpi_wmi_method(sc->sc_parent, 405 WMI_HP_GUID_METHOD, 0, 3, &ibuf, &obuf); 406 407 if (ACPI_FAILURE(rv)) 408 goto out; 409 410 obj = obuf.Pointer; 411 412 if (obj->Type != ACPI_TYPE_BUFFER) { 413 rv = AE_TYPE; 414 goto out; 415 } 416 417 /* 418 * val[0] unknown 419 * val[1] error code 420 * val[2] return value 421 */ 422 val = (uint32_t *)obj->Buffer.Pointer; 423 424 sc->sc_val = val[2]; 425 426 switch (val[1]) { 427 case 0: /* Ok. */ 428 break; 429 case 2: /* wrong signature */ 430 rv = AE_ERROR; 431 aprint_debug_dev(sc->sc_dev, "wrong signature " 432 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 433 break; 434 case 3: /* unknown command */ 435 rv = AE_ERROR; 436 aprint_debug_dev(sc->sc_dev, "unknown command " 437 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 438 break; 439 case 4: /* unknown command type */ 440 rv = AE_ERROR; 441 aprint_debug_dev(sc->sc_dev, "unknown command type " 442 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 443 break; 444 case 5: /* invalid parameters */ 445 rv = AE_ERROR; 446 aprint_debug_dev(sc->sc_dev, "invalid parameters " 447 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 448 break; 449 default: /* unknown error */ 450 rv = AE_ERROR; 451 aprint_debug_dev(sc->sc_dev, "unknown error " 452 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 453 break; 454 } 455 out: 456 if (obuf.Pointer != NULL) 457 ACPI_FREE(obuf.Pointer); 458 459 if (ACPI_FAILURE(rv)) { 460 aprint_debug_dev(sc->sc_dev, "failed to evaluate method " 461 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 462 return false; 463 } 464 465 return true; 466 } 467 468 static bool 469 wmi_hp_method_read(struct wmi_hp_softc *sc, uint8_t cmd) 470 { 471 472 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 473 sc->sc_arg[1] = WMI_HP_METHOD_ARG_READ; 474 sc->sc_arg[2] = cmd; 475 sc->sc_arg[3] = 0; 476 sc->sc_arg[4] = 0; 477 478 return wmi_hp_method(sc); 479 } 480 481 static bool 482 wmi_hp_method_write(struct wmi_hp_softc *sc, uint8_t cmd, uint32_t val) 483 { 484 485 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 486 sc->sc_arg[1] = WMI_HP_METHOD_ARG_WRITE; 487 sc->sc_arg[2] = cmd; 488 sc->sc_arg[3] = WMI_HP_METHOD_ARG_WRITE_SIZE; 489 sc->sc_arg[4] = val; 490 491 return wmi_hp_method(sc); 492 } 493 494 495 static void 496 wmi_hp_switch_init(struct wmi_hp_softc *sc) 497 { 498 int i, sensor[3]; 499 500 const char desc[][ENVSYS_DESCLEN] = { 501 "wireless", "bluetooth", "mobile" 502 }; 503 504 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) 505 return; 506 507 sensor[0] = WMI_HP_SWITCH_WLAN; 508 sensor[1] = WMI_HP_SWITCH_BT; 509 sensor[2] = WMI_HP_SWITCH_WWAN; 510 511 CTASSERT(WMI_HP_SENSOR_WLAN == 0); 512 CTASSERT(WMI_HP_SENSOR_BT == 1); 513 CTASSERT(WMI_HP_SENSOR_WWAN == 2); 514 515 for (i = 0; i < 3; i++) { 516 517 if ((sc->sc_val & sensor[i]) == 0) 518 continue; 519 520 (void)strlcpy(sc->sc_sensor[i].desc, desc[i], ENVSYS_DESCLEN); 521 522 sc->sc_sensor[i].state = ENVSYS_SINVALID; 523 sc->sc_sensor[i].units = ENVSYS_INDICATOR; 524 525 if (sysmon_envsys_sensor_attach(sc->sc_sme, 526 &sc->sc_sensor[i]) != 0) 527 break; 528 } 529 } 530 531 static void 532 wmi_hp_sensor_init(struct wmi_hp_softc *sc) 533 { 534 int sensor; 535 536 KDASSERT(sc->sc_sme == NULL); 537 KDASSERT(sc->sc_sensor != NULL); 538 539 (void)memset(sc->sc_sensor, 0, WMI_HP_SENSOR_SIZE); 540 541 sc->sc_sme = sysmon_envsys_create(); 542 543 wmi_hp_switch_init(sc); 544 545 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HDDTEMP) == true) { 546 sensor = WMI_HP_SENSOR_HDDTEMP; 547 (void)strlcpy(sc->sc_sensor[sensor].desc, "hddtemp", 548 ENVSYS_DESCLEN); 549 sc->sc_sensor[sensor].state = ENVSYS_SVALID; 550 sc->sc_sensor[sensor].units = ENVSYS_STEMP; 551 sc->sc_sensor[sensor].value_cur = 552 sc->sc_val * 1000000 + 273150000; 553 554 sysmon_envsys_sensor_attach(sc->sc_sme, 555 &sc->sc_sensor[sensor]); 556 } 557 558 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DISPLAY) == true) { 559 sensor = WMI_HP_SENSOR_DISPLAY; 560 (void)strlcpy(sc->sc_sensor[sensor].desc, "display", 561 ENVSYS_DESCLEN); 562 sc->sc_sensor[sensor].state = ENVSYS_SVALID; 563 sc->sc_sensor[sensor].units = ENVSYS_INDICATOR; 564 sc->sc_sensor[sensor].value_cur = sc->sc_val; 565 566 sysmon_envsys_sensor_attach(sc->sc_sme, 567 &sc->sc_sensor[sensor]); 568 } 569 570 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DOCK) == true) { 571 sensor = WMI_HP_SENSOR_DOCK; 572 (void)strlcpy(sc->sc_sensor[sensor].desc, "docking station", 573 ENVSYS_DESCLEN); 574 sc->sc_sensor[sensor].state = ENVSYS_SVALID; 575 sc->sc_sensor[sensor].units = ENVSYS_INDICATOR; 576 sc->sc_sensor[sensor].value_cur = sc->sc_val; 577 578 sysmon_envsys_sensor_attach(sc->sc_sme, 579 &sc->sc_sensor[sensor]); 580 } 581 582 sc->sc_sme->sme_cookie = sc; 583 sc->sc_sme->sme_refresh = wmi_hp_sensor_refresh; 584 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 585 586 if (sysmon_envsys_register(sc->sc_sme) != 0) 587 goto fail; 588 589 wmi_hp_sensor_switch_update(sc->sc_dev); 590 591 return; 592 593 fail: 594 aprint_debug_dev(sc->sc_dev, "failed to initialize sysmon\n"); 595 596 sysmon_envsys_destroy(sc->sc_sme); 597 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 598 599 sc->sc_sme = NULL; 600 sc->sc_sensor = NULL; 601 } 602 603 static void 604 wmi_hp_sensor_switch_update(void *aux) 605 { 606 struct wmi_hp_softc *sc; 607 device_t self = aux; 608 609 sc = device_private(self); 610 611 if (sc->sc_sme == NULL || sc->sc_sensor == NULL) 612 return; 613 614 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) { 615 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SINVALID; 616 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SINVALID; 617 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SINVALID; 618 return; 619 } 620 621 if ((sc->sc_val & WMI_HP_SWITCH_WLAN) != 0) { 622 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 0; 623 624 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WLAN_ONAIR) != 0) 625 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 1; 626 627 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SVALID; 628 } 629 630 if ((sc->sc_val & WMI_HP_SWITCH_BT) != 0) { 631 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 0; 632 633 if ((sc->sc_val & WMI_HP_SWITCH_MASK_BT_ONAIR) != 0) 634 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 1; 635 636 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SVALID; 637 } 638 639 if ((sc->sc_val & WMI_HP_SWITCH_WWAN) != 0) { 640 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 0; 641 642 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WWAN_ONAIR) != 0) 643 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 1; 644 645 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SVALID; 646 } 647 } 648 649 static void 650 wmi_hp_sensor_read(struct wmi_hp_softc *sc, envsys_data_t *sensor, int cmd) 651 { 652 if (wmi_hp_method_read(sc, cmd) == true) { 653 sensor->state = ENVSYS_SVALID; 654 sensor->value_cur = sc->sc_val; 655 } else { 656 sensor->state = ENVSYS_SINVALID; 657 } 658 } 659 660 static void 661 wmi_hp_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 662 { 663 struct wmi_hp_softc *sc = sme->sme_cookie; 664 envsys_data_t *sensor; 665 666 sensor = &sc->sc_sensor[WMI_HP_SENSOR_HDDTEMP]; 667 wmi_hp_sensor_read(sc, sensor, WMI_HP_METHOD_CMD_HDDTEMP); 668 if (sensor->state == ENVSYS_SVALID) { 669 sensor->value_cur = sensor->value_cur * 1000000 + 670 273150000; 671 } 672 673 wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DISPLAY], 674 WMI_HP_METHOD_CMD_DISPLAY); 675 676 wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DOCK], 677 WMI_HP_METHOD_CMD_DOCK); 678 } 679 680 MODULE(MODULE_CLASS_DRIVER, wmihp, "acpiwmi,sysmon_envsys"); 681 682 #ifdef _MODULE 683 #include "ioconf.c" 684 #endif 685 686 static int 687 wmihp_modcmd(modcmd_t cmd, void *aux) 688 { 689 int rv = 0; 690 691 switch (cmd) { 692 693 case MODULE_CMD_INIT: 694 695 #ifdef _MODULE 696 rv = config_init_component(cfdriver_ioconf_wmihp, 697 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); 698 #endif 699 break; 700 701 case MODULE_CMD_FINI: 702 703 #ifdef _MODULE 704 rv = config_fini_component(cfdriver_ioconf_wmihp, 705 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); 706 #endif 707 break; 708 709 default: 710 rv = ENOTTY; 711 } 712 713 return rv; 714 } 715 716 static int 717 sysctl_wmi_hp_set_als(SYSCTLFN_ARGS) 718 { 719 struct sysctlnode node; 720 int err; 721 int als = wmihp_als; 722 struct wmi_hp_softc *sc = wmi_hp_sc; 723 724 node = *rnode; 725 node.sysctl_data = &als; 726 727 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 728 729 if (err != 0 || newp == NULL) 730 return err;; 731 732 if (als < 0 || als > 1) 733 return EINVAL; 734 735 if (wmi_hp_method_write(sc, WMI_HP_METHOD_CMD_ALS, als) == true) { 736 wmihp_als = als; 737 return 0; 738 } 739 740 return EIO; 741 } 742 743 static void 744 sysctl_wmi_hp_setup(struct wmi_hp_softc *sc) 745 { 746 const struct sysctlnode *rnode; 747 int err; 748 749 err = sysctl_createv(&wmihp_sysctllog, 0, NULL, &rnode, 750 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL, 751 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); 752 753 if (err != 0) 754 return; 755 756 err = sysctl_createv(&wmihp_sysctllog, 0, &rnode, &rnode, 757 0, CTLTYPE_NODE, "wmi", SYSCTL_DESCR("ACPI HP WMI"), 758 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 759 760 if (err != 0) 761 return; 762 763 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_ALS) == true) { 764 (void)sysctl_createv(NULL, 0, &rnode, NULL, 765 CTLFLAG_READWRITE, CTLTYPE_BOOL, "als", 766 SYSCTL_DESCR("Ambient Light Sensor"), 767 sysctl_wmi_hp_set_als, 0, NULL, 0, CTL_CREATE, CTL_EOL); 768 } 769 } 770