1/* $NetBSD: wmi_hp.c,v 1.5 2011/02/16 08:19:56 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.5 2011/02/16 08:19:56 jruoho 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#define _COMPONENT ACPI_RESOURCE_COMPONENT 74ACPI_MODULE_NAME ("wmi_hp") 75 76#define WMI_HP_METHOD_ARG_READ 0x01 77#define WMI_HP_METHOD_ARG_WRITE 0x02 78#define WMI_HP_METHOD_ARG_WRITE_SIZE 0x04 79#define WMI_HP_METHOD_ARG_MAGIC 0x55434553 80#define WMI_HP_METHOD_ARG_SIZE 0x05 * sizeof(uint32_t) 81 82#define WMI_HP_METHOD_CMD_DISPLAY 0x01 83#define WMI_HP_METHOD_CMD_HDDTEMP 0x02 84#define WMI_HP_METHOD_CMD_ALS 0x03 85#define WMI_HP_METHOD_CMD_DOCK 0x04 86#define WMI_HP_METHOD_CMD_SWITCH 0x05 87#define WMI_HP_METHOD_CMD_HOTKEY 0x0C 88 89#define WMI_HP_EVENT_DOCK 0x01 90#define WMI_HP_EVENT_HOTKEY 0x04 91#define WMI_HP_EVENT_SWITCH 0x05 92/* WMI_HP_EVENT_UNKNOWN 0xXX */ 93 94#define WMI_HP_HOTKEY_BRIGHTNESS_UP 0x02 95#define WMI_HP_HOTKEY_BRIGHTNESS_DOWN 0x03 96/* WMI_HP_HOTKEY_UNKNOWN 0xXX */ 97 98#define WMI_HP_SWITCH_WLAN 0x01 99#define WMI_HP_SWITCH_BT 0x02 100#define WMI_HP_SWITCH_WWAN 0x04 101 102#define WMI_HP_SWITCH_ARG_WLAN_OFF 0x100 103#define WMI_HP_SWITCH_ARG_WLAN_ON 0x101 104#define WMI_HP_SWITCH_ARG_BT_OFF 0x200 105#define WMI_HP_SWITCH_ARG_BT_ON 0x202 106#define WMI_HP_SWITCH_ARG_WWAN_OFF 0x400 107#define WMI_HP_SWITCH_ARG_WWAN_ON 0x404 108 109#define WMI_HP_SWITCH_MASK_WLAN_ONAIR __BIT(8) 110#define WMI_HP_SWITCH_MASK_WLAN_ENABLED __BIT(9) 111#define WMI_HP_SWITCH_MASK_WLAN_RADIO __BIT(11) 112#define WMI_HP_SWITCH_MASK_BT_ONAIR __BIT(16) 113#define WMI_HP_SWITCH_MASK_BT_ENABLED __BIT(17) 114#define WMI_HP_SWITCH_MASK_BT_RADIO __BIT(19) 115#define WMI_HP_SWITCH_MASK_WWAN_ONAIR __BIT(24) 116#define WMI_HP_SWITCH_MASK_WWAN_ENABLED __BIT(25) 117#define WMI_HP_SWITCH_MASK_WWAN_RADIO __BIT(27) 118 119#define WMI_HP_GUID_EVENT "95F24279-4D7B-4334-9387-ACCDC67EF61C" 120#define WMI_HP_GUID_METHOD "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" 121 122#define WMI_HP_SENSOR_WLAN 0 123#define WMI_HP_SENSOR_BT 1 124#define WMI_HP_SENSOR_WWAN 2 125#define WMI_HP_SENSOR_COUNT 3 126#define WMI_HP_SENSOR_SIZE 3 * sizeof(envsys_data_t) 127 128struct wmi_hp_softc { 129 device_t sc_dev; 130 device_t sc_parent; 131 struct sysmon_envsys *sc_sme; 132 envsys_data_t *sc_sensor; 133 uint32_t *sc_arg; 134 uint32_t sc_val; 135}; 136 137static int wmi_hp_match(device_t, cfdata_t, void *); 138static void wmi_hp_attach(device_t, device_t, void *); 139static int wmi_hp_detach(device_t, int); 140static bool wmi_hp_suspend(device_t, const pmf_qual_t *); 141static bool wmi_hp_resume(device_t, const pmf_qual_t *); 142static void wmi_hp_notify_handler(ACPI_HANDLE, uint32_t, void *); 143static void wmi_hp_hotkey(void *); 144static bool wmi_hp_method(struct wmi_hp_softc *); 145static bool wmi_hp_method_read(struct wmi_hp_softc *, uint8_t); 146 147#if 0 148static bool wmi_hp_method_write(struct wmi_hp_softc *, uint8_t, uint32_t); 149#endif 150 151static void wmi_hp_sensor_init(struct wmi_hp_softc *); 152static void wmi_hp_sensor_update(void *); 153 154CFATTACH_DECL_NEW(wmihp, sizeof(struct wmi_hp_softc), 155 wmi_hp_match, wmi_hp_attach, wmi_hp_detach, NULL); 156 157static int 158wmi_hp_match(device_t parent, cfdata_t match, void *aux) 159{ 160 return acpi_wmi_guid_match(parent, WMI_HP_GUID_METHOD); 161} 162 163static void 164wmi_hp_attach(device_t parent, device_t self, void *aux) 165{ 166 struct wmi_hp_softc *sc = device_private(self); 167 ACPI_STATUS rv = AE_ERROR; 168 169 sc->sc_dev = self; 170 sc->sc_parent = parent; 171 172 sc->sc_sme = NULL; 173 sc->sc_sensor = NULL; 174 175 sc->sc_arg = kmem_alloc(WMI_HP_METHOD_ARG_SIZE, KM_SLEEP); 176 177 if (sc->sc_arg == NULL) 178 return; 179 180 aprint_naive("\n"); 181 aprint_normal(": HP WMI mappings\n"); 182 183 (void)pmf_device_register(sc->sc_dev, wmi_hp_suspend, wmi_hp_resume); 184 185 if (acpi_wmi_guid_match(parent, WMI_HP_GUID_EVENT) != 0) 186 rv = acpi_wmi_event_register(parent, wmi_hp_notify_handler); 187 188 if (ACPI_FAILURE(rv)) 189 return; 190 191 sc->sc_sensor = kmem_alloc(WMI_HP_SENSOR_SIZE, KM_SLEEP); 192 193 if (sc->sc_sensor == NULL) 194 return; 195 196 wmi_hp_sensor_init(sc); 197} 198 199static int 200wmi_hp_detach(device_t self, int flags) 201{ 202 struct wmi_hp_softc *sc = device_private(self); 203 device_t parent = sc->sc_parent; 204 205 (void)acpi_wmi_event_deregister(parent); 206 207 if (sc->sc_sme != NULL) 208 sysmon_envsys_unregister(sc->sc_sme); 209 210 if (sc->sc_sensor != NULL) 211 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 212 213 if (sc->sc_arg != NULL) 214 kmem_free(sc->sc_arg, WMI_HP_METHOD_ARG_SIZE); 215 216 pmf_device_deregister(self); 217 218 return 0; 219} 220 221static bool 222wmi_hp_suspend(device_t self, const pmf_qual_t *qual) 223{ 224 struct wmi_hp_softc *sc = device_private(self); 225 device_t parent = sc->sc_parent; 226 227 if (sc->sc_sensor != NULL) 228 (void)acpi_wmi_event_deregister(parent); 229 230 return true; 231} 232 233static bool 234wmi_hp_resume(device_t self, const pmf_qual_t *qual) 235{ 236 struct wmi_hp_softc *sc = device_private(self); 237 device_t parent = sc->sc_parent; 238 239 if (sc->sc_sensor != NULL) 240 (void)acpi_wmi_event_register(parent, wmi_hp_notify_handler); 241 242 return true; 243} 244 245static void 246wmi_hp_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) 247{ 248 static const int handler = OSL_NOTIFY_HANDLER; 249 struct wmi_hp_softc *sc; 250 device_t self = aux; 251 ACPI_OBJECT *obj; 252 ACPI_BUFFER buf; 253 ACPI_STATUS rv; 254 uint32_t val; 255 256 buf.Pointer = NULL; 257 258 sc = device_private(self); 259 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); 260 261 if (ACPI_FAILURE(rv)) 262 goto out; 263 264 obj = buf.Pointer; 265 266 if (obj->Type != ACPI_TYPE_BUFFER) { 267 rv = AE_TYPE; 268 goto out; 269 } 270 271 if (obj->Buffer.Length != 8) { 272 rv = AE_LIMIT; 273 goto out; 274 } 275 276 val = *((uint8_t *)obj->Buffer.Pointer); 277 278 if (val == 0x00) { 279 rv = AE_BAD_DATA; 280 goto out; 281 } 282 283 switch (val) { 284 285 case WMI_HP_EVENT_SWITCH: 286 rv = AcpiOsExecute(handler, wmi_hp_sensor_update, self); 287 break; 288 289 case WMI_HP_EVENT_HOTKEY: 290 rv = AcpiOsExecute(handler, wmi_hp_hotkey, self); 291 break; 292 293 case WMI_HP_EVENT_DOCK: /* FALLTHROUGH */ 294 295 default: 296 aprint_debug_dev(sc->sc_dev, "unknown event 0x%02X\n", evt); 297 break; 298 } 299 300out: 301 if (buf.Pointer != NULL) 302 ACPI_FREE(buf.Pointer); 303 304 if (ACPI_FAILURE(rv)) 305 aprint_error_dev(sc->sc_dev, "failed to get data for " 306 "event 0x%02X: %s\n", evt, AcpiFormatException(rv)); 307} 308 309static void 310wmi_hp_hotkey(void *aux) 311{ 312 struct wmi_hp_softc *sc; 313 device_t self = aux; 314 315 sc = device_private(self); 316 317 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HOTKEY) != true) 318 return; 319 320 switch (sc->sc_val) { 321 322 case WMI_HP_HOTKEY_BRIGHTNESS_UP: 323 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 324 break; 325 326 case WMI_HP_HOTKEY_BRIGHTNESS_DOWN: 327 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 328 break; 329 330 default: 331 aprint_debug_dev(self, "unknown hotkey 0x%02x\n", sc->sc_val); 332 break; 333 } 334} 335 336static bool 337wmi_hp_method(struct wmi_hp_softc *sc) 338{ 339 ACPI_BUFFER ibuf, obuf; 340 ACPI_STATUS rv = AE_OK; 341 ACPI_OBJECT *obj; 342 uint32_t cmd, *val; 343 344 cmd = sc->sc_arg[2]; 345 346 KDASSERT(cmd != 0); 347 KDASSERT(sc->sc_arg[0] == WMI_HP_METHOD_ARG_MAGIC); 348 349 obuf.Pointer = NULL; 350 ibuf.Pointer = sc->sc_arg; 351 ibuf.Length = WMI_HP_METHOD_ARG_SIZE; 352 353 rv = acpi_wmi_method(sc->sc_parent, 354 WMI_HP_GUID_METHOD, 0, 3, &ibuf, &obuf); 355 356 if (ACPI_FAILURE(rv)) 357 goto out; 358 359 obj = obuf.Pointer; 360 361 if (obj->Type != ACPI_TYPE_BUFFER) { 362 rv = AE_TYPE; 363 goto out; 364 } 365 366 /* 367 * val[0] unknown 368 * val[1] error code 369 * val[2] return value 370 */ 371 val = (uint32_t *)obj->Buffer.Pointer; 372 373 if (val[1] != 0) { 374 rv = AE_ERROR; 375 goto out; 376 } 377 378 sc->sc_val = val[2]; 379 380out: 381 if (obuf.Pointer != NULL) 382 ACPI_FREE(obuf.Pointer); 383 384 if (ACPI_FAILURE(rv)) { 385 aprint_debug_dev(sc->sc_dev, "failed to evaluate method " 386 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv)); 387 return false; 388 } 389 390 return true; 391} 392 393static bool 394wmi_hp_method_read(struct wmi_hp_softc *sc, uint8_t cmd) 395{ 396 397 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 398 sc->sc_arg[1] = WMI_HP_METHOD_ARG_READ; 399 sc->sc_arg[2] = cmd; 400 sc->sc_arg[3] = 0; 401 sc->sc_arg[4] = 0; 402 403 return wmi_hp_method(sc); 404} 405 406#if 0 407static bool 408wmi_hp_method_write(struct wmi_hp_softc *sc, uint8_t cmd, uint32_t val) 409{ 410 411 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; 412 sc->sc_arg[1] = WMI_HP_METHOD_ARG_WRITE; 413 sc->sc_arg[2] = cmd; 414 sc->sc_arg[3] = WMI_HP_METHOD_ARG_WRITE_SIZE; 415 sc->sc_arg[4] = val; 416 417 return wmi_hp_method(sc); 418} 419#endif 420 421static void 422wmi_hp_sensor_init(struct wmi_hp_softc *sc) 423{ 424 int i, j, sensor[3]; 425 426 const char desc[][ENVSYS_DESCLEN] = { 427 "wireless", "bluetooth", "mobile" 428 }; 429 430 KDASSERT(sc->sc_sme == NULL); 431 KDASSERT(sc->sc_sensor != NULL); 432 433 (void)memset(sc->sc_sensor, 0, WMI_HP_SENSOR_SIZE); 434 435 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) 436 return; 437 438 sc->sc_sme = sysmon_envsys_create(); 439 440 sensor[0] = WMI_HP_SWITCH_WLAN; 441 sensor[1] = WMI_HP_SWITCH_BT; 442 sensor[2] = WMI_HP_SWITCH_WWAN; 443 444 CTASSERT(WMI_HP_SENSOR_WLAN == 0); 445 CTASSERT(WMI_HP_SENSOR_BT == 1); 446 CTASSERT(WMI_HP_SENSOR_WWAN == 2); 447 448 for (i = j = 0; i < 3; i++) { 449 450 if ((sc->sc_val & sensor[i]) == 0) 451 continue; 452 453 (void)strlcpy(sc->sc_sensor[i].desc, desc[i], ENVSYS_DESCLEN); 454 455 sc->sc_sensor[i].state = ENVSYS_SINVALID; 456 sc->sc_sensor[i].units = ENVSYS_INDICATOR; 457 458 if (sysmon_envsys_sensor_attach(sc->sc_sme, 459 &sc->sc_sensor[i]) != 0) 460 goto fail; 461 462 j++; 463 } 464 465 if (j == 0) 466 goto fail; 467 468 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; 469 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 470 471 if (sysmon_envsys_register(sc->sc_sme) != 0) 472 goto fail; 473 474 wmi_hp_sensor_update(sc->sc_dev); 475 476 return; 477 478fail: 479 aprint_debug_dev(sc->sc_dev, "failed to initialize sysmon\n"); 480 481 sysmon_envsys_destroy(sc->sc_sme); 482 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); 483 484 sc->sc_sme = NULL; 485 sc->sc_sensor = NULL; 486} 487 488static void 489wmi_hp_sensor_update(void *aux) 490{ 491 struct wmi_hp_softc *sc; 492 device_t self = aux; 493 494 sc = device_private(self); 495 496 if (sc->sc_sme == NULL || sc->sc_sensor == NULL) 497 return; 498 499 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) { 500 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SINVALID; 501 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SINVALID; 502 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SINVALID; 503 return; 504 } 505 506 if ((sc->sc_val & WMI_HP_SWITCH_WLAN) != 0) { 507 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 0; 508 509 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WLAN_ONAIR) != 0) 510 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 1; 511 512 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SVALID; 513 } 514 515 if ((sc->sc_val & WMI_HP_SWITCH_BT) != 0) { 516 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 0; 517 518 if ((sc->sc_val & WMI_HP_SWITCH_MASK_BT_ONAIR) != 0) 519 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 1; 520 521 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SVALID; 522 } 523 524 if ((sc->sc_val & WMI_HP_SWITCH_WWAN) != 0) { 525 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 0; 526 527 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WWAN_ONAIR) != 0) 528 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 1; 529 530 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SVALID; 531 } 532} 533 534MODULE(MODULE_CLASS_DRIVER, wmihp, "acpiwmi"); 535 536#ifdef _MODULE 537#include "ioconf.c" 538#endif 539 540static int 541wmihp_modcmd(modcmd_t cmd, void *aux) 542{ 543 int rv = 0; 544 545 switch (cmd) { 546 547 case MODULE_CMD_INIT: 548 549#ifdef _MODULE 550 rv = config_init_component(cfdriver_ioconf_wmihp, 551 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); 552#endif 553 break; 554 555 case MODULE_CMD_FINI: 556 557#ifdef _MODULE 558 rv = config_fini_component(cfdriver_ioconf_wmihp, 559 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); 560#endif 561 break; 562 563 default: 564 rv = ENOTTY; 565 } 566 567 return rv; 568} 569