1 /* $NetBSD: wmi_hp.c,v 1.11 2018/06/06 01:49:08 maya 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.11 2018/06/06 01:49:08 maya 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 aprint_naive("\n"); 203 aprint_normal(": HP WMI mappings\n"); 204 205 (void)pmf_device_register(sc->sc_dev, wmi_hp_suspend, wmi_hp_resume); 206 207 if (acpi_wmi_guid_match(parent, WMI_HP_GUID_EVENT) != 0) 208 rv = acpi_wmi_event_register(parent, wmi_hp_notify_handler); 209 210 if (ACPI_FAILURE(rv)) 211 return; 212 213 sc->sc_sensor = kmem_alloc(WMI_HP_SENSOR_SIZE, KM_SLEEP); 214 215 wmi_hp_sc = sc; /* XXX Can I pass sc as a cookie to sysctl? */ 216 wmi_hp_sensor_init(sc); 217 sysctl_wmi_hp_setup(sc); 218 } 219 220 static int 221 wmi_hp_detach(device_t self, int flags) 222 { 223 struct wmi_hp_softc *sc = device_private(self); 224 device_t parent = sc->sc_parent; 225 226 (void)acpi_wmi_event_deregister(parent); 227 228 if (sc->sc_sme != NULL) 229 sysmon_envsys_unregister(sc->sc_sme); 230 231 if (sc->sc_sensor != NULL) 232 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 233 234 if (sc->sc_arg != NULL) 235 kmem_free(sc->sc_arg, WMI_HP_METHOD_ARG_SIZE); 236 237 pmf_device_deregister(self); 238 239 if (wmihp_sysctllog != NULL) 240 sysctl_teardown(&wmihp_sysctllog); 241 wmihp_sysctllog = NULL; 242 wmi_hp_sc = NULL; 243 244 return 0; 245 } 246 247 static bool 248 wmi_hp_suspend(device_t self, const pmf_qual_t *qual) 249 { 250 struct wmi_hp_softc *sc = device_private(self); 251 device_t parent = sc->sc_parent; 252 253 if (sc->sc_sensor != NULL) 254 (void)acpi_wmi_event_deregister(parent); 255 256 return true; 257 } 258 259 static bool 260 wmi_hp_resume(device_t self, const pmf_qual_t *qual) 261 { 262 struct wmi_hp_softc *sc = device_private(self); 263 device_t parent = sc->sc_parent; 264 265 if (sc->sc_sensor != NULL) 266 (void)acpi_wmi_event_register(parent, wmi_hp_notify_handler); 267 268 return true; 269 } 270 271 static void 272 wmi_hp_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 273 { 274 static const int handler = OSL_NOTIFY_HANDLER; 275 struct wmi_hp_softc *sc; 276 device_t self = aux; 277 ACPI_OBJECT *obj; 278 ACPI_BUFFER buf; 279 ACPI_STATUS rv; 280 uint32_t val; 281 282 buf.Pointer = NULL; 283 284 sc = device_private(self); 285 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 286 287 if (ACPI_FAILURE(rv)) 288 goto out; 289 290 obj = buf.Pointer; 291 292 if (obj->Type != ACPI_TYPE_BUFFER) { 293 rv = AE_TYPE; 294 goto out; 295 } 296 297 if (obj->Buffer.Length != 8) { 298 rv = AE_LIMIT; 299 goto out; 300 } 301 302 val = *((uint8_t *)obj->Buffer.Pointer); 303 304 if (val == 0x00) { 305 rv = AE_BAD_DATA; 306 goto out; 307 } 308 309 switch (val) { 310 311 case WMI_HP_EVENT_SWITCH: 312 rv = AcpiOsExecute(handler, wmi_hp_sensor_switch_update, self); 313 break; 314 315 case WMI_HP_EVENT_HOTKEY: 316 rv = AcpiOsExecute(handler, wmi_hp_hotkey, self); 317 break; 318 319 case WMI_HP_EVENT_DOCK: /* FALLTHROUGH */ 320 321 default: 322 aprint_debug_dev(sc->sc_dev, "unknown event 0x%02X\n", evt); 323 break; 324 } 325 326 out: 327 if (buf.Pointer != NULL) 328 ACPI_FREE(buf.Pointer); 329 330 if (ACPI_FAILURE(rv)) 331 aprint_error_dev(sc->sc_dev, "failed to get data for " 332 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 333 } 334 335 static void 336 wmi_hp_hotkey(void *aux) 337 { 338 struct wmi_hp_softc *sc; 339 device_t self = aux; 340 341 sc = device_private(self); 342 343 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HOTKEY) != true) 344 return; 345 346 switch (sc->sc_val) { 347 348 case WMI_HP_HOTKEY_BRIGHTNESS_UP: 349 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 350 break; 351 352 case WMI_HP_HOTKEY_BRIGHTNESS_DOWN: 353 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 354 break; 355 356 case WMI_HP_HOTKEY_PROG1: 357 aprint_debug_dev(self, "PROG1 hotkey pressed\n"); 358 break; 359 case WMI_HP_HOTKEY_MEDIA1: 360 aprint_debug_dev(self, "MEDIA1 hotkey pressed\n"); 361 break; 362 case WMI_HP_HOTKEY_MEDIA2: 363 aprint_debug_dev(self, "MEDIA2 hotkey pressed\n"); 364 break; 365 case WMI_HP_HOTKEY_INFO: 366 aprint_debug_dev(self, "INFO hotkey pressed\n"); 367 break; 368 case WMI_HP_HOTKEY_DIRECTION: 369 aprint_debug_dev(self, "DIRECTION hotkey pressed\n"); 370 break; 371 case WMI_HP_HOTKEY_HELP: 372 aprint_debug_dev(self, "HELP hotkey pressed\n"); 373 break; 374 default: 375 aprint_debug_dev(self, "unknown hotkey 0x%02x\n", sc->sc_val); 376 break; 377 } 378 } 379 380 static bool 381 wmi_hp_method(struct wmi_hp_softc *sc) 382 { 383 ACPI_BUFFER ibuf, obuf; 384 ACPI_STATUS rv = AE_OK; 385 ACPI_OBJECT *obj; 386 uint32_t cmd, *val; 387 388 cmd = sc->sc_arg[2]; 389 390 KDASSERT(cmd != 0); 391 KDASSERT(sc->sc_arg[0] == WMI_HP_METHOD_ARG_MAGIC); 392 393 obuf.Pointer = NULL; 394 ibuf.Pointer = sc->sc_arg; 395 ibuf.Length = WMI_HP_METHOD_ARG_SIZE; 396 397 rv = acpi_wmi_method(sc->sc_parent, 398 WMI_HP_GUID_METHOD, 0, 3, &ibuf, &obuf); 399 400 if (ACPI_FAILURE(rv)) 401 goto out; 402 403 obj = obuf.Pointer; 404 405 if (obj->Type != ACPI_TYPE_BUFFER) { 406 rv = AE_TYPE; 407 goto out; 408 } 409 410 /* 411 * val[0] unknown 412 * val[1] error code 413 * val[2] return value 414 */ 415 val = (uint32_t *)obj->Buffer.Pointer; 416 417 sc->sc_val = val[2]; 418 419 switch (val[1]) { 420 case 0: /* Ok. */ 421 break; 422 case 2: /* wrong signature */ 423 rv = AE_ERROR; 424 aprint_debug_dev(sc->sc_dev, "wrong signature " 425 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 426 break; 427 case 3: /* unknown command */ 428 rv = AE_ERROR; 429 aprint_debug_dev(sc->sc_dev, "unknown command " 430 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 431 break; 432 case 4: /* unknown command type */ 433 rv = AE_ERROR; 434 aprint_debug_dev(sc->sc_dev, "unknown command type " 435 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 436 break; 437 case 5: /* invalid parameters */ 438 rv = AE_ERROR; 439 aprint_debug_dev(sc->sc_dev, "invalid parameters " 440 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 441 break; 442 default: /* unknown error */ 443 rv = AE_ERROR; 444 aprint_debug_dev(sc->sc_dev, "unknown error " 445 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 446 break; 447 } 448 out: 449 if (obuf.Pointer != NULL) 450 ACPI_FREE(obuf.Pointer); 451 452 if (ACPI_FAILURE(rv)) { 453 aprint_debug_dev(sc->sc_dev, "failed to evaluate method " 454 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 455 return false; 456 } 457 458 return true; 459 } 460 461 static bool 462 wmi_hp_method_read(struct wmi_hp_softc *sc, uint8_t cmd) 463 { 464 465 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 466 sc->sc_arg[1] = WMI_HP_METHOD_ARG_READ; 467 sc->sc_arg[2] = cmd; 468 sc->sc_arg[3] = 0; 469 sc->sc_arg[4] = 0; 470 471 return wmi_hp_method(sc); 472 } 473 474 static bool 475 wmi_hp_method_write(struct wmi_hp_softc *sc, uint8_t cmd, uint32_t val) 476 { 477 478 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 479 sc->sc_arg[1] = WMI_HP_METHOD_ARG_WRITE; 480 sc->sc_arg[2] = cmd; 481 sc->sc_arg[3] = WMI_HP_METHOD_ARG_WRITE_SIZE; 482 sc->sc_arg[4] = val; 483 484 return wmi_hp_method(sc); 485 } 486 487 488 static void 489 wmi_hp_switch_init(struct wmi_hp_softc *sc) 490 { 491 int i, sensor[3]; 492 493 const char desc[][ENVSYS_DESCLEN] = { 494 "wireless", "bluetooth", "mobile" 495 }; 496 497 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) 498 return; 499 500 sensor[0] = WMI_HP_SWITCH_WLAN; 501 sensor[1] = WMI_HP_SWITCH_BT; 502 sensor[2] = WMI_HP_SWITCH_WWAN; 503 504 CTASSERT(WMI_HP_SENSOR_WLAN == 0); 505 CTASSERT(WMI_HP_SENSOR_BT == 1); 506 CTASSERT(WMI_HP_SENSOR_WWAN == 2); 507 508 for (i = 0; i < 3; i++) { 509 510 if ((sc->sc_val & sensor[i]) == 0) 511 continue; 512 513 (void)strlcpy(sc->sc_sensor[i].desc, desc[i], ENVSYS_DESCLEN); 514 515 sc->sc_sensor[i].state = ENVSYS_SINVALID; 516 sc->sc_sensor[i].units = ENVSYS_INDICATOR; 517 518 if (sysmon_envsys_sensor_attach(sc->sc_sme, 519 &sc->sc_sensor[i]) != 0) 520 break; 521 } 522 } 523 524 static void 525 wmi_hp_sensor_init(struct wmi_hp_softc *sc) 526 { 527 int sensor; 528 529 KDASSERT(sc->sc_sme == NULL); 530 KDASSERT(sc->sc_sensor != NULL); 531 532 (void)memset(sc->sc_sensor, 0, WMI_HP_SENSOR_SIZE); 533 534 sc->sc_sme = sysmon_envsys_create(); 535 536 wmi_hp_switch_init(sc); 537 538 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HDDTEMP) == true) { 539 sensor = WMI_HP_SENSOR_HDDTEMP; 540 (void)strlcpy(sc->sc_sensor[sensor].desc, "hddtemp", 541 ENVSYS_DESCLEN); 542 sc->sc_sensor[sensor].state = ENVSYS_SVALID; 543 sc->sc_sensor[sensor].units = ENVSYS_STEMP; 544 sc->sc_sensor[sensor].value_cur = 545 sc->sc_val * 1000000 + 273150000; 546 547 sysmon_envsys_sensor_attach(sc->sc_sme, 548 &sc->sc_sensor[sensor]); 549 } 550 551 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DISPLAY) == true) { 552 sensor = WMI_HP_SENSOR_DISPLAY; 553 (void)strlcpy(sc->sc_sensor[sensor].desc, "display", 554 ENVSYS_DESCLEN); 555 sc->sc_sensor[sensor].state = ENVSYS_SVALID; 556 sc->sc_sensor[sensor].units = ENVSYS_INDICATOR; 557 sc->sc_sensor[sensor].value_cur = sc->sc_val; 558 559 sysmon_envsys_sensor_attach(sc->sc_sme, 560 &sc->sc_sensor[sensor]); 561 } 562 563 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DOCK) == true) { 564 sensor = WMI_HP_SENSOR_DOCK; 565 (void)strlcpy(sc->sc_sensor[sensor].desc, "docking station", 566 ENVSYS_DESCLEN); 567 sc->sc_sensor[sensor].state = ENVSYS_SVALID; 568 sc->sc_sensor[sensor].units = ENVSYS_INDICATOR; 569 sc->sc_sensor[sensor].value_cur = sc->sc_val; 570 571 sysmon_envsys_sensor_attach(sc->sc_sme, 572 &sc->sc_sensor[sensor]); 573 } 574 575 sc->sc_sme->sme_cookie = sc; 576 sc->sc_sme->sme_refresh = wmi_hp_sensor_refresh; 577 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 578 579 if (sysmon_envsys_register(sc->sc_sme) != 0) 580 goto fail; 581 582 wmi_hp_sensor_switch_update(sc->sc_dev); 583 584 return; 585 586 fail: 587 aprint_debug_dev(sc->sc_dev, "failed to initialize sysmon\n"); 588 589 sysmon_envsys_destroy(sc->sc_sme); 590 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 591 592 sc->sc_sme = NULL; 593 sc->sc_sensor = NULL; 594 } 595 596 static void 597 wmi_hp_sensor_switch_update(void *aux) 598 { 599 struct wmi_hp_softc *sc; 600 device_t self = aux; 601 602 sc = device_private(self); 603 604 if (sc->sc_sme == NULL || sc->sc_sensor == NULL) 605 return; 606 607 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) { 608 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SINVALID; 609 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SINVALID; 610 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SINVALID; 611 return; 612 } 613 614 if ((sc->sc_val & WMI_HP_SWITCH_WLAN) != 0) { 615 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 0; 616 617 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WLAN_ONAIR) != 0) 618 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 1; 619 620 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SVALID; 621 } 622 623 if ((sc->sc_val & WMI_HP_SWITCH_BT) != 0) { 624 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 0; 625 626 if ((sc->sc_val & WMI_HP_SWITCH_MASK_BT_ONAIR) != 0) 627 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 1; 628 629 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SVALID; 630 } 631 632 if ((sc->sc_val & WMI_HP_SWITCH_WWAN) != 0) { 633 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 0; 634 635 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WWAN_ONAIR) != 0) 636 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 1; 637 638 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SVALID; 639 } 640 } 641 642 static void 643 wmi_hp_sensor_read(struct wmi_hp_softc *sc, envsys_data_t *sensor, int cmd) 644 { 645 if (wmi_hp_method_read(sc, cmd) == true) { 646 sensor->state = ENVSYS_SVALID; 647 sensor->value_cur = sc->sc_val; 648 } else { 649 sensor->state = ENVSYS_SINVALID; 650 } 651 } 652 653 static void 654 wmi_hp_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 655 { 656 struct wmi_hp_softc *sc = sme->sme_cookie; 657 envsys_data_t *sensor; 658 659 sensor = &sc->sc_sensor[WMI_HP_SENSOR_HDDTEMP]; 660 wmi_hp_sensor_read(sc, sensor, WMI_HP_METHOD_CMD_HDDTEMP); 661 if (sensor->state == ENVSYS_SVALID) { 662 sensor->value_cur = sensor->value_cur * 1000000 + 663 273150000; 664 } 665 666 wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DISPLAY], 667 WMI_HP_METHOD_CMD_DISPLAY); 668 669 wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DOCK], 670 WMI_HP_METHOD_CMD_DOCK); 671 } 672 673 MODULE(MODULE_CLASS_DRIVER, wmihp, "acpiwmi,sysmon_envsys"); 674 675 #ifdef _MODULE 676 #include "ioconf.c" 677 #endif 678 679 static int 680 wmihp_modcmd(modcmd_t cmd, void *aux) 681 { 682 int rv = 0; 683 684 switch (cmd) { 685 686 case MODULE_CMD_INIT: 687 688 #ifdef _MODULE 689 rv = config_init_component(cfdriver_ioconf_wmihp, 690 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); 691 #endif 692 break; 693 694 case MODULE_CMD_FINI: 695 696 #ifdef _MODULE 697 rv = config_fini_component(cfdriver_ioconf_wmihp, 698 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); 699 #endif 700 break; 701 702 default: 703 rv = ENOTTY; 704 } 705 706 return rv; 707 } 708 709 static int 710 sysctl_wmi_hp_set_als(SYSCTLFN_ARGS) 711 { 712 struct sysctlnode node; 713 int err; 714 int als = wmihp_als; 715 struct wmi_hp_softc *sc = wmi_hp_sc; 716 717 node = *rnode; 718 node.sysctl_data = &als; 719 720 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 721 722 if (err != 0 || newp == NULL) 723 return err; 724 725 if (als < 0 || als > 1) 726 return EINVAL; 727 728 if (wmi_hp_method_write(sc, WMI_HP_METHOD_CMD_ALS, als) == true) { 729 wmihp_als = als; 730 return 0; 731 } 732 733 return EIO; 734 } 735 736 static void 737 sysctl_wmi_hp_setup(struct wmi_hp_softc *sc) 738 { 739 const struct sysctlnode *rnode; 740 int err; 741 742 err = sysctl_createv(&wmihp_sysctllog, 0, NULL, &rnode, 743 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL, 744 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); 745 746 if (err != 0) 747 return; 748 749 err = sysctl_createv(&wmihp_sysctllog, 0, &rnode, &rnode, 750 0, CTLTYPE_NODE, "wmi", SYSCTL_DESCR("ACPI HP WMI"), 751 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 752 753 if (err != 0) 754 return; 755 756 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_ALS) == true) { 757 (void)sysctl_createv(NULL, 0, &rnode, NULL, 758 CTLFLAG_READWRITE, CTLTYPE_BOOL, "als", 759 SYSCTL_DESCR("Ambient Light Sensor"), 760 sysctl_wmi_hp_set_als, 0, NULL, 0, CTL_CREATE, CTL_EOL); 761 } 762 } 763