1194701Srpaulo/*- 2194701Srpaulo * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de> 3194701Srpaulo * All rights reserved. 4194701Srpaulo * 5194701Srpaulo * Redistribution and use in source and binary forms, with or without 6194701Srpaulo * modification, are permitted provided that the following conditions 7194701Srpaulo * are met: 8194701Srpaulo * 1. Redistributions of source code must retain the above copyright 9194701Srpaulo * notice, this list of conditions and the following disclaimer. 10194701Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11194701Srpaulo * notice, this list of conditions and the following disclaimer in the 12194701Srpaulo * documentation and/or other materials provided with the distribution. 13194701Srpaulo * 14194701Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15194701Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16194701Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17194701Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18194701Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19194701Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20194701Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21194701Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22194701Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23194701Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24194701Srpaulo * SUCH DAMAGE. 25194701Srpaulo */ 26194701Srpaulo 27194701Srpaulo#include <sys/cdefs.h> 28194701Srpaulo__FBSDID("$FreeBSD: releng/10.3/sys/dev/acpi_support/acpi_hp.c 273736 2014-10-27 14:38:00Z hselasky $"); 29194701Srpaulo 30194701Srpaulo/* 31194701Srpaulo * Driver for extra ACPI-controlled features found on HP laptops 32194701Srpaulo * that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p). 33194701Srpaulo * Allows to control and read status of integrated hardware and read 34194701Srpaulo * BIOS settings through CMI. 35194701Srpaulo * Inspired by the hp-wmi driver, which implements a subset of these 36194701Srpaulo * features (hotkeys) on Linux. 37194701Srpaulo * 38194701Srpaulo * HP CMI whitepaper: 39194701Srpaulo * http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf 40194701Srpaulo * wmi-hp for Linux: 41194701Srpaulo * http://www.kernel.org 42194701Srpaulo * WMI and ACPI: 43194701Srpaulo * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx 44194701Srpaulo */ 45194701Srpaulo 46194701Srpaulo#include "opt_acpi.h" 47194701Srpaulo#include <sys/param.h> 48194701Srpaulo#include <sys/conf.h> 49194701Srpaulo#include <sys/uio.h> 50194701Srpaulo#include <sys/proc.h> 51194701Srpaulo#include <sys/kernel.h> 52194701Srpaulo#include <sys/bus.h> 53194701Srpaulo#include <sys/sbuf.h> 54194701Srpaulo#include <sys/module.h> 55194701Srpaulo#include <sys/sysctl.h> 56194701Srpaulo 57194701Srpaulo#include <contrib/dev/acpica/include/acpi.h> 58194701Srpaulo#include <contrib/dev/acpica/include/accommon.h> 59194701Srpaulo#include <dev/acpica/acpivar.h> 60194701Srpaulo#include "acpi_wmi_if.h" 61194701Srpaulo 62194701Srpaulo#define _COMPONENT ACPI_OEM 63194701SrpauloACPI_MODULE_NAME("HP") 64194701Srpaulo 65194701Srpaulo#define ACPI_HP_WMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" 66194701Srpaulo#define ACPI_HP_WMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" 67194701Srpaulo#define ACPI_HP_WMI_CMI_GUID "2D114B49-2DFB-4130-B8FE-4A3C09E75133" 68194701Srpaulo 69194701Srpaulo#define ACPI_HP_WMI_DISPLAY_COMMAND 0x1 70194701Srpaulo#define ACPI_HP_WMI_HDDTEMP_COMMAND 0x2 71194701Srpaulo#define ACPI_HP_WMI_ALS_COMMAND 0x3 72194701Srpaulo#define ACPI_HP_WMI_DOCK_COMMAND 0x4 73194701Srpaulo#define ACPI_HP_WMI_WIRELESS_COMMAND 0x5 74194701Srpaulo 75194701Srpaulo#define ACPI_HP_METHOD_WLAN_ENABLED 1 76194701Srpaulo#define ACPI_HP_METHOD_WLAN_RADIO 2 77194701Srpaulo#define ACPI_HP_METHOD_WLAN_ON_AIR 3 78194701Srpaulo#define ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON 4 79194701Srpaulo#define ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF 5 80194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_ENABLED 6 81194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_RADIO 7 82194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_ON_AIR 8 83194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON 9 84194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF 10 85194701Srpaulo#define ACPI_HP_METHOD_WWAN_ENABLED 11 86194701Srpaulo#define ACPI_HP_METHOD_WWAN_RADIO 12 87194701Srpaulo#define ACPI_HP_METHOD_WWAN_ON_AIR 13 88194701Srpaulo#define ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON 14 89194701Srpaulo#define ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF 15 90194701Srpaulo#define ACPI_HP_METHOD_ALS 16 91194701Srpaulo#define ACPI_HP_METHOD_DISPLAY 17 92194701Srpaulo#define ACPI_HP_METHOD_HDDTEMP 18 93194701Srpaulo#define ACPI_HP_METHOD_DOCK 19 94194701Srpaulo#define ACPI_HP_METHOD_CMI_DETAIL 20 95195325Srpaulo#define ACPI_HP_METHOD_VERBOSE 21 96194701Srpaulo 97194701Srpaulo#define HP_MASK_WWAN_ON_AIR 0x1000000 98194701Srpaulo#define HP_MASK_BLUETOOTH_ON_AIR 0x10000 99194701Srpaulo#define HP_MASK_WLAN_ON_AIR 0x100 100194701Srpaulo#define HP_MASK_WWAN_RADIO 0x8000000 101194701Srpaulo#define HP_MASK_BLUETOOTH_RADIO 0x80000 102194701Srpaulo#define HP_MASK_WLAN_RADIO 0x800 103194701Srpaulo#define HP_MASK_WWAN_ENABLED 0x2000000 104194701Srpaulo#define HP_MASK_BLUETOOTH_ENABLED 0x20000 105194701Srpaulo#define HP_MASK_WLAN_ENABLED 0x200 106194701Srpaulo 107194701Srpaulo#define ACPI_HP_CMI_DETAIL_PATHS 0x01 108194701Srpaulo#define ACPI_HP_CMI_DETAIL_ENUMS 0x02 109194701Srpaulo#define ACPI_HP_CMI_DETAIL_FLAGS 0x04 110195185Srpaulo#define ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE 0x08 111194701Srpaulo 112194701Srpaulostruct acpi_hp_inst_seq_pair { 113194701Srpaulo UINT32 sequence; /* sequence number as suggested by cmi bios */ 114194701Srpaulo UINT8 instance; /* object instance on guid */ 115194701Srpaulo}; 116194701Srpaulo 117194701Srpaulostruct acpi_hp_softc { 118194701Srpaulo device_t dev; 119194701Srpaulo device_t wmi_dev; 120194701Srpaulo int has_notify; /* notification GUID found */ 121194701Srpaulo int has_cmi; /* CMI GUID found */ 122194701Srpaulo int cmi_detail; /* CMI detail level 123194701Srpaulo (set by sysctl) */ 124195325Srpaulo int verbose; /* add debug output */ 125194701Srpaulo int wlan_enable_if_radio_on; /* set by sysctl */ 126194701Srpaulo int wlan_disable_if_radio_off; /* set by sysctl */ 127194701Srpaulo int bluetooth_enable_if_radio_on; /* set by sysctl */ 128194701Srpaulo int bluetooth_disable_if_radio_off; /* set by sysctl */ 129194701Srpaulo int wwan_enable_if_radio_on; /* set by sysctl */ 130194701Srpaulo int wwan_disable_if_radio_off; /* set by sysctl */ 131194701Srpaulo int was_wlan_on_air; /* last known WLAN 132194701Srpaulo on air status */ 133194701Srpaulo int was_bluetooth_on_air; /* last known BT 134194701Srpaulo on air status */ 135194701Srpaulo int was_wwan_on_air; /* last known WWAN 136194701Srpaulo on air status */ 137194701Srpaulo struct sysctl_ctx_list *sysctl_ctx; 138194701Srpaulo struct sysctl_oid *sysctl_tree; 139194701Srpaulo struct cdev *hpcmi_dev_t; /* hpcmi device handle */ 140194701Srpaulo struct sbuf hpcmi_sbuf; /* /dev/hpcmi output sbuf */ 141194701Srpaulo pid_t hpcmi_open_pid; /* pid operating on 142194701Srpaulo /dev/hpcmi */ 143194701Srpaulo int hpcmi_bufptr; /* current pointer position 144194701Srpaulo in /dev/hpcmi output buffer 145194701Srpaulo */ 146194701Srpaulo int cmi_order_size; /* size of cmi_order list */ 147194701Srpaulo struct acpi_hp_inst_seq_pair cmi_order[128]; /* list of CMI 148194701Srpaulo instances ordered by BIOS suggested sequence */ 149194701Srpaulo}; 150194701Srpaulo 151194701Srpaulostatic struct { 152194701Srpaulo char *name; 153194701Srpaulo int method; 154194701Srpaulo char *description; 155273736Shselasky int flag_rdonly; 156194701Srpaulo} acpi_hp_sysctls[] = { 157194701Srpaulo { 158194701Srpaulo .name = "wlan_enabled", 159194701Srpaulo .method = ACPI_HP_METHOD_WLAN_ENABLED, 160194701Srpaulo .description = "Enable/Disable WLAN (WiFi)", 161194701Srpaulo }, 162194701Srpaulo { 163194701Srpaulo .name = "wlan_radio", 164194701Srpaulo .method = ACPI_HP_METHOD_WLAN_RADIO, 165194701Srpaulo .description = "WLAN radio status", 166273736Shselasky .flag_rdonly = 1 167194701Srpaulo }, 168194701Srpaulo { 169194701Srpaulo .name = "wlan_on_air", 170194701Srpaulo .method = ACPI_HP_METHOD_WLAN_ON_AIR, 171194701Srpaulo .description = "WLAN radio ready to use (enabled and radio)", 172273736Shselasky .flag_rdonly = 1 173194701Srpaulo }, 174194701Srpaulo { 175194701Srpaulo .name = "wlan_enable_if_radio_on", 176194701Srpaulo .method = ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON, 177194701Srpaulo .description = "Enable WLAN if radio is turned on", 178194701Srpaulo }, 179194701Srpaulo { 180194701Srpaulo .name = "wlan_disable_if_radio_off", 181194701Srpaulo .method = ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF, 182194701Srpaulo .description = "Disable WLAN if radio is turned off", 183194701Srpaulo }, 184194701Srpaulo { 185194701Srpaulo .name = "bt_enabled", 186194701Srpaulo .method = ACPI_HP_METHOD_BLUETOOTH_ENABLED, 187194701Srpaulo .description = "Enable/Disable Bluetooth", 188194701Srpaulo }, 189194701Srpaulo { 190194701Srpaulo .name = "bt_radio", 191194701Srpaulo .method = ACPI_HP_METHOD_BLUETOOTH_RADIO, 192194701Srpaulo .description = "Bluetooth radio status", 193273736Shselasky .flag_rdonly = 1 194194701Srpaulo }, 195194701Srpaulo { 196194701Srpaulo .name = "bt_on_air", 197194701Srpaulo .method = ACPI_HP_METHOD_BLUETOOTH_ON_AIR, 198194701Srpaulo .description = "Bluetooth radio ready to use" 199194701Srpaulo " (enabled and radio)", 200273736Shselasky .flag_rdonly = 1 201194701Srpaulo }, 202194701Srpaulo { 203194701Srpaulo .name = "bt_enable_if_radio_on", 204194701Srpaulo .method = ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON, 205194701Srpaulo .description = "Enable bluetooth if radio is turned on", 206194701Srpaulo }, 207194701Srpaulo { 208194701Srpaulo .name = "bt_disable_if_radio_off", 209194701Srpaulo .method = ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF, 210194701Srpaulo .description = "Disable bluetooth if radio is turned off", 211194701Srpaulo }, 212194701Srpaulo { 213194701Srpaulo .name = "wwan_enabled", 214194701Srpaulo .method = ACPI_HP_METHOD_WWAN_ENABLED, 215194701Srpaulo .description = "Enable/Disable WWAN (UMTS)", 216194701Srpaulo }, 217194701Srpaulo { 218194701Srpaulo .name = "wwan_radio", 219194701Srpaulo .method = ACPI_HP_METHOD_WWAN_RADIO, 220194701Srpaulo .description = "WWAN radio status", 221273736Shselasky .flag_rdonly = 1 222194701Srpaulo }, 223194701Srpaulo { 224194701Srpaulo .name = "wwan_on_air", 225194701Srpaulo .method = ACPI_HP_METHOD_WWAN_ON_AIR, 226194701Srpaulo .description = "WWAN radio ready to use (enabled and radio)", 227273736Shselasky .flag_rdonly = 1 228194701Srpaulo }, 229194701Srpaulo { 230194701Srpaulo .name = "wwan_enable_if_radio_on", 231194701Srpaulo .method = ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON, 232194701Srpaulo .description = "Enable WWAN if radio is turned on", 233194701Srpaulo }, 234194701Srpaulo { 235194701Srpaulo .name = "wwan_disable_if_radio_off", 236194701Srpaulo .method = ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF, 237194701Srpaulo .description = "Disable WWAN if radio is turned off", 238194701Srpaulo }, 239194701Srpaulo { 240194701Srpaulo .name = "als_enabled", 241194701Srpaulo .method = ACPI_HP_METHOD_ALS, 242194701Srpaulo .description = "Enable/Disable ALS (Ambient light sensor)", 243194701Srpaulo }, 244194701Srpaulo { 245194701Srpaulo .name = "display", 246194701Srpaulo .method = ACPI_HP_METHOD_DISPLAY, 247194701Srpaulo .description = "Display status", 248273736Shselasky .flag_rdonly = 1 249194701Srpaulo }, 250194701Srpaulo { 251194701Srpaulo .name = "hdd_temperature", 252194701Srpaulo .method = ACPI_HP_METHOD_HDDTEMP, 253194701Srpaulo .description = "HDD temperature", 254273736Shselasky .flag_rdonly = 1 255194701Srpaulo }, 256194701Srpaulo { 257194701Srpaulo .name = "is_docked", 258194701Srpaulo .method = ACPI_HP_METHOD_DOCK, 259194701Srpaulo .description = "Docking station status", 260273736Shselasky .flag_rdonly = 1 261194701Srpaulo }, 262194701Srpaulo { 263194701Srpaulo .name = "cmi_detail", 264194701Srpaulo .method = ACPI_HP_METHOD_CMI_DETAIL, 265194701Srpaulo .description = "Details shown in CMI output " 266194701Srpaulo "(cat /dev/hpcmi)", 267194701Srpaulo }, 268195325Srpaulo { 269195325Srpaulo .name = "verbose", 270195325Srpaulo .method = ACPI_HP_METHOD_VERBOSE, 271195325Srpaulo .description = "Verbosity level", 272195325Srpaulo }, 273194701Srpaulo 274194701Srpaulo { NULL, 0, NULL, 0 } 275194701Srpaulo}; 276194701Srpaulo 277194701SrpauloACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping"); 278194701Srpaulo 279212251Savgstatic void acpi_hp_identify(driver_t *driver, device_t parent); 280194701Srpaulostatic int acpi_hp_probe(device_t dev); 281194701Srpaulostatic int acpi_hp_attach(device_t dev); 282194701Srpaulostatic int acpi_hp_detach(device_t dev); 283194701Srpaulo 284194701Srpaulostatic void acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc* sc); 285194701Srpaulostatic int acpi_hp_sysctl(SYSCTL_HANDLER_ARGS); 286194701Srpaulostatic int acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, 287194701Srpaulo int arg, int oldarg); 288194701Srpaulostatic int acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method); 289194701Srpaulostatic int acpi_hp_exec_wmi_command(device_t wmi_dev, int command, 290194701Srpaulo int is_write, int val); 291194701Srpaulostatic void acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context); 292194701Srpaulostatic int acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, 293194701Srpaulo UINT8 instance, char* outbuf, size_t outsize, 294194701Srpaulo UINT32* sequence, int detail); 295194701Srpaulostatic void acpi_hp_hex_decode(char* buffer); 296194701Srpaulo 297194701Srpaulostatic d_open_t acpi_hp_hpcmi_open; 298194701Srpaulostatic d_close_t acpi_hp_hpcmi_close; 299194701Srpaulostatic d_read_t acpi_hp_hpcmi_read; 300194701Srpaulo 301194701Srpaulo/* handler /dev/hpcmi device */ 302194701Srpaulostatic struct cdevsw hpcmi_cdevsw = { 303194701Srpaulo .d_version = D_VERSION, 304194701Srpaulo .d_open = acpi_hp_hpcmi_open, 305194701Srpaulo .d_close = acpi_hp_hpcmi_close, 306194701Srpaulo .d_read = acpi_hp_hpcmi_read, 307194701Srpaulo .d_name = "hpcmi", 308194701Srpaulo}; 309194701Srpaulo 310194701Srpaulostatic device_method_t acpi_hp_methods[] = { 311212251Savg DEVMETHOD(device_identify, acpi_hp_identify), 312194701Srpaulo DEVMETHOD(device_probe, acpi_hp_probe), 313194701Srpaulo DEVMETHOD(device_attach, acpi_hp_attach), 314194701Srpaulo DEVMETHOD(device_detach, acpi_hp_detach), 315246128Ssbz 316246128Ssbz DEVMETHOD_END 317194701Srpaulo}; 318194701Srpaulo 319194701Srpaulostatic driver_t acpi_hp_driver = { 320194701Srpaulo "acpi_hp", 321194701Srpaulo acpi_hp_methods, 322194701Srpaulo sizeof(struct acpi_hp_softc), 323194701Srpaulo}; 324194701Srpaulo 325194701Srpaulostatic devclass_t acpi_hp_devclass; 326194701Srpaulo 327212457SavgDRIVER_MODULE(acpi_hp, acpi_wmi, acpi_hp_driver, acpi_hp_devclass, 328194701Srpaulo 0, 0); 329194701SrpauloMODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1); 330194701SrpauloMODULE_DEPEND(acpi_hp, acpi, 1, 1, 1); 331194701Srpaulo 332194701Srpaulostatic void 333194701Srpauloacpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc) 334194701Srpaulo{ 335195325Srpaulo int wireless; 336195325Srpaulo int new_wlan_status; 337195325Srpaulo int new_bluetooth_status; 338195325Srpaulo int new_wwan_status; 339194701Srpaulo 340194701Srpaulo wireless = acpi_hp_exec_wmi_command(sc->wmi_dev, 341194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 342194701Srpaulo new_wlan_status = -1; 343194701Srpaulo new_bluetooth_status = -1; 344194701Srpaulo new_wwan_status = -1; 345194701Srpaulo 346195325Srpaulo if (sc->verbose) 347195325Srpaulo device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless); 348194701Srpaulo if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO) 349194701Srpaulo && (wireless & HP_MASK_WLAN_ENABLED)) { 350194701Srpaulo acpi_hp_exec_wmi_command(sc->wmi_dev, 351194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100); 352194701Srpaulo new_wlan_status = 0; 353194701Srpaulo } 354194701Srpaulo else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO) 355194701Srpaulo && !(wireless & HP_MASK_WLAN_ENABLED)) { 356194701Srpaulo acpi_hp_exec_wmi_command(sc->wmi_dev, 357194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101); 358194701Srpaulo new_wlan_status = 1; 359194701Srpaulo } 360194701Srpaulo if (sc->bluetooth_disable_if_radio_off && 361194701Srpaulo !(wireless & HP_MASK_BLUETOOTH_RADIO) && 362194701Srpaulo (wireless & HP_MASK_BLUETOOTH_ENABLED)) { 363194701Srpaulo acpi_hp_exec_wmi_command(sc->wmi_dev, 364194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200); 365194701Srpaulo new_bluetooth_status = 0; 366194701Srpaulo } 367194701Srpaulo else if (sc->bluetooth_enable_if_radio_on && 368194701Srpaulo (wireless & HP_MASK_BLUETOOTH_RADIO) && 369194701Srpaulo !(wireless & HP_MASK_BLUETOOTH_ENABLED)) { 370194701Srpaulo acpi_hp_exec_wmi_command(sc->wmi_dev, 371194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202); 372194701Srpaulo new_bluetooth_status = 1; 373194701Srpaulo } 374194701Srpaulo if (sc->wwan_disable_if_radio_off && 375194701Srpaulo !(wireless & HP_MASK_WWAN_RADIO) && 376194701Srpaulo (wireless & HP_MASK_WWAN_ENABLED)) { 377194701Srpaulo acpi_hp_exec_wmi_command(sc->wmi_dev, 378194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400); 379194701Srpaulo new_wwan_status = 0; 380194701Srpaulo } 381194701Srpaulo else if (sc->wwan_enable_if_radio_on && 382194701Srpaulo (wireless & HP_MASK_WWAN_RADIO) && 383194701Srpaulo !(wireless & HP_MASK_WWAN_ENABLED)) { 384194701Srpaulo acpi_hp_exec_wmi_command(sc->wmi_dev, 385194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404); 386194701Srpaulo new_wwan_status = 1; 387194701Srpaulo } 388194701Srpaulo 389194701Srpaulo if (new_wlan_status == -1) { 390194701Srpaulo new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR); 391194701Srpaulo if ((new_wlan_status?1:0) != sc->was_wlan_on_air) { 392194701Srpaulo sc->was_wlan_on_air = sc->was_wlan_on_air?0:1; 393195325Srpaulo if (sc->verbose) 394195325Srpaulo device_printf(sc->wmi_dev, 395195325Srpaulo "WLAN on air changed to %i " 396195325Srpaulo "(new_wlan_status is %i)\n", 397195325Srpaulo sc->was_wlan_on_air, new_wlan_status); 398212251Savg acpi_UserNotify("HP", ACPI_ROOT_OBJECT, 399194701Srpaulo 0xc0+sc->was_wlan_on_air); 400194701Srpaulo } 401194701Srpaulo } 402194701Srpaulo if (new_bluetooth_status == -1) { 403194701Srpaulo new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR); 404194701Srpaulo if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) { 405194701Srpaulo sc->was_bluetooth_on_air = sc->was_bluetooth_on_air? 406194701Srpaulo 0:1; 407195325Srpaulo if (sc->verbose) 408195325Srpaulo device_printf(sc->wmi_dev, 409195325Srpaulo "BLUETOOTH on air changed" 410195325Srpaulo " to %i (new_bluetooth_status is %i)\n", 411195325Srpaulo sc->was_bluetooth_on_air, 412195325Srpaulo new_bluetooth_status); 413212251Savg acpi_UserNotify("HP", ACPI_ROOT_OBJECT, 414194701Srpaulo 0xd0+sc->was_bluetooth_on_air); 415194701Srpaulo } 416194701Srpaulo } 417194701Srpaulo if (new_wwan_status == -1) { 418194701Srpaulo new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR); 419194701Srpaulo if ((new_wwan_status?1:0) != sc->was_wwan_on_air) { 420194701Srpaulo sc->was_wwan_on_air = sc->was_wwan_on_air?0:1; 421195325Srpaulo if (sc->verbose) 422195325Srpaulo device_printf(sc->wmi_dev, 423195325Srpaulo "WWAN on air changed to %i" 424195325Srpaulo " (new_wwan_status is %i)\n", 425195325Srpaulo sc->was_wwan_on_air, new_wwan_status); 426212251Savg acpi_UserNotify("HP", ACPI_ROOT_OBJECT, 427194701Srpaulo 0xe0+sc->was_wwan_on_air); 428194701Srpaulo } 429194701Srpaulo } 430194701Srpaulo} 431194701Srpaulo 432212251Savgstatic void 433212251Savgacpi_hp_identify(driver_t *driver, device_t parent) 434212251Savg{ 435212251Savg 436212251Savg /* Don't do anything if driver is disabled. */ 437212251Savg if (acpi_disabled("hp")) 438212251Savg return; 439212251Savg 440212251Savg /* Add only a single device instance. */ 441212251Savg if (device_find_child(parent, "acpi_hp", -1) != NULL) 442212251Savg return; 443212251Savg 444212457Savg if (BUS_ADD_CHILD(parent, 0, "acpi_hp", -1) == NULL) 445212251Savg device_printf(parent, "add acpi_hp child failed\n"); 446212251Savg} 447212251Savg 448194701Srpaulostatic int 449194701Srpauloacpi_hp_probe(device_t dev) 450194701Srpaulo{ 451212251Savg 452194701Srpaulo device_set_desc(dev, "HP ACPI-WMI Mapping"); 453194701Srpaulo return (0); 454194701Srpaulo} 455194701Srpaulo 456194701Srpaulostatic int 457194701Srpauloacpi_hp_attach(device_t dev) 458194701Srpaulo{ 459194701Srpaulo struct acpi_hp_softc *sc; 460195325Srpaulo int arg; 461194701Srpaulo 462194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 463194701Srpaulo 464194701Srpaulo sc = device_get_softc(dev); 465194701Srpaulo sc->dev = dev; 466194701Srpaulo sc->has_notify = 0; 467194701Srpaulo sc->has_cmi = 0; 468194701Srpaulo sc->bluetooth_enable_if_radio_on = 0; 469194701Srpaulo sc->bluetooth_disable_if_radio_off = 0; 470194701Srpaulo sc->wlan_enable_if_radio_on = 0; 471194701Srpaulo sc->wlan_disable_if_radio_off = 0; 472194701Srpaulo sc->wlan_enable_if_radio_on = 0; 473194701Srpaulo sc->wlan_disable_if_radio_off = 0; 474194701Srpaulo sc->was_wlan_on_air = 0; 475194701Srpaulo sc->was_bluetooth_on_air = 0; 476194701Srpaulo sc->was_wwan_on_air = 0; 477194701Srpaulo sc->cmi_detail = 0; 478194701Srpaulo sc->cmi_order_size = -1; 479195325Srpaulo sc->verbose = 0; 480194701Srpaulo memset(sc->cmi_order, 0, sizeof(sc->cmi_order)); 481194701Srpaulo 482212457Savg sc->wmi_dev = device_get_parent(dev); 483194701Srpaulo if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, 484194701Srpaulo ACPI_HP_WMI_BIOS_GUID)) { 485194701Srpaulo device_printf(dev, 486194701Srpaulo "WMI device does not provide the HP BIOS GUID\n"); 487194701Srpaulo return (EINVAL); 488194701Srpaulo } 489194701Srpaulo if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, 490194701Srpaulo ACPI_HP_WMI_EVENT_GUID)) { 491194701Srpaulo device_printf(dev, 492194701Srpaulo "HP event GUID detected, installing event handler\n"); 493194701Srpaulo if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev, 494194701Srpaulo ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) { 495194701Srpaulo device_printf(dev, 496194701Srpaulo "Could not install notification handler!\n"); 497194701Srpaulo } 498194701Srpaulo else { 499194701Srpaulo sc->has_notify = 1; 500194701Srpaulo } 501194701Srpaulo } 502195185Srpaulo if ((sc->has_cmi = 503195185Srpaulo ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID) 504195185Srpaulo )) { 505194701Srpaulo device_printf(dev, "HP CMI GUID detected\n"); 506194701Srpaulo } 507194701Srpaulo 508194701Srpaulo if (sc->has_cmi) { 509194701Srpaulo sc->hpcmi_dev_t = make_dev(&hpcmi_cdevsw, 0, UID_ROOT, 510194701Srpaulo GID_WHEEL, 0644, "hpcmi"); 511194701Srpaulo sc->hpcmi_dev_t->si_drv1 = sc; 512194701Srpaulo sc->hpcmi_open_pid = 0; 513194701Srpaulo sc->hpcmi_bufptr = -1; 514194701Srpaulo } 515194701Srpaulo 516194701Srpaulo ACPI_SERIAL_BEGIN(hp); 517194701Srpaulo 518194701Srpaulo sc->sysctl_ctx = device_get_sysctl_ctx(dev); 519194701Srpaulo sc->sysctl_tree = device_get_sysctl_tree(dev); 520194701Srpaulo for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) { 521194701Srpaulo arg = 0; 522194701Srpaulo if ((!sc->has_notify && 523194701Srpaulo (acpi_hp_sysctls[i].method == 524194701Srpaulo ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON || 525194701Srpaulo acpi_hp_sysctls[i].method == 526194701Srpaulo ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF || 527194701Srpaulo acpi_hp_sysctls[i].method == 528194701Srpaulo ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON || 529194701Srpaulo acpi_hp_sysctls[i].method == 530194701Srpaulo ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF || 531194701Srpaulo acpi_hp_sysctls[i].method == 532194701Srpaulo ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON || 533194701Srpaulo acpi_hp_sysctls[i].method == 534194701Srpaulo ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) || 535194701Srpaulo (arg = acpi_hp_sysctl_get(sc, 536194701Srpaulo acpi_hp_sysctls[i].method)) < 0) { 537194701Srpaulo continue; 538194701Srpaulo } 539194701Srpaulo if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) { 540194701Srpaulo sc->was_wlan_on_air = arg; 541194701Srpaulo } 542194701Srpaulo else if (acpi_hp_sysctls[i].method == 543194701Srpaulo ACPI_HP_METHOD_BLUETOOTH_ON_AIR) { 544194701Srpaulo sc->was_bluetooth_on_air = arg; 545194701Srpaulo } 546194701Srpaulo else if (acpi_hp_sysctls[i].method == 547194701Srpaulo ACPI_HP_METHOD_WWAN_ON_AIR) { 548194701Srpaulo sc->was_wwan_on_air = arg; 549194701Srpaulo } 550194701Srpaulo 551273736Shselasky if (acpi_hp_sysctls[i].flag_rdonly != 0) { 552273736Shselasky SYSCTL_ADD_PROC(sc->sysctl_ctx, 553273736Shselasky SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 554273736Shselasky acpi_hp_sysctls[i].name, CTLTYPE_INT | CTLFLAG_RD, 555273736Shselasky sc, i, acpi_hp_sysctl, "I", 556273736Shselasky acpi_hp_sysctls[i].description); 557273736Shselasky } else { 558273736Shselasky SYSCTL_ADD_PROC(sc->sysctl_ctx, 559273736Shselasky SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 560273736Shselasky acpi_hp_sysctls[i].name, CTLTYPE_INT | CTLFLAG_RW, 561273736Shselasky sc, i, acpi_hp_sysctl, "I", 562273736Shselasky acpi_hp_sysctls[i].description); 563273736Shselasky } 564194701Srpaulo } 565194701Srpaulo ACPI_SERIAL_END(hp); 566194701Srpaulo 567194701Srpaulo return (0); 568194701Srpaulo} 569194701Srpaulo 570194701Srpaulostatic int 571194701Srpauloacpi_hp_detach(device_t dev) 572194701Srpaulo{ 573250053Sjhb struct acpi_hp_softc *sc; 574194701Srpaulo 575194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 576250053Sjhb sc = device_get_softc(dev); 577250053Sjhb if (sc->has_cmi && sc->hpcmi_open_pid != 0) 578250053Sjhb return (EBUSY); 579250053Sjhb 580250053Sjhb if (sc->has_notify) 581250053Sjhb ACPI_WMI_REMOVE_EVENT_HANDLER(dev, ACPI_HP_WMI_EVENT_GUID); 582250053Sjhb 583250053Sjhb if (sc->has_cmi) { 584194701Srpaulo if (sc->hpcmi_bufptr != -1) { 585194701Srpaulo sbuf_delete(&sc->hpcmi_sbuf); 586194701Srpaulo sc->hpcmi_bufptr = -1; 587194701Srpaulo } 588194701Srpaulo sc->hpcmi_open_pid = 0; 589194701Srpaulo destroy_dev(sc->hpcmi_dev_t); 590194701Srpaulo } 591194701Srpaulo 592250053Sjhb return (0); 593194701Srpaulo} 594194701Srpaulo 595194701Srpaulostatic int 596194701Srpauloacpi_hp_sysctl(SYSCTL_HANDLER_ARGS) 597194701Srpaulo{ 598195325Srpaulo struct acpi_hp_softc *sc; 599195325Srpaulo int arg; 600195325Srpaulo int oldarg; 601195325Srpaulo int error = 0; 602195325Srpaulo int function; 603195325Srpaulo int method; 604194701Srpaulo 605194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 606194701Srpaulo 607194701Srpaulo sc = (struct acpi_hp_softc *)oidp->oid_arg1; 608194701Srpaulo function = oidp->oid_arg2; 609194701Srpaulo method = acpi_hp_sysctls[function].method; 610194701Srpaulo 611194701Srpaulo ACPI_SERIAL_BEGIN(hp); 612194701Srpaulo arg = acpi_hp_sysctl_get(sc, method); 613194701Srpaulo oldarg = arg; 614194701Srpaulo error = sysctl_handle_int(oidp, &arg, 0, req); 615194701Srpaulo if (!error && req->newptr != NULL) { 616194701Srpaulo error = acpi_hp_sysctl_set(sc, method, arg, oldarg); 617194701Srpaulo } 618194701Srpaulo ACPI_SERIAL_END(hp); 619194701Srpaulo 620194701Srpaulo return (error); 621194701Srpaulo} 622194701Srpaulo 623194701Srpaulostatic int 624194701Srpauloacpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method) 625194701Srpaulo{ 626195325Srpaulo int val = 0; 627194701Srpaulo 628194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 629194701Srpaulo ACPI_SERIAL_ASSERT(hp); 630194701Srpaulo 631194701Srpaulo switch (method) { 632194701Srpaulo case ACPI_HP_METHOD_WLAN_ENABLED: 633194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 634194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 635194701Srpaulo val = ((val & HP_MASK_WLAN_ENABLED) != 0); 636194701Srpaulo break; 637194701Srpaulo case ACPI_HP_METHOD_WLAN_RADIO: 638194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 639194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 640194701Srpaulo val = ((val & HP_MASK_WLAN_RADIO) != 0); 641194701Srpaulo break; 642194701Srpaulo case ACPI_HP_METHOD_WLAN_ON_AIR: 643194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 644194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 645194701Srpaulo val = ((val & HP_MASK_WLAN_ON_AIR) != 0); 646194701Srpaulo break; 647194701Srpaulo case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON: 648194701Srpaulo val = sc->wlan_enable_if_radio_on; 649194701Srpaulo break; 650194701Srpaulo case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF: 651194701Srpaulo val = sc->wlan_disable_if_radio_off; 652194701Srpaulo break; 653194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_ENABLED: 654194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 655194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 656194701Srpaulo val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0); 657194701Srpaulo break; 658194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_RADIO: 659194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 660194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 661194701Srpaulo val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0); 662194701Srpaulo break; 663194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_ON_AIR: 664194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 665194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 666194701Srpaulo val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0); 667194701Srpaulo break; 668194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON: 669194701Srpaulo val = sc->bluetooth_enable_if_radio_on; 670194701Srpaulo break; 671194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF: 672194701Srpaulo val = sc->bluetooth_disable_if_radio_off; 673194701Srpaulo break; 674194701Srpaulo case ACPI_HP_METHOD_WWAN_ENABLED: 675194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 676194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 677194701Srpaulo val = ((val & HP_MASK_WWAN_ENABLED) != 0); 678194701Srpaulo break; 679194701Srpaulo case ACPI_HP_METHOD_WWAN_RADIO: 680194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 681194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 682194701Srpaulo val = ((val & HP_MASK_WWAN_RADIO) != 0); 683194701Srpaulo break; 684194701Srpaulo case ACPI_HP_METHOD_WWAN_ON_AIR: 685194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 686194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); 687194701Srpaulo val = ((val & HP_MASK_WWAN_ON_AIR) != 0); 688194701Srpaulo break; 689194701Srpaulo case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON: 690194701Srpaulo val = sc->wwan_enable_if_radio_on; 691194701Srpaulo break; 692194701Srpaulo case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF: 693194701Srpaulo val = sc->wwan_disable_if_radio_off; 694194701Srpaulo break; 695194701Srpaulo case ACPI_HP_METHOD_ALS: 696194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 697194701Srpaulo ACPI_HP_WMI_ALS_COMMAND, 0, 0); 698194701Srpaulo break; 699194701Srpaulo case ACPI_HP_METHOD_DISPLAY: 700194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 701194701Srpaulo ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0); 702194701Srpaulo break; 703194701Srpaulo case ACPI_HP_METHOD_HDDTEMP: 704194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 705194701Srpaulo ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0); 706194701Srpaulo break; 707194701Srpaulo case ACPI_HP_METHOD_DOCK: 708194701Srpaulo val = acpi_hp_exec_wmi_command(sc->wmi_dev, 709194701Srpaulo ACPI_HP_WMI_DOCK_COMMAND, 0, 0); 710194701Srpaulo break; 711194701Srpaulo case ACPI_HP_METHOD_CMI_DETAIL: 712194701Srpaulo val = sc->cmi_detail; 713194701Srpaulo break; 714195325Srpaulo case ACPI_HP_METHOD_VERBOSE: 715195325Srpaulo val = sc->verbose; 716195325Srpaulo break; 717194701Srpaulo } 718194701Srpaulo 719194701Srpaulo return (val); 720194701Srpaulo} 721194701Srpaulo 722194701Srpaulostatic int 723194701Srpauloacpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg) 724194701Srpaulo{ 725194701Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 726194701Srpaulo ACPI_SERIAL_ASSERT(hp); 727194701Srpaulo 728195325Srpaulo if (method != ACPI_HP_METHOD_CMI_DETAIL && 729195325Srpaulo method != ACPI_HP_METHOD_VERBOSE) 730194701Srpaulo arg = arg?1:0; 731194701Srpaulo 732194701Srpaulo if (arg != oldarg) { 733194701Srpaulo switch (method) { 734194701Srpaulo case ACPI_HP_METHOD_WLAN_ENABLED: 735194701Srpaulo return (acpi_hp_exec_wmi_command(sc->wmi_dev, 736194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 737194701Srpaulo arg?0x101:0x100)); 738194701Srpaulo case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON: 739194701Srpaulo sc->wlan_enable_if_radio_on = arg; 740194701Srpaulo acpi_hp_evaluate_auto_on_off(sc); 741194701Srpaulo break; 742194701Srpaulo case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF: 743194701Srpaulo sc->wlan_disable_if_radio_off = arg; 744194701Srpaulo acpi_hp_evaluate_auto_on_off(sc); 745194701Srpaulo break; 746194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_ENABLED: 747194701Srpaulo return (acpi_hp_exec_wmi_command(sc->wmi_dev, 748194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 749194701Srpaulo arg?0x202:0x200)); 750194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON: 751194701Srpaulo sc->bluetooth_enable_if_radio_on = arg; 752194701Srpaulo acpi_hp_evaluate_auto_on_off(sc); 753194701Srpaulo break; 754194701Srpaulo case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF: 755194701Srpaulo sc->bluetooth_disable_if_radio_off = arg?1:0; 756194701Srpaulo acpi_hp_evaluate_auto_on_off(sc); 757194701Srpaulo break; 758194701Srpaulo case ACPI_HP_METHOD_WWAN_ENABLED: 759194701Srpaulo return (acpi_hp_exec_wmi_command(sc->wmi_dev, 760194701Srpaulo ACPI_HP_WMI_WIRELESS_COMMAND, 1, 761194701Srpaulo arg?0x404:0x400)); 762194701Srpaulo case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON: 763194701Srpaulo sc->wwan_enable_if_radio_on = arg?1:0; 764194701Srpaulo acpi_hp_evaluate_auto_on_off(sc); 765194701Srpaulo break; 766194701Srpaulo case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF: 767194701Srpaulo sc->wwan_disable_if_radio_off = arg?1:0; 768194701Srpaulo acpi_hp_evaluate_auto_on_off(sc); 769194701Srpaulo break; 770194701Srpaulo case ACPI_HP_METHOD_ALS: 771194701Srpaulo return (acpi_hp_exec_wmi_command(sc->wmi_dev, 772194701Srpaulo ACPI_HP_WMI_ALS_COMMAND, 1, 773194701Srpaulo arg?1:0)); 774194701Srpaulo case ACPI_HP_METHOD_CMI_DETAIL: 775194701Srpaulo sc->cmi_detail = arg; 776195185Srpaulo if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) != 777195185Srpaulo (oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) { 778195185Srpaulo sc->cmi_order_size = -1; 779195185Srpaulo } 780194701Srpaulo break; 781195325Srpaulo case ACPI_HP_METHOD_VERBOSE: 782195325Srpaulo sc->verbose = arg; 783195325Srpaulo break; 784194701Srpaulo } 785194701Srpaulo } 786194701Srpaulo 787194701Srpaulo return (0); 788194701Srpaulo} 789194701Srpaulo 790194701Srpaulostatic __inline void 791194701Srpauloacpi_hp_free_buffer(ACPI_BUFFER* buf) { 792194701Srpaulo if (buf && buf->Pointer) { 793194701Srpaulo AcpiOsFree(buf->Pointer); 794194701Srpaulo } 795194701Srpaulo} 796194701Srpaulo 797194701Srpaulostatic void 798194701Srpauloacpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context) 799194701Srpaulo{ 800194701Srpaulo device_t dev = context; 801194701Srpaulo ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 802194701Srpaulo 803194701Srpaulo struct acpi_hp_softc *sc = device_get_softc(dev); 804194701Srpaulo ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL }; 805194701Srpaulo ACPI_OBJECT *obj; 806194701Srpaulo ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response); 807194701Srpaulo obj = (ACPI_OBJECT*) response.Pointer; 808194701Srpaulo if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) { 809194701Srpaulo if (*((UINT8 *) obj->Buffer.Pointer) == 0x5) { 810194701Srpaulo acpi_hp_evaluate_auto_on_off(sc); 811194701Srpaulo } 812194701Srpaulo } 813194701Srpaulo acpi_hp_free_buffer(&response); 814194701Srpaulo} 815194701Srpaulo 816194701Srpaulostatic int 817194701Srpauloacpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val) 818194701Srpaulo{ 819195325Srpaulo UINT32 params[5] = { 0x55434553, 820195325Srpaulo is_write?2:1, 821195325Srpaulo command, 822195325Srpaulo is_write?4:0, 823195325Srpaulo val}; 824195325Srpaulo UINT32* result; 825195325Srpaulo ACPI_OBJECT *obj; 826195325Srpaulo ACPI_BUFFER in = { sizeof(params), ¶ms }; 827195325Srpaulo ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; 828194701Srpaulo int retval; 829194701Srpaulo 830194701Srpaulo if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID, 831194701Srpaulo 0, 0x3, &in, &out))) { 832194701Srpaulo acpi_hp_free_buffer(&out); 833194701Srpaulo return (-EINVAL); 834194701Srpaulo } 835194701Srpaulo obj = out.Pointer; 836194701Srpaulo if (!obj || obj->Type != ACPI_TYPE_BUFFER) { 837194701Srpaulo acpi_hp_free_buffer(&out); 838194701Srpaulo return (-EINVAL); 839194701Srpaulo } 840194701Srpaulo result = (UINT32*) obj->Buffer.Pointer; 841194701Srpaulo retval = result[2]; 842194701Srpaulo if (result[1] > 0) { 843194701Srpaulo retval = result[1]; 844194701Srpaulo } 845194701Srpaulo acpi_hp_free_buffer(&out); 846194701Srpaulo 847194701Srpaulo return (retval); 848194701Srpaulo} 849194701Srpaulo 850194701Srpaulostatic __inline char* 851194701Srpauloacpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) { 852195325Srpaulo int length; 853195325Srpaulo 854194701Srpaulo dst[0] = 0; 855194701Srpaulo if (obj->Type == ACPI_TYPE_STRING) { 856194701Srpaulo length = obj->String.Length+1; 857194701Srpaulo if (length > size) { 858194701Srpaulo length = size - 1; 859194701Srpaulo } 860194701Srpaulo strlcpy(dst, obj->String.Pointer, length); 861194701Srpaulo acpi_hp_hex_decode(dst); 862194701Srpaulo } 863194701Srpaulo 864194701Srpaulo return (dst); 865194701Srpaulo} 866194701Srpaulo 867194701Srpaulo 868194701Srpaulo/* 869194701Srpaulo * Read BIOS Setting block in instance "instance". 870194701Srpaulo * The block returned is ACPI_TYPE_PACKAGE which should contain the following 871194701Srpaulo * elements: 872194701Srpaulo * Index Meaning 873195325Srpaulo * 0 Setting Name [string] 874195325Srpaulo * 1 Value (comma separated, asterisk marks the current value) [string] 875195325Srpaulo * 2 Path within the bios hierarchy [string] 876195325Srpaulo * 3 IsReadOnly [int] 877195325Srpaulo * 4 DisplayInUI [int] 878195325Srpaulo * 5 RequiresPhysicalPresence [int] 879195325Srpaulo * 6 Sequence for ordering within the bios settings (absolute) [int] 880195325Srpaulo * 7 Length of prerequisites array [int] 881195325Srpaulo * 8..8+[7] PrerequisiteN [string] 882195325Srpaulo * 9+[7] Current value (in case of enum) [string] / Array length [int] 883195325Srpaulo * 10+[7] Enum length [int] / Array values 884195325Srpaulo * 11+[7]ff Enum value at index x [string] 885194701Srpaulo */ 886194701Srpaulostatic int 887194701Srpauloacpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance, 888194701Srpaulo char* outbuf, size_t outsize, UINT32* sequence, int detail) 889194701Srpaulo{ 890195325Srpaulo ACPI_OBJECT *obj; 891195325Srpaulo ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; 892195325Srpaulo int i; 893195325Srpaulo int outlen; 894195325Srpaulo int size = 255; 895195325Srpaulo int has_enums = 0; 896195325Srpaulo int valuebase = 0; 897195325Srpaulo char string_buffer[size]; 898195325Srpaulo int enumbase; 899194701Srpaulo 900194701Srpaulo outlen = 0; 901194701Srpaulo outbuf[0] = 0; 902194701Srpaulo if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) { 903194701Srpaulo acpi_hp_free_buffer(&out); 904194701Srpaulo return (-EINVAL); 905194701Srpaulo } 906194701Srpaulo obj = out.Pointer; 907209055Sjkim if (!obj || obj->Type != ACPI_TYPE_PACKAGE) { 908194701Srpaulo acpi_hp_free_buffer(&out); 909194701Srpaulo return (-EINVAL); 910194701Srpaulo } 911194701Srpaulo 912195325Srpaulo if (obj->Package.Count >= 8 && 913195325Srpaulo obj->Package.Elements[7].Type == ACPI_TYPE_INTEGER) { 914195325Srpaulo valuebase = 8 + obj->Package.Elements[7].Integer.Value; 915195325Srpaulo } 916195325Srpaulo 917194701Srpaulo /* check if this matches our expectations based on limited knowledge */ 918195325Srpaulo if (valuebase > 7 && obj->Package.Count > valuebase + 1 && 919194701Srpaulo obj->Package.Elements[0].Type == ACPI_TYPE_STRING && 920194701Srpaulo obj->Package.Elements[1].Type == ACPI_TYPE_STRING && 921194701Srpaulo obj->Package.Elements[2].Type == ACPI_TYPE_STRING && 922194701Srpaulo obj->Package.Elements[3].Type == ACPI_TYPE_INTEGER && 923194701Srpaulo obj->Package.Elements[4].Type == ACPI_TYPE_INTEGER && 924194701Srpaulo obj->Package.Elements[5].Type == ACPI_TYPE_INTEGER && 925194701Srpaulo obj->Package.Elements[6].Type == ACPI_TYPE_INTEGER && 926195325Srpaulo obj->Package.Elements[valuebase].Type == ACPI_TYPE_STRING && 927195325Srpaulo obj->Package.Elements[valuebase+1].Type == ACPI_TYPE_INTEGER && 928195325Srpaulo obj->Package.Count > valuebase + 929195325Srpaulo obj->Package.Elements[valuebase+1].Integer.Value 930195325Srpaulo ) { 931195325Srpaulo enumbase = valuebase + 1; 932194701Srpaulo if (detail & ACPI_HP_CMI_DETAIL_PATHS) { 933194701Srpaulo strlcat(outbuf, acpi_hp_get_string_from_object( 934194701Srpaulo &obj->Package.Elements[2], string_buffer, size), 935194701Srpaulo outsize); 936194701Srpaulo outlen += 48; 937194701Srpaulo while (strlen(outbuf) < outlen) 938194701Srpaulo strlcat(outbuf, " ", outsize); 939194701Srpaulo } 940194701Srpaulo strlcat(outbuf, acpi_hp_get_string_from_object( 941194701Srpaulo &obj->Package.Elements[0], string_buffer, size), 942194701Srpaulo outsize); 943194701Srpaulo outlen += 43; 944194701Srpaulo while (strlen(outbuf) < outlen) 945194701Srpaulo strlcat(outbuf, " ", outsize); 946195325Srpaulo strlcat(outbuf, acpi_hp_get_string_from_object( 947195325Srpaulo &obj->Package.Elements[valuebase], string_buffer, 948195325Srpaulo size), 949195325Srpaulo outsize); 950194701Srpaulo outlen += 21; 951194701Srpaulo while (strlen(outbuf) < outlen) 952194701Srpaulo strlcat(outbuf, " ", outsize); 953194701Srpaulo for (i = 0; i < strlen(outbuf); ++i) 954194701Srpaulo if (outbuf[i] == '\\') 955194701Srpaulo outbuf[i] = '/'; 956194701Srpaulo if (detail & ACPI_HP_CMI_DETAIL_ENUMS) { 957195325Srpaulo for (i = enumbase + 1; i < enumbase + 1 + 958194701Srpaulo obj->Package.Elements[enumbase].Integer.Value; 959194701Srpaulo ++i) { 960194701Srpaulo acpi_hp_get_string_from_object( 961194701Srpaulo &obj->Package.Elements[i], string_buffer, 962194701Srpaulo size); 963194701Srpaulo if (strlen(string_buffer) > 1 || 964194701Srpaulo (strlen(string_buffer) == 1 && 965194701Srpaulo string_buffer[0] != ' ')) { 966194701Srpaulo if (has_enums) 967194701Srpaulo strlcat(outbuf, "/", outsize); 968194701Srpaulo else 969194701Srpaulo strlcat(outbuf, " (", outsize); 970194701Srpaulo strlcat(outbuf, string_buffer, outsize); 971194701Srpaulo has_enums = 1; 972194701Srpaulo } 973194701Srpaulo } 974194701Srpaulo } 975194701Srpaulo if (has_enums) 976194701Srpaulo strlcat(outbuf, ")", outsize); 977194701Srpaulo if (detail & ACPI_HP_CMI_DETAIL_FLAGS) { 978194701Srpaulo strlcat(outbuf, obj->Package.Elements[3].Integer.Value? 979194701Srpaulo " [ReadOnly]":"", outsize); 980194701Srpaulo strlcat(outbuf, obj->Package.Elements[4].Integer.Value? 981194701Srpaulo "":" [NOUI]", outsize); 982194701Srpaulo strlcat(outbuf, obj->Package.Elements[5].Integer.Value? 983194701Srpaulo " [RPP]":"", outsize); 984194701Srpaulo } 985194701Srpaulo *sequence = (UINT32) obj->Package.Elements[6].Integer.Value; 986194701Srpaulo } 987194701Srpaulo acpi_hp_free_buffer(&out); 988194701Srpaulo 989194701Srpaulo return (0); 990194701Srpaulo} 991194701Srpaulo 992194701Srpaulo 993194701Srpaulo 994194701Srpaulo/* 995194701Srpaulo * Convert given two digit hex string (hexin) to an UINT8 referenced 996194701Srpaulo * by byteout. 997194701Srpaulo * Return != 0 if the was a problem (invalid input) 998194701Srpaulo */ 999194701Srpaulostatic __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout) 1000194701Srpaulo{ 1001195325Srpaulo unsigned int hi; 1002195325Srpaulo unsigned int lo; 1003194701Srpaulo 1004194701Srpaulo hi = hexin[0]; 1005194701Srpaulo lo = hexin[1]; 1006194701Srpaulo if ('0' <= hi && hi <= '9') 1007194701Srpaulo hi -= '0'; 1008194701Srpaulo else if ('A' <= hi && hi <= 'F') 1009194701Srpaulo hi -= ('A' - 10); 1010194701Srpaulo else if ('a' <= hi && hi <= 'f') 1011194701Srpaulo hi -= ('a' - 10); 1012194701Srpaulo else 1013194701Srpaulo return (1); 1014194701Srpaulo if ('0' <= lo && lo <= '9') 1015194701Srpaulo lo -= '0'; 1016194701Srpaulo else if ('A' <= lo && lo <= 'F') 1017194701Srpaulo lo -= ('A' - 10); 1018194701Srpaulo else if ('a' <= lo && lo <= 'f') 1019194701Srpaulo lo -= ('a' - 10); 1020194701Srpaulo else 1021194701Srpaulo return (1); 1022194701Srpaulo *byteout = (hi << 4) + lo; 1023194701Srpaulo 1024194701Srpaulo return (0); 1025194701Srpaulo} 1026194701Srpaulo 1027194701Srpaulo 1028194701Srpaulostatic void 1029194701Srpauloacpi_hp_hex_decode(char* buffer) 1030194701Srpaulo{ 1031195325Srpaulo int i; 1032195325Srpaulo int length = strlen(buffer); 1033195325Srpaulo UINT8 *uin; 1034195325Srpaulo UINT8 uout; 1035194701Srpaulo 1036194701Srpaulo if (((int)length/2)*2 == length || length < 10) return; 1037194701Srpaulo 1038194701Srpaulo for (i = 0; i<length; ++i) { 1039194701Srpaulo if (!((i+1)%3)) { 1040194701Srpaulo if (buffer[i] != ' ') 1041194701Srpaulo return; 1042194701Srpaulo } 1043194701Srpaulo else 1044194701Srpaulo if (!((buffer[i] >= '0' && buffer[i] <= '9') || 1045194701Srpaulo (buffer[i] >= 'A' && buffer[i] <= 'F'))) 1046194701Srpaulo return; 1047194701Srpaulo } 1048194701Srpaulo 1049194701Srpaulo for (i = 0; i<length; i += 3) { 1050194701Srpaulo uin = &buffer[i]; 1051194701Srpaulo uout = 0; 1052194701Srpaulo acpi_hp_hex_to_int(uin, &uout); 1053194701Srpaulo buffer[i/3] = (char) uout; 1054194701Srpaulo } 1055194701Srpaulo buffer[(length+1)/3] = 0; 1056194701Srpaulo} 1057194701Srpaulo 1058194701Srpaulo 1059194701Srpaulo/* 1060194701Srpaulo * open hpcmi device 1061194701Srpaulo */ 1062194701Srpaulostatic int 1063194701Srpauloacpi_hp_hpcmi_open(struct cdev* dev, int flags, int mode, struct thread *td) 1064194701Srpaulo{ 1065195325Srpaulo struct acpi_hp_softc *sc; 1066195325Srpaulo int ret; 1067194701Srpaulo 1068194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 1069194701Srpaulo return (EBADF); 1070194701Srpaulo sc = dev->si_drv1; 1071194701Srpaulo 1072194701Srpaulo ACPI_SERIAL_BEGIN(hp); 1073194701Srpaulo if (sc->hpcmi_open_pid != 0) { 1074194701Srpaulo ret = EBUSY; 1075194701Srpaulo } 1076194701Srpaulo else { 1077194701Srpaulo if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND) 1078194701Srpaulo == NULL) { 1079194701Srpaulo ret = ENXIO; 1080194701Srpaulo } else { 1081194701Srpaulo sc->hpcmi_open_pid = td->td_proc->p_pid; 1082194701Srpaulo sc->hpcmi_bufptr = 0; 1083194701Srpaulo ret = 0; 1084194701Srpaulo } 1085194701Srpaulo } 1086194701Srpaulo ACPI_SERIAL_END(hp); 1087194701Srpaulo 1088194701Srpaulo return (ret); 1089194701Srpaulo} 1090194701Srpaulo 1091194701Srpaulo/* 1092194701Srpaulo * close hpcmi device 1093194701Srpaulo */ 1094194701Srpaulostatic int 1095194701Srpauloacpi_hp_hpcmi_close(struct cdev* dev, int flags, int mode, struct thread *td) 1096194701Srpaulo{ 1097195325Srpaulo struct acpi_hp_softc *sc; 1098195325Srpaulo int ret; 1099194701Srpaulo 1100194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 1101194701Srpaulo return (EBADF); 1102194701Srpaulo sc = dev->si_drv1; 1103194701Srpaulo 1104194701Srpaulo ACPI_SERIAL_BEGIN(hp); 1105194701Srpaulo if (sc->hpcmi_open_pid == 0) { 1106194701Srpaulo ret = EBADF; 1107194701Srpaulo } 1108194701Srpaulo else { 1109194701Srpaulo if (sc->hpcmi_bufptr != -1) { 1110194701Srpaulo sbuf_delete(&sc->hpcmi_sbuf); 1111194701Srpaulo sc->hpcmi_bufptr = -1; 1112194701Srpaulo } 1113194701Srpaulo sc->hpcmi_open_pid = 0; 1114194701Srpaulo ret = 0; 1115194701Srpaulo } 1116194701Srpaulo ACPI_SERIAL_END(hp); 1117194701Srpaulo 1118194701Srpaulo return (ret); 1119194701Srpaulo} 1120194701Srpaulo 1121194701Srpaulo/* 1122194701Srpaulo * Read from hpcmi bios information 1123194701Srpaulo */ 1124194701Srpaulostatic int 1125194701Srpauloacpi_hp_hpcmi_read(struct cdev *dev, struct uio *buf, int flag) 1126194701Srpaulo{ 1127195325Srpaulo struct acpi_hp_softc *sc; 1128195325Srpaulo int pos, i, l, ret; 1129195325Srpaulo UINT8 instance; 1130195325Srpaulo UINT8 maxInstance; 1131195325Srpaulo UINT32 sequence; 1132195325Srpaulo int linesize = 1025; 1133195325Srpaulo char line[linesize]; 1134194701Srpaulo 1135194701Srpaulo if (dev == NULL || dev->si_drv1 == NULL) 1136194701Srpaulo return (EBADF); 1137194701Srpaulo sc = dev->si_drv1; 1138194701Srpaulo 1139194701Srpaulo ACPI_SERIAL_BEGIN(hp); 1140194701Srpaulo if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid 1141194701Srpaulo || sc->hpcmi_bufptr == -1) { 1142194701Srpaulo ret = EBADF; 1143194701Srpaulo } 1144194701Srpaulo else { 1145194701Srpaulo if (!sbuf_done(&sc->hpcmi_sbuf)) { 1146194701Srpaulo if (sc->cmi_order_size < 0) { 1147195185Srpaulo maxInstance = sc->has_cmi; 1148195185Srpaulo if (!(sc->cmi_detail & 1149195185Srpaulo ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) && 1150195185Srpaulo maxInstance > 0) { 1151195185Srpaulo maxInstance--; 1152195185Srpaulo } 1153194701Srpaulo sc->cmi_order_size = 0; 1154195185Srpaulo for (instance = 0; instance < maxInstance; 1155194701Srpaulo ++instance) { 1156194701Srpaulo if (acpi_hp_get_cmi_block(sc->wmi_dev, 1157194701Srpaulo ACPI_HP_WMI_CMI_GUID, instance, 1158194701Srpaulo line, linesize, &sequence, 1159194701Srpaulo sc->cmi_detail)) { 1160195185Srpaulo instance = maxInstance; 1161194701Srpaulo } 1162194701Srpaulo else { 1163194701Srpaulo pos = sc->cmi_order_size; 1164194701Srpaulo for (i=0; 1165194701Srpaulo i<sc->cmi_order_size && i<127; 1166194701Srpaulo ++i) { 1167194701Srpaulo if (sc->cmi_order[i].sequence > sequence) { 1168194701Srpaulo pos = i; 1169194701Srpaulo break; 1170194701Srpaulo } 1171194701Srpaulo } 1172194701Srpaulo for (i=sc->cmi_order_size; 1173194701Srpaulo i>pos; 1174194701Srpaulo --i) { 1175194701Srpaulo sc->cmi_order[i].sequence = 1176194701Srpaulo sc->cmi_order[i-1].sequence; 1177194701Srpaulo sc->cmi_order[i].instance = 1178194701Srpaulo sc->cmi_order[i-1].instance; 1179194701Srpaulo } 1180194701Srpaulo sc->cmi_order[pos].sequence = 1181194701Srpaulo sequence; 1182194701Srpaulo sc->cmi_order[pos].instance = 1183194701Srpaulo instance; 1184194701Srpaulo sc->cmi_order_size++; 1185194701Srpaulo } 1186194701Srpaulo } 1187194701Srpaulo } 1188194701Srpaulo for (i=0; i<sc->cmi_order_size; ++i) { 1189194701Srpaulo if (!acpi_hp_get_cmi_block(sc->wmi_dev, 1190194701Srpaulo ACPI_HP_WMI_CMI_GUID, 1191194701Srpaulo sc->cmi_order[i].instance, line, linesize, 1192194701Srpaulo &sequence, sc->cmi_detail)) { 1193194701Srpaulo sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line); 1194194701Srpaulo } 1195194701Srpaulo } 1196194701Srpaulo sbuf_finish(&sc->hpcmi_sbuf); 1197194701Srpaulo } 1198194701Srpaulo if (sbuf_len(&sc->hpcmi_sbuf) <= 0) { 1199194701Srpaulo sbuf_delete(&sc->hpcmi_sbuf); 1200194701Srpaulo sc->hpcmi_bufptr = -1; 1201194701Srpaulo sc->hpcmi_open_pid = 0; 1202194701Srpaulo ret = ENOMEM; 1203194701Srpaulo } else { 1204194701Srpaulo l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) - 1205194701Srpaulo sc->hpcmi_bufptr); 1206194701Srpaulo ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) + 1207194701Srpaulo sc->hpcmi_bufptr, l, buf) : 0; 1208194701Srpaulo sc->hpcmi_bufptr += l; 1209194701Srpaulo } 1210194701Srpaulo } 1211194701Srpaulo ACPI_SERIAL_END(hp); 1212194701Srpaulo 1213194701Srpaulo return (ret); 1214194701Srpaulo} 1215