1288 lines
36 KiB
C
1288 lines
36 KiB
C
/*-
|
|
* Copyright (c) 2009 Michael Gmelin <freebsd@grem.de>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
/*
|
|
* Driver for extra ACPI-controlled features found on HP laptops
|
|
* that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p).
|
|
* Allows to control and read status of integrated hardware and read
|
|
* BIOS settings through CMI.
|
|
* Inspired by the hp-wmi driver, which implements a subset of these
|
|
* features (hotkeys) on Linux.
|
|
*
|
|
* HP CMI whitepaper:
|
|
* http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf
|
|
* wmi-hp for Linux:
|
|
* http://www.kernel.org
|
|
* WMI and ACPI:
|
|
* http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
|
|
*/
|
|
|
|
#include "opt_acpi.h"
|
|
#include <sys/param.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/sbuf.h>
|
|
#include <sys/module.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <contrib/dev/acpica/include/acpi.h>
|
|
#include <contrib/dev/acpica/include/accommon.h>
|
|
#include <dev/acpica/acpivar.h>
|
|
#include "acpi_wmi_if.h"
|
|
|
|
#define _COMPONENT ACPI_OEM
|
|
ACPI_MODULE_NAME("HP")
|
|
|
|
#define ACPI_HP_WMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
|
|
#define ACPI_HP_WMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
|
|
#define ACPI_HP_WMI_CMI_GUID "2D114B49-2DFB-4130-B8FE-4A3C09E75133"
|
|
|
|
#define ACPI_HP_WMI_DISPLAY_COMMAND 0x1
|
|
#define ACPI_HP_WMI_HDDTEMP_COMMAND 0x2
|
|
#define ACPI_HP_WMI_ALS_COMMAND 0x3
|
|
#define ACPI_HP_WMI_DOCK_COMMAND 0x4
|
|
#define ACPI_HP_WMI_WIRELESS_COMMAND 0x5
|
|
#define ACPI_HP_WMI_BIOS_COMMAND 0x9
|
|
#define ACPI_HP_WMI_FEATURE_COMMAND 0xb
|
|
#define ACPI_HP_WMI_HOTKEY_COMMAND 0xc
|
|
#define ACPI_HP_WMI_FEATURE2_COMMAND 0xd
|
|
#define ACPI_HP_WMI_WIRELESS2_COMMAND 0x1b
|
|
#define ACPI_HP_WMI_POSTCODEERROR_COMMAND 0x2a
|
|
|
|
#define ACPI_HP_METHOD_WLAN_ENABLED 1
|
|
#define ACPI_HP_METHOD_WLAN_RADIO 2
|
|
#define ACPI_HP_METHOD_WLAN_ON_AIR 3
|
|
#define ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON 4
|
|
#define ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF 5
|
|
#define ACPI_HP_METHOD_BLUETOOTH_ENABLED 6
|
|
#define ACPI_HP_METHOD_BLUETOOTH_RADIO 7
|
|
#define ACPI_HP_METHOD_BLUETOOTH_ON_AIR 8
|
|
#define ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON 9
|
|
#define ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF 10
|
|
#define ACPI_HP_METHOD_WWAN_ENABLED 11
|
|
#define ACPI_HP_METHOD_WWAN_RADIO 12
|
|
#define ACPI_HP_METHOD_WWAN_ON_AIR 13
|
|
#define ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON 14
|
|
#define ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF 15
|
|
#define ACPI_HP_METHOD_ALS 16
|
|
#define ACPI_HP_METHOD_DISPLAY 17
|
|
#define ACPI_HP_METHOD_HDDTEMP 18
|
|
#define ACPI_HP_METHOD_DOCK 19
|
|
#define ACPI_HP_METHOD_CMI_DETAIL 20
|
|
#define ACPI_HP_METHOD_VERBOSE 21
|
|
|
|
#define HP_MASK_WWAN_ON_AIR 0x1000000
|
|
#define HP_MASK_BLUETOOTH_ON_AIR 0x10000
|
|
#define HP_MASK_WLAN_ON_AIR 0x100
|
|
#define HP_MASK_WWAN_RADIO 0x8000000
|
|
#define HP_MASK_BLUETOOTH_RADIO 0x80000
|
|
#define HP_MASK_WLAN_RADIO 0x800
|
|
#define HP_MASK_WWAN_ENABLED 0x2000000
|
|
#define HP_MASK_BLUETOOTH_ENABLED 0x20000
|
|
#define HP_MASK_WLAN_ENABLED 0x200
|
|
|
|
#define ACPI_HP_EVENT_DOCK 0x01
|
|
#define ACPI_HP_EVENT_PARK_HDD 0x02
|
|
#define ACPI_HP_EVENT_SMART_ADAPTER 0x03
|
|
#define ACPI_HP_EVENT_BEZEL_BUTTON 0x04
|
|
#define ACPI_HP_EVENT_WIRELESS 0x05
|
|
#define ACPI_HP_EVENT_CPU_BATTERY_THROTTLE 0x06
|
|
#define ACPI_HP_EVENT_LOCK_SWITCH 0x07
|
|
#define ACPI_HP_EVENT_LID_SWITCH 0x08
|
|
#define ACPI_HP_EVENT_SCREEN_ROTATION 0x09
|
|
#define ACPI_HP_EVENT_COOLSENSE_SYSTEM_MOBILE 0x0A
|
|
#define ACPI_HP_EVENT_COOLSENSE_SYSTEM_HOT 0x0B
|
|
#define ACPI_HP_EVENT_PROXIMITY_SENSOR 0x0C
|
|
#define ACPI_HP_EVENT_BACKLIT_KB_BRIGHTNESS 0x0D
|
|
#define ACPI_HP_EVENT_PEAKSHIFT_PERIOD 0x0F
|
|
#define ACPI_HP_EVENT_BATTERY_CHARGE_PERIOD 0x10
|
|
|
|
#define ACPI_HP_CMI_DETAIL_PATHS 0x01
|
|
#define ACPI_HP_CMI_DETAIL_ENUMS 0x02
|
|
#define ACPI_HP_CMI_DETAIL_FLAGS 0x04
|
|
#define ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE 0x08
|
|
|
|
#define ACPI_HP_WMI_RET_WRONG_SIGNATURE 0x02
|
|
#define ACPI_HP_WMI_RET_UNKNOWN_COMMAND 0x03
|
|
#define ACPI_HP_WMI_RET_UNKNOWN_CMDTYPE 0x04
|
|
#define ACPI_HP_WMI_RET_INVALID_PARAMETERS 0x05
|
|
|
|
struct acpi_hp_inst_seq_pair {
|
|
UINT32 sequence; /* sequence number as suggested by cmi bios */
|
|
UINT8 instance; /* object instance on guid */
|
|
};
|
|
|
|
struct acpi_hp_softc {
|
|
device_t dev;
|
|
device_t wmi_dev;
|
|
int has_notify; /* notification GUID found */
|
|
int has_cmi; /* CMI GUID found */
|
|
int has_wireless; /* Wireless command found */
|
|
int cmi_detail; /* CMI detail level
|
|
(set by sysctl) */
|
|
int verbose; /* add debug output */
|
|
int wlan_enable_if_radio_on; /* set by sysctl */
|
|
int wlan_disable_if_radio_off; /* set by sysctl */
|
|
int bluetooth_enable_if_radio_on; /* set by sysctl */
|
|
int bluetooth_disable_if_radio_off; /* set by sysctl */
|
|
int wwan_enable_if_radio_on; /* set by sysctl */
|
|
int wwan_disable_if_radio_off; /* set by sysctl */
|
|
int was_wlan_on_air; /* last known WLAN
|
|
on air status */
|
|
int was_bluetooth_on_air; /* last known BT
|
|
on air status */
|
|
int was_wwan_on_air; /* last known WWAN
|
|
on air status */
|
|
struct sysctl_ctx_list *sysctl_ctx;
|
|
struct sysctl_oid *sysctl_tree;
|
|
struct cdev *hpcmi_dev_t; /* hpcmi device handle */
|
|
struct sbuf hpcmi_sbuf; /* /dev/hpcmi output sbuf */
|
|
pid_t hpcmi_open_pid; /* pid operating on
|
|
/dev/hpcmi */
|
|
int hpcmi_bufptr; /* current pointer position
|
|
in /dev/hpcmi output buffer
|
|
*/
|
|
int cmi_order_size; /* size of cmi_order list */
|
|
struct acpi_hp_inst_seq_pair cmi_order[128]; /* list of CMI
|
|
instances ordered by BIOS suggested sequence */
|
|
};
|
|
|
|
static struct {
|
|
char *name;
|
|
int method;
|
|
char *description;
|
|
int flag_rdonly;
|
|
} acpi_hp_sysctls[] = {
|
|
{
|
|
.name = "wlan_enabled",
|
|
.method = ACPI_HP_METHOD_WLAN_ENABLED,
|
|
.description = "Enable/Disable WLAN (WiFi)",
|
|
},
|
|
{
|
|
.name = "wlan_radio",
|
|
.method = ACPI_HP_METHOD_WLAN_RADIO,
|
|
.description = "WLAN radio status",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "wlan_on_air",
|
|
.method = ACPI_HP_METHOD_WLAN_ON_AIR,
|
|
.description = "WLAN radio ready to use (enabled and radio)",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "wlan_enable_if_radio_on",
|
|
.method = ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON,
|
|
.description = "Enable WLAN if radio is turned on",
|
|
},
|
|
{
|
|
.name = "wlan_disable_if_radio_off",
|
|
.method = ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF,
|
|
.description = "Disable WLAN if radio is turned off",
|
|
},
|
|
{
|
|
.name = "bt_enabled",
|
|
.method = ACPI_HP_METHOD_BLUETOOTH_ENABLED,
|
|
.description = "Enable/Disable Bluetooth",
|
|
},
|
|
{
|
|
.name = "bt_radio",
|
|
.method = ACPI_HP_METHOD_BLUETOOTH_RADIO,
|
|
.description = "Bluetooth radio status",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "bt_on_air",
|
|
.method = ACPI_HP_METHOD_BLUETOOTH_ON_AIR,
|
|
.description = "Bluetooth radio ready to use"
|
|
" (enabled and radio)",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "bt_enable_if_radio_on",
|
|
.method = ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON,
|
|
.description = "Enable bluetooth if radio is turned on",
|
|
},
|
|
{
|
|
.name = "bt_disable_if_radio_off",
|
|
.method = ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF,
|
|
.description = "Disable bluetooth if radio is turned off",
|
|
},
|
|
{
|
|
.name = "wwan_enabled",
|
|
.method = ACPI_HP_METHOD_WWAN_ENABLED,
|
|
.description = "Enable/Disable WWAN (UMTS)",
|
|
},
|
|
{
|
|
.name = "wwan_radio",
|
|
.method = ACPI_HP_METHOD_WWAN_RADIO,
|
|
.description = "WWAN radio status",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "wwan_on_air",
|
|
.method = ACPI_HP_METHOD_WWAN_ON_AIR,
|
|
.description = "WWAN radio ready to use (enabled and radio)",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "wwan_enable_if_radio_on",
|
|
.method = ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON,
|
|
.description = "Enable WWAN if radio is turned on",
|
|
},
|
|
{
|
|
.name = "wwan_disable_if_radio_off",
|
|
.method = ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF,
|
|
.description = "Disable WWAN if radio is turned off",
|
|
},
|
|
{
|
|
.name = "als_enabled",
|
|
.method = ACPI_HP_METHOD_ALS,
|
|
.description = "Enable/Disable ALS (Ambient light sensor)",
|
|
},
|
|
{
|
|
.name = "display",
|
|
.method = ACPI_HP_METHOD_DISPLAY,
|
|
.description = "Display status",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "hdd_temperature",
|
|
.method = ACPI_HP_METHOD_HDDTEMP,
|
|
.description = "HDD temperature",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "is_docked",
|
|
.method = ACPI_HP_METHOD_DOCK,
|
|
.description = "Docking station status",
|
|
.flag_rdonly = 1
|
|
},
|
|
{
|
|
.name = "cmi_detail",
|
|
.method = ACPI_HP_METHOD_CMI_DETAIL,
|
|
.description = "Details shown in CMI output "
|
|
"(cat /dev/hpcmi)",
|
|
},
|
|
{
|
|
.name = "verbose",
|
|
.method = ACPI_HP_METHOD_VERBOSE,
|
|
.description = "Verbosity level",
|
|
},
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
ACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping");
|
|
|
|
static void acpi_hp_identify(driver_t *driver, device_t parent);
|
|
static int acpi_hp_probe(device_t dev);
|
|
static int acpi_hp_attach(device_t dev);
|
|
static int acpi_hp_detach(device_t dev);
|
|
|
|
static void acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc* sc);
|
|
static int acpi_hp_sysctl(SYSCTL_HANDLER_ARGS);
|
|
static int acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method,
|
|
int arg, int oldarg);
|
|
static int acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method);
|
|
static int acpi_hp_exec_wmi_command(device_t wmi_dev, int command,
|
|
int is_write, int val, int *retval);
|
|
static void acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context);
|
|
static int acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid,
|
|
UINT8 instance, char* outbuf, size_t outsize,
|
|
UINT32* sequence, int detail);
|
|
static void acpi_hp_hex_decode(char* buffer);
|
|
|
|
static d_open_t acpi_hp_hpcmi_open;
|
|
static d_close_t acpi_hp_hpcmi_close;
|
|
static d_read_t acpi_hp_hpcmi_read;
|
|
|
|
/* handler /dev/hpcmi device */
|
|
static struct cdevsw hpcmi_cdevsw = {
|
|
.d_version = D_VERSION,
|
|
.d_open = acpi_hp_hpcmi_open,
|
|
.d_close = acpi_hp_hpcmi_close,
|
|
.d_read = acpi_hp_hpcmi_read,
|
|
.d_name = "hpcmi",
|
|
};
|
|
|
|
static device_method_t acpi_hp_methods[] = {
|
|
DEVMETHOD(device_identify, acpi_hp_identify),
|
|
DEVMETHOD(device_probe, acpi_hp_probe),
|
|
DEVMETHOD(device_attach, acpi_hp_attach),
|
|
DEVMETHOD(device_detach, acpi_hp_detach),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t acpi_hp_driver = {
|
|
"acpi_hp",
|
|
acpi_hp_methods,
|
|
sizeof(struct acpi_hp_softc),
|
|
};
|
|
|
|
DRIVER_MODULE(acpi_hp, acpi_wmi, acpi_hp_driver, 0, 0);
|
|
MODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1);
|
|
MODULE_DEPEND(acpi_hp, acpi, 1, 1, 1);
|
|
|
|
static void
|
|
acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc)
|
|
{
|
|
int res;
|
|
int wireless;
|
|
int new_wlan_status;
|
|
int new_bluetooth_status;
|
|
int new_wwan_status;
|
|
|
|
res = acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &wireless);
|
|
if (res != 0) {
|
|
device_printf(sc->wmi_dev, "Wireless command error %x\n", res);
|
|
return;
|
|
}
|
|
new_wlan_status = -1;
|
|
new_bluetooth_status = -1;
|
|
new_wwan_status = -1;
|
|
|
|
if (sc->verbose)
|
|
device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless);
|
|
if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO)
|
|
&& (wireless & HP_MASK_WLAN_ENABLED)) {
|
|
acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100, NULL);
|
|
new_wlan_status = 0;
|
|
}
|
|
else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO)
|
|
&& !(wireless & HP_MASK_WLAN_ENABLED)) {
|
|
acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101, NULL);
|
|
new_wlan_status = 1;
|
|
}
|
|
if (sc->bluetooth_disable_if_radio_off &&
|
|
!(wireless & HP_MASK_BLUETOOTH_RADIO) &&
|
|
(wireless & HP_MASK_BLUETOOTH_ENABLED)) {
|
|
acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200, NULL);
|
|
new_bluetooth_status = 0;
|
|
}
|
|
else if (sc->bluetooth_enable_if_radio_on &&
|
|
(wireless & HP_MASK_BLUETOOTH_RADIO) &&
|
|
!(wireless & HP_MASK_BLUETOOTH_ENABLED)) {
|
|
acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202, NULL);
|
|
new_bluetooth_status = 1;
|
|
}
|
|
if (sc->wwan_disable_if_radio_off &&
|
|
!(wireless & HP_MASK_WWAN_RADIO) &&
|
|
(wireless & HP_MASK_WWAN_ENABLED)) {
|
|
acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400, NULL);
|
|
new_wwan_status = 0;
|
|
}
|
|
else if (sc->wwan_enable_if_radio_on &&
|
|
(wireless & HP_MASK_WWAN_RADIO) &&
|
|
!(wireless & HP_MASK_WWAN_ENABLED)) {
|
|
acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404, NULL);
|
|
new_wwan_status = 1;
|
|
}
|
|
|
|
if (new_wlan_status == -1) {
|
|
new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR);
|
|
if ((new_wlan_status?1:0) != sc->was_wlan_on_air) {
|
|
sc->was_wlan_on_air = sc->was_wlan_on_air?0:1;
|
|
if (sc->verbose)
|
|
device_printf(sc->wmi_dev,
|
|
"WLAN on air changed to %i "
|
|
"(new_wlan_status is %i)\n",
|
|
sc->was_wlan_on_air, new_wlan_status);
|
|
acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
|
|
0xc0+sc->was_wlan_on_air);
|
|
}
|
|
}
|
|
if (new_bluetooth_status == -1) {
|
|
new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR);
|
|
if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) {
|
|
sc->was_bluetooth_on_air = sc->was_bluetooth_on_air?
|
|
0:1;
|
|
if (sc->verbose)
|
|
device_printf(sc->wmi_dev,
|
|
"BLUETOOTH on air changed"
|
|
" to %i (new_bluetooth_status is %i)\n",
|
|
sc->was_bluetooth_on_air,
|
|
new_bluetooth_status);
|
|
acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
|
|
0xd0+sc->was_bluetooth_on_air);
|
|
}
|
|
}
|
|
if (new_wwan_status == -1) {
|
|
new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR);
|
|
if ((new_wwan_status?1:0) != sc->was_wwan_on_air) {
|
|
sc->was_wwan_on_air = sc->was_wwan_on_air?0:1;
|
|
if (sc->verbose)
|
|
device_printf(sc->wmi_dev,
|
|
"WWAN on air changed to %i"
|
|
" (new_wwan_status is %i)\n",
|
|
sc->was_wwan_on_air, new_wwan_status);
|
|
acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
|
|
0xe0+sc->was_wwan_on_air);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
acpi_hp_identify(driver_t *driver, device_t parent)
|
|
{
|
|
|
|
/* Don't do anything if driver is disabled. */
|
|
if (acpi_disabled("hp"))
|
|
return;
|
|
|
|
/* Add only a single device instance. */
|
|
if (device_find_child(parent, "acpi_hp", -1) != NULL)
|
|
return;
|
|
|
|
/* Check BIOS GUID to see whether system is compatible. */
|
|
if (!ACPI_WMI_PROVIDES_GUID_STRING(parent,
|
|
ACPI_HP_WMI_BIOS_GUID))
|
|
return;
|
|
|
|
if (BUS_ADD_CHILD(parent, 0, "acpi_hp", -1) == NULL)
|
|
device_printf(parent, "add acpi_hp child failed\n");
|
|
}
|
|
|
|
static int
|
|
acpi_hp_probe(device_t dev)
|
|
{
|
|
|
|
device_set_desc(dev, "HP ACPI-WMI Mapping");
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
acpi_hp_attach(device_t dev)
|
|
{
|
|
struct acpi_hp_softc *sc;
|
|
int arg;
|
|
|
|
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
|
|
|
|
sc = device_get_softc(dev);
|
|
sc->dev = dev;
|
|
sc->has_notify = 0;
|
|
sc->has_cmi = 0;
|
|
sc->bluetooth_enable_if_radio_on = 0;
|
|
sc->bluetooth_disable_if_radio_off = 0;
|
|
sc->wlan_enable_if_radio_on = 0;
|
|
sc->wlan_disable_if_radio_off = 0;
|
|
sc->wlan_enable_if_radio_on = 0;
|
|
sc->wlan_disable_if_radio_off = 0;
|
|
sc->was_wlan_on_air = 0;
|
|
sc->was_bluetooth_on_air = 0;
|
|
sc->was_wwan_on_air = 0;
|
|
sc->cmi_detail = 0;
|
|
sc->cmi_order_size = -1;
|
|
sc->verbose = bootverbose;
|
|
memset(sc->cmi_order, 0, sizeof(sc->cmi_order));
|
|
|
|
sc->wmi_dev = device_get_parent(dev);
|
|
if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
|
|
ACPI_HP_WMI_BIOS_GUID)) {
|
|
device_printf(dev,
|
|
"WMI device does not provide the HP BIOS GUID\n");
|
|
return (EINVAL);
|
|
}
|
|
if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
|
|
ACPI_HP_WMI_EVENT_GUID)) {
|
|
device_printf(dev,
|
|
"HP event GUID detected, installing event handler\n");
|
|
if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
|
|
ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) {
|
|
device_printf(dev,
|
|
"Could not install notification handler!\n");
|
|
}
|
|
else {
|
|
sc->has_notify = 1;
|
|
}
|
|
}
|
|
if ((sc->has_cmi =
|
|
ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID)
|
|
)) {
|
|
device_printf(dev, "HP CMI GUID detected\n");
|
|
}
|
|
|
|
if (sc->has_cmi) {
|
|
sc->hpcmi_dev_t = make_dev(&hpcmi_cdevsw, 0, UID_ROOT,
|
|
GID_WHEEL, 0644, "hpcmi");
|
|
sc->hpcmi_dev_t->si_drv1 = sc;
|
|
sc->hpcmi_open_pid = 0;
|
|
sc->hpcmi_bufptr = -1;
|
|
}
|
|
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, NULL) == 0)
|
|
sc->has_wireless = 1;
|
|
|
|
ACPI_SERIAL_BEGIN(hp);
|
|
|
|
sc->sysctl_ctx = device_get_sysctl_ctx(dev);
|
|
sc->sysctl_tree = device_get_sysctl_tree(dev);
|
|
for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) {
|
|
arg = 0;
|
|
if (((!sc->has_notify || !sc->has_wireless) &&
|
|
(acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON ||
|
|
acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF ||
|
|
acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON ||
|
|
acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF ||
|
|
acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON ||
|
|
acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) ||
|
|
(arg = acpi_hp_sysctl_get(sc,
|
|
acpi_hp_sysctls[i].method)) < 0) {
|
|
continue;
|
|
}
|
|
if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) {
|
|
sc->was_wlan_on_air = arg;
|
|
}
|
|
else if (acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_BLUETOOTH_ON_AIR) {
|
|
sc->was_bluetooth_on_air = arg;
|
|
}
|
|
else if (acpi_hp_sysctls[i].method ==
|
|
ACPI_HP_METHOD_WWAN_ON_AIR) {
|
|
sc->was_wwan_on_air = arg;
|
|
}
|
|
|
|
if (acpi_hp_sysctls[i].flag_rdonly != 0) {
|
|
SYSCTL_ADD_PROC(sc->sysctl_ctx,
|
|
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
|
|
acpi_hp_sysctls[i].name,
|
|
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
|
sc, i, acpi_hp_sysctl, "I",
|
|
acpi_hp_sysctls[i].description);
|
|
} else {
|
|
SYSCTL_ADD_PROC(sc->sysctl_ctx,
|
|
SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
|
|
acpi_hp_sysctls[i].name,
|
|
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
|
|
sc, i, acpi_hp_sysctl, "I",
|
|
acpi_hp_sysctls[i].description);
|
|
}
|
|
}
|
|
ACPI_SERIAL_END(hp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
acpi_hp_detach(device_t dev)
|
|
{
|
|
struct acpi_hp_softc *sc;
|
|
|
|
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
|
|
sc = device_get_softc(dev);
|
|
if (sc->has_cmi && sc->hpcmi_open_pid != 0)
|
|
return (EBUSY);
|
|
|
|
if (sc->has_notify) {
|
|
ACPI_WMI_REMOVE_EVENT_HANDLER(sc->wmi_dev,
|
|
ACPI_HP_WMI_EVENT_GUID);
|
|
}
|
|
|
|
if (sc->has_cmi) {
|
|
if (sc->hpcmi_bufptr != -1) {
|
|
sbuf_delete(&sc->hpcmi_sbuf);
|
|
sc->hpcmi_bufptr = -1;
|
|
}
|
|
sc->hpcmi_open_pid = 0;
|
|
destroy_dev(sc->hpcmi_dev_t);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
acpi_hp_sysctl(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct acpi_hp_softc *sc;
|
|
int arg;
|
|
int oldarg;
|
|
int error = 0;
|
|
int function;
|
|
int method;
|
|
|
|
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
|
|
|
|
sc = (struct acpi_hp_softc *)oidp->oid_arg1;
|
|
function = oidp->oid_arg2;
|
|
method = acpi_hp_sysctls[function].method;
|
|
|
|
ACPI_SERIAL_BEGIN(hp);
|
|
arg = acpi_hp_sysctl_get(sc, method);
|
|
oldarg = arg;
|
|
error = sysctl_handle_int(oidp, &arg, 0, req);
|
|
if (!error && req->newptr != NULL) {
|
|
error = acpi_hp_sysctl_set(sc, method, arg, oldarg);
|
|
}
|
|
ACPI_SERIAL_END(hp);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method)
|
|
{
|
|
int val = 0;
|
|
|
|
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
|
|
ACPI_SERIAL_ASSERT(hp);
|
|
|
|
switch (method) {
|
|
case ACPI_HP_METHOD_WLAN_ENABLED:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_WLAN_ENABLED) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_WLAN_RADIO:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_WLAN_RADIO) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_WLAN_ON_AIR:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_WLAN_ON_AIR) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
|
|
val = sc->wlan_enable_if_radio_on;
|
|
break;
|
|
case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
|
|
val = sc->wlan_disable_if_radio_off;
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_RADIO:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_ON_AIR:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
|
|
val = sc->bluetooth_enable_if_radio_on;
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
|
|
val = sc->bluetooth_disable_if_radio_off;
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_ENABLED:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_WWAN_ENABLED) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_RADIO:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_WWAN_RADIO) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_ON_AIR:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
val = ((val & HP_MASK_WWAN_ON_AIR) != 0);
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
|
|
val = sc->wwan_enable_if_radio_on;
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
|
|
val = sc->wwan_disable_if_radio_off;
|
|
break;
|
|
case ACPI_HP_METHOD_ALS:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_ALS_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_DISPLAY:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_HDDTEMP:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_DOCK:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_DOCK_COMMAND, 0, 0, &val))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_CMI_DETAIL:
|
|
val = sc->cmi_detail;
|
|
break;
|
|
case ACPI_HP_METHOD_VERBOSE:
|
|
val = sc->verbose;
|
|
break;
|
|
}
|
|
|
|
return (val);
|
|
}
|
|
|
|
static int
|
|
acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg)
|
|
{
|
|
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
|
|
ACPI_SERIAL_ASSERT(hp);
|
|
|
|
if (method != ACPI_HP_METHOD_CMI_DETAIL &&
|
|
method != ACPI_HP_METHOD_VERBOSE)
|
|
arg = arg?1:0;
|
|
|
|
if (arg != oldarg) {
|
|
switch (method) {
|
|
case ACPI_HP_METHOD_WLAN_ENABLED:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1,
|
|
arg?0x101:0x100, NULL))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
|
|
sc->wlan_enable_if_radio_on = arg;
|
|
acpi_hp_evaluate_auto_on_off(sc);
|
|
break;
|
|
case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
|
|
sc->wlan_disable_if_radio_off = arg;
|
|
acpi_hp_evaluate_auto_on_off(sc);
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1,
|
|
arg?0x202:0x200, NULL))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
|
|
sc->bluetooth_enable_if_radio_on = arg;
|
|
acpi_hp_evaluate_auto_on_off(sc);
|
|
break;
|
|
case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
|
|
sc->bluetooth_disable_if_radio_off = arg?1:0;
|
|
acpi_hp_evaluate_auto_on_off(sc);
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_ENABLED:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_WIRELESS_COMMAND, 1,
|
|
arg?0x404:0x400, NULL))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
|
|
sc->wwan_enable_if_radio_on = arg?1:0;
|
|
acpi_hp_evaluate_auto_on_off(sc);
|
|
break;
|
|
case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
|
|
sc->wwan_disable_if_radio_off = arg?1:0;
|
|
acpi_hp_evaluate_auto_on_off(sc);
|
|
break;
|
|
case ACPI_HP_METHOD_ALS:
|
|
if (acpi_hp_exec_wmi_command(sc->wmi_dev,
|
|
ACPI_HP_WMI_ALS_COMMAND, 1, arg?1:0, NULL))
|
|
return (-EINVAL);
|
|
break;
|
|
case ACPI_HP_METHOD_CMI_DETAIL:
|
|
sc->cmi_detail = arg;
|
|
if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) !=
|
|
(oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) {
|
|
sc->cmi_order_size = -1;
|
|
}
|
|
break;
|
|
case ACPI_HP_METHOD_VERBOSE:
|
|
sc->verbose = arg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static __inline void
|
|
acpi_hp_free_buffer(ACPI_BUFFER* buf) {
|
|
if (buf && buf->Pointer) {
|
|
AcpiOsFree(buf->Pointer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context)
|
|
{
|
|
device_t dev = context;
|
|
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
|
|
|
|
struct acpi_hp_softc *sc = device_get_softc(dev);
|
|
ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
ACPI_OBJECT *obj;
|
|
ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
|
|
obj = (ACPI_OBJECT*) response.Pointer;
|
|
if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) {
|
|
switch (*((UINT8 *) obj->Buffer.Pointer)) {
|
|
case ACPI_HP_EVENT_WIRELESS:
|
|
acpi_hp_evaluate_auto_on_off(sc);
|
|
break;
|
|
default:
|
|
if (sc->verbose) {
|
|
device_printf(sc->dev, "Event %02x\n",
|
|
*((UINT8 *) obj->Buffer.Pointer));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
acpi_hp_free_buffer(&response);
|
|
}
|
|
|
|
static int
|
|
acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write,
|
|
int val, int *retval)
|
|
{
|
|
UINT32 params[4+32] = { 0x55434553, is_write ? 2 : 1,
|
|
command, 4, val};
|
|
UINT32* result;
|
|
ACPI_OBJECT *obj;
|
|
ACPI_BUFFER in = { sizeof(params), ¶ms };
|
|
ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
int res;
|
|
|
|
if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID,
|
|
0, 0x3, &in, &out))) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
obj = out.Pointer;
|
|
if (!obj || obj->Type != ACPI_TYPE_BUFFER) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
result = (UINT32*) obj->Buffer.Pointer;
|
|
res = result[1];
|
|
if (res == 0 && retval != NULL)
|
|
*retval = result[2];
|
|
acpi_hp_free_buffer(&out);
|
|
|
|
return (res);
|
|
}
|
|
|
|
static __inline char*
|
|
acpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) {
|
|
int length;
|
|
|
|
dst[0] = 0;
|
|
if (obj->Type == ACPI_TYPE_STRING) {
|
|
length = obj->String.Length+1;
|
|
if (length > size) {
|
|
length = size - 1;
|
|
}
|
|
strlcpy(dst, obj->String.Pointer, length);
|
|
acpi_hp_hex_decode(dst);
|
|
}
|
|
|
|
return (dst);
|
|
}
|
|
|
|
/*
|
|
* Read BIOS Setting block in instance "instance".
|
|
* The block returned is ACPI_TYPE_PACKAGE which should contain the following
|
|
* elements:
|
|
* Index Meaning
|
|
* 0 Setting Name [string]
|
|
* 1 Value (comma separated, asterisk marks the current value) [string]
|
|
* 2 Path within the bios hierarchy [string]
|
|
* 3 IsReadOnly [int]
|
|
* 4 DisplayInUI [int]
|
|
* 5 RequiresPhysicalPresence [int]
|
|
* 6 Sequence for ordering within the bios settings (absolute) [int]
|
|
* 7 Length of prerequisites array [int]
|
|
* 8..8+[7] PrerequisiteN [string]
|
|
* 9+[7] Current value (in case of enum) [string] / Array length [int]
|
|
* 10+[7] Enum length [int] / Array values
|
|
* 11+[7]ff Enum value at index x [string]
|
|
*/
|
|
static int
|
|
acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance,
|
|
char* outbuf, size_t outsize, UINT32* sequence, int detail)
|
|
{
|
|
ACPI_OBJECT *obj;
|
|
ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
int i;
|
|
int outlen;
|
|
int has_enums = 0;
|
|
int valuebase = 0;
|
|
char string_buffer[255];
|
|
int enumbase;
|
|
|
|
outlen = 0;
|
|
outbuf[0] = 0;
|
|
if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
obj = out.Pointer;
|
|
if (!obj || obj->Type != ACPI_TYPE_PACKAGE) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
|
|
/* Check if first 6 bytes matches our expectations. */
|
|
if (obj->Package.Count < 8 ||
|
|
obj->Package.Elements[0].Type != ACPI_TYPE_STRING ||
|
|
obj->Package.Elements[1].Type != ACPI_TYPE_STRING ||
|
|
obj->Package.Elements[2].Type != ACPI_TYPE_STRING ||
|
|
obj->Package.Elements[3].Type != ACPI_TYPE_INTEGER ||
|
|
obj->Package.Elements[4].Type != ACPI_TYPE_INTEGER ||
|
|
obj->Package.Elements[5].Type != ACPI_TYPE_INTEGER ||
|
|
obj->Package.Elements[6].Type != ACPI_TYPE_INTEGER ||
|
|
obj->Package.Elements[7].Type != ACPI_TYPE_INTEGER) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
|
|
/* Skip prerequisites and optionally array. */
|
|
valuebase = 8 + obj->Package.Elements[7].Integer.Value;
|
|
if (obj->Package.Count <= valuebase) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
if (obj->Package.Elements[valuebase].Type == ACPI_TYPE_INTEGER)
|
|
valuebase += 1 + obj->Package.Elements[valuebase].Integer.Value;
|
|
|
|
/* Check if we have value and enum. */
|
|
if (obj->Package.Count <= valuebase + 1 ||
|
|
obj->Package.Elements[valuebase].Type != ACPI_TYPE_STRING ||
|
|
obj->Package.Elements[valuebase+1].Type != ACPI_TYPE_INTEGER) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
enumbase = valuebase + 1;
|
|
if (obj->Package.Count <= valuebase +
|
|
obj->Package.Elements[enumbase].Integer.Value) {
|
|
acpi_hp_free_buffer(&out);
|
|
return (-EINVAL);
|
|
}
|
|
|
|
if (detail & ACPI_HP_CMI_DETAIL_PATHS) {
|
|
strlcat(outbuf, acpi_hp_get_string_from_object(
|
|
&obj->Package.Elements[2],
|
|
string_buffer, sizeof(string_buffer)), outsize);
|
|
outlen += 48;
|
|
while (strlen(outbuf) < outlen)
|
|
strlcat(outbuf, " ", outsize);
|
|
}
|
|
strlcat(outbuf, acpi_hp_get_string_from_object(
|
|
&obj->Package.Elements[0],
|
|
string_buffer, sizeof(string_buffer)), outsize);
|
|
outlen += 43;
|
|
while (strlen(outbuf) < outlen)
|
|
strlcat(outbuf, " ", outsize);
|
|
strlcat(outbuf, acpi_hp_get_string_from_object(
|
|
&obj->Package.Elements[valuebase],
|
|
string_buffer, sizeof(string_buffer)), outsize);
|
|
outlen += 21;
|
|
while (strlen(outbuf) < outlen)
|
|
strlcat(outbuf, " ", outsize);
|
|
for (i = 0; i < strlen(outbuf); ++i)
|
|
if (outbuf[i] == '\\')
|
|
outbuf[i] = '/';
|
|
if (detail & ACPI_HP_CMI_DETAIL_ENUMS) {
|
|
for (i = enumbase + 1; i < enumbase + 1 +
|
|
obj->Package.Elements[enumbase].Integer.Value; ++i) {
|
|
acpi_hp_get_string_from_object(
|
|
&obj->Package.Elements[i],
|
|
string_buffer, sizeof(string_buffer));
|
|
if (strlen(string_buffer) > 1 ||
|
|
(strlen(string_buffer) == 1 &&
|
|
string_buffer[0] != ' ')) {
|
|
if (has_enums)
|
|
strlcat(outbuf, "/", outsize);
|
|
else
|
|
strlcat(outbuf, " (", outsize);
|
|
strlcat(outbuf, string_buffer, outsize);
|
|
has_enums = 1;
|
|
}
|
|
}
|
|
}
|
|
if (has_enums)
|
|
strlcat(outbuf, ")", outsize);
|
|
if (detail & ACPI_HP_CMI_DETAIL_FLAGS) {
|
|
strlcat(outbuf, obj->Package.Elements[3].Integer.Value ?
|
|
" [ReadOnly]" : "", outsize);
|
|
strlcat(outbuf, obj->Package.Elements[4].Integer.Value ?
|
|
"" : " [NOUI]", outsize);
|
|
strlcat(outbuf, obj->Package.Elements[5].Integer.Value ?
|
|
" [RPP]" : "", outsize);
|
|
}
|
|
*sequence = (UINT32) obj->Package.Elements[6].Integer.Value;
|
|
acpi_hp_free_buffer(&out);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Convert given two digit hex string (hexin) to an UINT8 referenced
|
|
* by byteout.
|
|
* Return != 0 if the was a problem (invalid input)
|
|
*/
|
|
static __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout)
|
|
{
|
|
unsigned int hi;
|
|
unsigned int lo;
|
|
|
|
hi = hexin[0];
|
|
lo = hexin[1];
|
|
if ('0' <= hi && hi <= '9')
|
|
hi -= '0';
|
|
else if ('A' <= hi && hi <= 'F')
|
|
hi -= ('A' - 10);
|
|
else if ('a' <= hi && hi <= 'f')
|
|
hi -= ('a' - 10);
|
|
else
|
|
return (1);
|
|
if ('0' <= lo && lo <= '9')
|
|
lo -= '0';
|
|
else if ('A' <= lo && lo <= 'F')
|
|
lo -= ('A' - 10);
|
|
else if ('a' <= lo && lo <= 'f')
|
|
lo -= ('a' - 10);
|
|
else
|
|
return (1);
|
|
*byteout = (hi << 4) + lo;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
acpi_hp_hex_decode(char* buffer)
|
|
{
|
|
int i;
|
|
int length = strlen(buffer);
|
|
UINT8 *uin;
|
|
UINT8 uout;
|
|
|
|
if (rounddown((int)length, 2) == length || length < 10)
|
|
return;
|
|
|
|
for (i = 0; i<length; ++i) {
|
|
if (!((i+1)%3)) {
|
|
if (buffer[i] != ' ')
|
|
return;
|
|
}
|
|
else
|
|
if (!((buffer[i] >= '0' && buffer[i] <= '9') ||
|
|
(buffer[i] >= 'A' && buffer[i] <= 'F')))
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i<length; i += 3) {
|
|
uin = &buffer[i];
|
|
uout = 0;
|
|
acpi_hp_hex_to_int(uin, &uout);
|
|
buffer[i/3] = (char) uout;
|
|
}
|
|
buffer[(length+1)/3] = 0;
|
|
}
|
|
|
|
/*
|
|
* open hpcmi device
|
|
*/
|
|
static int
|
|
acpi_hp_hpcmi_open(struct cdev* dev, int flags, int mode, struct thread *td)
|
|
{
|
|
struct acpi_hp_softc *sc;
|
|
int ret;
|
|
|
|
if (dev == NULL || dev->si_drv1 == NULL)
|
|
return (EBADF);
|
|
sc = dev->si_drv1;
|
|
|
|
ACPI_SERIAL_BEGIN(hp);
|
|
if (sc->hpcmi_open_pid != 0) {
|
|
ret = EBUSY;
|
|
}
|
|
else {
|
|
if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND)
|
|
== NULL) {
|
|
ret = ENXIO;
|
|
} else {
|
|
sc->hpcmi_open_pid = td->td_proc->p_pid;
|
|
sc->hpcmi_bufptr = 0;
|
|
ret = 0;
|
|
}
|
|
}
|
|
ACPI_SERIAL_END(hp);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* close hpcmi device
|
|
*/
|
|
static int
|
|
acpi_hp_hpcmi_close(struct cdev* dev, int flags, int mode, struct thread *td)
|
|
{
|
|
struct acpi_hp_softc *sc;
|
|
int ret;
|
|
|
|
if (dev == NULL || dev->si_drv1 == NULL)
|
|
return (EBADF);
|
|
sc = dev->si_drv1;
|
|
|
|
ACPI_SERIAL_BEGIN(hp);
|
|
if (sc->hpcmi_open_pid == 0) {
|
|
ret = EBADF;
|
|
}
|
|
else {
|
|
if (sc->hpcmi_bufptr != -1) {
|
|
sbuf_delete(&sc->hpcmi_sbuf);
|
|
sc->hpcmi_bufptr = -1;
|
|
}
|
|
sc->hpcmi_open_pid = 0;
|
|
ret = 0;
|
|
}
|
|
ACPI_SERIAL_END(hp);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Read from hpcmi bios information
|
|
*/
|
|
static int
|
|
acpi_hp_hpcmi_read(struct cdev *dev, struct uio *buf, int flag)
|
|
{
|
|
struct acpi_hp_softc *sc;
|
|
int pos, i, l, ret;
|
|
UINT8 instance;
|
|
UINT8 maxInstance;
|
|
UINT32 sequence;
|
|
char line[1025];
|
|
|
|
if (dev == NULL || dev->si_drv1 == NULL)
|
|
return (EBADF);
|
|
sc = dev->si_drv1;
|
|
|
|
ACPI_SERIAL_BEGIN(hp);
|
|
if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid
|
|
|| sc->hpcmi_bufptr == -1) {
|
|
ret = EBADF;
|
|
}
|
|
else {
|
|
if (!sbuf_done(&sc->hpcmi_sbuf)) {
|
|
if (sc->cmi_order_size < 0) {
|
|
maxInstance = sc->has_cmi;
|
|
if (!(sc->cmi_detail &
|
|
ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) &&
|
|
maxInstance > 0) {
|
|
maxInstance--;
|
|
}
|
|
sc->cmi_order_size = 0;
|
|
for (instance = 0; instance < maxInstance;
|
|
++instance) {
|
|
if (acpi_hp_get_cmi_block(sc->wmi_dev,
|
|
ACPI_HP_WMI_CMI_GUID, instance,
|
|
line, sizeof(line), &sequence,
|
|
sc->cmi_detail)) {
|
|
instance = maxInstance;
|
|
}
|
|
else {
|
|
pos = sc->cmi_order_size;
|
|
for (i=0;
|
|
i<sc->cmi_order_size && i<127;
|
|
++i) {
|
|
if (sc->cmi_order[i].sequence > sequence) {
|
|
pos = i;
|
|
break;
|
|
}
|
|
}
|
|
for (i=sc->cmi_order_size;
|
|
i>pos;
|
|
--i) {
|
|
sc->cmi_order[i].sequence =
|
|
sc->cmi_order[i-1].sequence;
|
|
sc->cmi_order[i].instance =
|
|
sc->cmi_order[i-1].instance;
|
|
}
|
|
sc->cmi_order[pos].sequence =
|
|
sequence;
|
|
sc->cmi_order[pos].instance =
|
|
instance;
|
|
sc->cmi_order_size++;
|
|
}
|
|
}
|
|
}
|
|
for (i=0; i<sc->cmi_order_size; ++i) {
|
|
if (!acpi_hp_get_cmi_block(sc->wmi_dev,
|
|
ACPI_HP_WMI_CMI_GUID,
|
|
sc->cmi_order[i].instance, line, sizeof(line),
|
|
&sequence, sc->cmi_detail)) {
|
|
sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line);
|
|
}
|
|
}
|
|
sbuf_finish(&sc->hpcmi_sbuf);
|
|
}
|
|
if (sbuf_len(&sc->hpcmi_sbuf) <= 0) {
|
|
sbuf_delete(&sc->hpcmi_sbuf);
|
|
sc->hpcmi_bufptr = -1;
|
|
sc->hpcmi_open_pid = 0;
|
|
ret = ENOMEM;
|
|
} else {
|
|
l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) -
|
|
sc->hpcmi_bufptr);
|
|
ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) +
|
|
sc->hpcmi_bufptr, l, buf) : 0;
|
|
sc->hpcmi_bufptr += l;
|
|
}
|
|
}
|
|
ACPI_SERIAL_END(hp);
|
|
|
|
return (ret);
|
|
}
|