Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 30 Jun 2009 20:52:01 +0200
From:      Michael Gmelin <freebsdusb@bindone.de>
To:        "Paul B. Mahol" <onemda@gmail.com>, Rui Paulo <rpaulo@freebsd.org>
Cc:        freebsd-acpi@freebsd.org
Subject:   Re: New patchset for acpi_wmi/acpi_hp
Message-ID:  <4A4A5ED1.2060600@bindone.de>
In-Reply-To: <4A4A5D5C.3000400@bindone.de>
References:  <4A48DEB8.1040808@bindone.de>		<83012DEA-D524-49CA-A8D2-6B03E69C4ED5@freebsd.org>		<4A49E411.1060509@bindone.de>	<3a142e750906301122j792fb80qeb07783cbf8ea1c2@mail.gmail.com> <4A4A5D5C.3000400@bindone.de>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------050804040908060506020603
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit

Michael Gmelin wrote:
> Paul B. Mahol wrote:
>> On 6/30/09, Michael Gmelin <freebsdusb@bindone.de> wrote:
>>> Rui Paulo wrote:
>>>> On 29 Jun 2009, at 16:33, Michael Gmelin wrote:
>>>>
>>>>> Hello,
>>>>>
>>>>> please find attached more patches against CURRENT of today. These
>>>>> patches include the patches I've sent on the 27th.
>> Everything points they do not.
>>
>>>> Committed, thanks.
>>>>
>>>> --
>>>> Rui Paulo
>>>>
>>> cool thanks, this should be pretty stable and clean enough so I don't
>>> expect any changes besides adding supported hardware to the man page in
>>> the near future (keeping my fingers crossed :)
>>>
>> For some reason patches used for this commit do not have anywhere "verbose"
>> sysctl in code, and cat /dev/hpcmi doesnt work for me using code from CURRENT.
>> Better side of story is that acpica no longer complains about DTD.
>>
>>
>> Sorry, I really should noticed and reported this much before ...
>>
> Damnit, you're right. Don't know what actually happened.
> 
> Sorry Rui, can you help me out?
> 
> Thanks
> Michael
> 
> _______________________________________________
> freebsd-acpi@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-acpi
> To unsubscribe, send any mail to "freebsd-acpi-unsubscribe@freebsd.org"
Please find attached the sources the way the should look like.
Again big sorry, seems like I've been very confused doing that.

Tell me if I should try to make a new patchset against these files based
on current. /&!$%"§! :(

@Paulo: Maybe you can verify if these files compile ok and provide
everything promised (background: My machine runs a very old current and
I basically have to "port" that stuff into a current sourcetree to
create patchsets.. seems like I missed a generation in there).

Michael


--------------050804040908060506020603
Content-Type: text/plain;
 name="acpi_hp.4"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="acpi_hp.4"

.\" Copyright (c) 2009 Michael Gmelin
.\" 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.
.\"
.\" $FreeBSD$
.\"
.Dd June 21, 2009
.Dt ACPI_HP 4 i386
.Os
.Sh NAME
.Nm acpi_hp
.Nd "ACPI extras driver for HP laptops"
.Sh SYNOPSIS
To compile this driver into the kernel,
place the following line in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device acpi_hp"
.Ed
.Pp
Alternatively, to load the driver as a
module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
acpi_hp_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
driver provides support for ACPI-controlled features found on HP laptops
that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p).
.Pp
The main purpose of this driver is to provide an interface,
accessible via
.Xr sysctl 8 ,
.Xr devd 8 and
.Xr devfs 8 ,
through which applications can determine and change the status of
various laptop components and BIOS settings.
.Pp
.Ss Xr devd 8 Ss Events
Devd events received by
.Xr devd 8
provide the following information:
.Pp
.Bl -tag -width "subsystem" -offset indent -compact
.It system
.Qq Li ACPI
.It subsystem
.Qq Li HP
.It type
The source of the event in the ACPI namespace.
The value depends on the model.
.It notify
Event code (see below).
.El
.Pp
Event codes:
.Pp
.Bl -tag -width "0xc0" -offset indent -compact
.It Li 0xc0
WLAN on air status changed to 0 (not on air)
.It Li 0xc1
WLAN on air status changed to 1 (on air)
.It Li 0xd0
Bluetooth on air status changed to 0 (not on air)
.It Li 0xd1
Bluetooth on air status changed to 1 (on air)
.It Li 0xe0
WWAN on air status changed to 0 (not on air)
.It Li 0xe1
WWAN on air status changed to 1 (on air)
.El
.Ss Xr devfs 8 Ss Device
You can read /dev/hpcmi to see your current BIOS settings. The detail level
can be adjusted by setting the sysctl 
.Va cmi_detail
as described below.
.Sh SYSCTL VARIABLES
The following sysctls are currently implemented:
.Ss WLAN:
.Bl -tag -width indent
.It Va dev.acpi_hp.0.wlan_enabled
Toggle WLAN chip activity. 
.It Va dev.acpi_hp.0.wlan_radio
(read-only)
WLAN radio status (controlled by hardware switch)
.It Va dev.acpi_hp.0.wlan_on_air
(read-only)
WLAN on air (chip enabled, hardware switch enabled + enabled in BIOS)
.It Va dev.acpi_hp.0.wlan_enabled_if_radio_on
If set to 1, the WLAN chip will be enabled if the radio is turned on
.It Va dev.acpi_hp.0.wlan_disable_if_radio_off
If set to 1, the WLAN chip will be disabled if the radio is turned off
.El
.Ss Bluetooth:
.Bl -tag -width indent
.It Va dev.acpi_hp.0.bt_enabled
Toggle Bluetooth chip activity. 
.It Va dev.acpi_hp.0.bt_radio
(read-only)
Bluetooth radio status (controlled by hardware switch)
.It Va dev.acpi_hp.0.bt_on_air
(read-only)
Bluetooth on air (chip enabled, hardware switch enabled + enabled in BIOS)
.It Va dev.acpi_hp.0.bt_enabled_if_radio_on
If set to 1, the Bluetooth chip will be enabled if the radio is turned on
.It Va dev.acpi_hp.0.bt_disable_if_radio_off
If set to 1, the Bluetooth chip will be disabled if the radio is turned off
.El
.Ss WWAN:
.Bl -tag -width indent
.It Va dev.acpi_hp.0.wwan_enabled
Toggle WWAN chip activity. 
.It Va dev.acpi_hp.0.wwan_radio
(read-only)
WWAN radio status (controlled by hardware switch)
.It Va dev.acpi_hp.0.wwan_on_air
(read-only)
WWAN on air (chip enabled, hardware switch enabled + enabled in BIOS)
.It Va dev.acpi_hp.0.wwan_enabled_if_radio_on
If set to 1, the WWAN chip will be enabled if the radio is turned on
.It Va dev.acpi_hp.0.wwan_disable_if_radio_off
If set to 1, the WWAN chip will be disabled if the radio is turned off
.El
.Ss Misc:
.Bl -tag -width indent
.It Va dev.acpi_hp.0.als_enabled
Toggle ambient light sensor (ALS)
.It Va dev.acpi_hp.0.display
(read-only)
Display status (bitmask)
.It Va dev.acpi_hp.0.hdd_temperature
(read-only)
HDD temperature
.It Va dev.acpi_hp.0.is_docked
(read-only)
Docking station status (1 if docked)
.It Va dev.acpi_hp.0.cmi_detail
Bitmask to control detail level in /dev/hpcmi output (values can be ORed).
.Bl -tag -width "0x01" -offset indent -compact
.It Li 0x01
Show path component of BIOS setting
.It Li 0x02
Show a list of valid options for the BIOS setting
.It Li 0x04
Show additional flags of BIOS setting (ReadOnly etc.)
.It Li 0x08
Query highest BIOS entry instance. This is broken on many HP models and
therefore disabled by default.
.El
.It Va dev.acpi_hp.0.verbose
(read-only)
Set verbosity level
.El
.Pp
Defaults for these sysctls can be set in
.Xr sysctl.conf 5 .
.Sh HARDWARE
The
.Nm
driver has been reported to support the following hardware:
.Pp
.Bl -bullet -compact
.It
HP Compaq 8510p
.It
HP Compaq nx7300
.El
.Pp
It should work on most HP laptops that feature a WMI enabled BIOS.
.Sh FILES
.Bl -tag -width ".Pa /dev/hpcmi"
.It Pa /dev/hpcmi
Interface to read BIOS settings
.El
.Sh EXAMPLES
The following can be added to
.Xr devd.conf 5
in order disable the LAN interface when WLAN on air and reenable if it's
not:
.Bd -literal -offset indent
notify 0 {
	match "system"          "ACPI";
	match "subsystem"       "HP";
	match "notify"          "0xc0";
	action                  "ifconfig em0 up";
};

notify 0 {
	match "system"          "ACPI";
	match "subsystem"       "HP";
	match "notify"          "0xc1";
	action                  "ifconfig em0 down";
};
.Ed
.Pp
Enable the ambient light sensor:
.Bd -literal -offset indent
sysctl dev.acpi_hp.0.als_enabled=1
.Ed
.Pp
Enable Bluetooth:
.Bd -literal -offset indent
sysctl dev.acpi_hp.0.bt_enabled=1
.Ed
.Pp
Get BIOS settings:
.Bd -literal -offset indent
cat /dev/hpcmi

Serial Port                                Disable              
Infrared Port                              Enable               
Parallel Port                              Disable              
Flash Media Reader                         Disable              
USB Ports including Express Card slot      Enable               
1394 Port                                  Enable               
Cardbus Slot                               Disable              
Express Card Slot                          Disable   
(...)
.Ed
.Pp
Set maximum detail level for /dev/hpcmi output:
.Bd -literal -offset indent
sysctl dev.acpi_hp.0.cmi_detail=7
.Ed
.Pp
.Sh SEE ALSO
.Xr acpi 4 ,
.Xr acpi_wmi 4 ,
.Xr sysctl.conf 5 ,
.Xr devd 8 ,
.Xr devfs 8 ,
.Xr sysctl 8
.Sh HISTORY
The
.Nm
device driver first appeared in
.Fx CURRENT .
.Sh AUTHORS
.An -nosplit
The
.Nm
driver was written by
.An Michael Gmelin Aq freebsd@grem.de
.Pp
It has been inspired by hp-wmi driver, which implements a subset of these   
features (hotkeys) on Linux.
.Pp
.Bl -tag -width indent
.It HP CMI whitepaper:
http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf
.It wmi-hp for Linux:
http://www.kernel.org
.It WMI and ACPI:
http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
.El
.Pp
This manual page was written by
.An Michael Gmelin Aq freebsd@grem.de
.Sh BUGS
This driver is experimental and has only been tested on CURRENT i386 on an
HP Compaq 8510p which featured all supported wireless devices (WWAN/BT/WLAN).
Expect undefined results when operating on different hardware.
.Pp
Loading the driver is slow. Reading from /dev/hpcmi is even slower.
.Pp
Additional features like HP specific sensor readings or writing BIOS
settings are not supported.

--------------050804040908060506020603
Content-Type: text/plain;
 name="acpi_hp.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="acpi_hp.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>
__FBSDID("$FreeBSD$");

/*
 * 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_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_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

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;
	ACPI_HANDLE	handle;
	device_t	wmi_dev;
	int		has_notify;		/* notification GUID found */
	int		has_cmi;		/* CMI GUID 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	access;
} acpi_hp_sysctls[] = {
	{
		.name		= "wlan_enabled",
		.method		= ACPI_HP_METHOD_WLAN_ENABLED,
		.description	= "Enable/Disable WLAN (WiFi)",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "wlan_radio",
		.method		= ACPI_HP_METHOD_WLAN_RADIO,
		.description	= "WLAN radio status",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "wlan_on_air",
		.method		= ACPI_HP_METHOD_WLAN_ON_AIR,
		.description	= "WLAN radio ready to use (enabled and radio)",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "wlan_enable_if_radio_on",
		.method		= ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON,
		.description	= "Enable WLAN if radio is turned on",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "wlan_disable_if_radio_off",
		.method		= ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF,
		.description	= "Disable WLAN if radio is turned off",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "bt_enabled",
		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLED,
		.description	= "Enable/Disable Bluetooth",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "bt_radio",
		.method		= ACPI_HP_METHOD_BLUETOOTH_RADIO,
		.description	= "Bluetooth radio status",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "bt_on_air",
		.method		= ACPI_HP_METHOD_BLUETOOTH_ON_AIR,
		.description	= "Bluetooth radio ready to use"
				    " (enabled and radio)",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "bt_enable_if_radio_on",
		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON,
		.description	= "Enable bluetooth if radio is turned on",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "bt_disable_if_radio_off",
		.method		= ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF,
		.description	= "Disable bluetooth if radio is turned off",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "wwan_enabled",
		.method		= ACPI_HP_METHOD_WWAN_ENABLED,
		.description	= "Enable/Disable WWAN (UMTS)",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "wwan_radio",
		.method		= ACPI_HP_METHOD_WWAN_RADIO,
		.description	= "WWAN radio status",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "wwan_on_air",
		.method		= ACPI_HP_METHOD_WWAN_ON_AIR,
		.description	= "WWAN radio ready to use (enabled and radio)",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "wwan_enable_if_radio_on",
		.method		= ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON,
		.description	= "Enable WWAN if radio is turned on",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "wwan_disable_if_radio_off",
		.method		= ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF,
		.description	= "Disable WWAN if radio is turned off",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "als_enabled",
		.method		= ACPI_HP_METHOD_ALS,
		.description	= "Enable/Disable ALS (Ambient light sensor)",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "display",
		.method		= ACPI_HP_METHOD_DISPLAY,
		.description	= "Display status",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "hdd_temperature",
		.method		= ACPI_HP_METHOD_HDDTEMP,
		.description	= "HDD temperature",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "is_docked",
		.method		= ACPI_HP_METHOD_DOCK,
		.description	= "Docking station status",
		.access		= CTLTYPE_INT | CTLFLAG_RD
	},
	{
		.name		= "cmi_detail",
		.method		= ACPI_HP_METHOD_CMI_DETAIL,
		.description	= "Details shown in CMI output "
				    "(cat /dev/hpcmi)",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},
	{
		.name		= "verbose",
		.method		= ACPI_HP_METHOD_VERBOSE,
		.description	= "Verbosity level",
		.access		= CTLTYPE_INT | CTLFLAG_RW
	},

	{ NULL, 0, NULL, 0 }
};

ACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping");

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);
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_probe, acpi_hp_probe),
	DEVMETHOD(device_attach, acpi_hp_attach),
	DEVMETHOD(device_detach, acpi_hp_detach),
	{0, 0}
};

static driver_t	acpi_hp_driver = {
	"acpi_hp",
	acpi_hp_methods,
	sizeof(struct acpi_hp_softc),
};

static devclass_t acpi_hp_devclass;

DRIVER_MODULE(acpi_hp, acpi, acpi_hp_driver, acpi_hp_devclass,
		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	wireless;
	int	new_wlan_status;
	int	new_bluetooth_status;
	int	new_wwan_status;

	wireless = acpi_hp_exec_wmi_command(sc->wmi_dev,
		    ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
	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);
		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);
		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);
		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);
		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);
		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);
		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", sc->handle,
			    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", sc->handle,
			    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", sc->handle,
			    0xe0+sc->was_wwan_on_air);
		}
	}
}

static int
acpi_hp_probe(device_t dev)
{
	if (acpi_disabled("hp") || device_get_unit(dev) != 0)
		return (ENXIO);
	device_set_desc(dev, "HP ACPI-WMI Mapping");

	return (0);
}

static int
acpi_hp_attach(device_t dev)
{
	struct acpi_hp_softc	*sc;
	struct acpi_softc	*acpi_sc;
	devclass_t		wmi_devclass;
	int			arg;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);

	sc = device_get_softc(dev);
	sc->dev = dev;
	sc->handle = acpi_get_handle(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 = 0;
	memset(sc->cmi_order, 0, sizeof(sc->cmi_order));
	acpi_sc = acpi_device_get_parent_softc(dev);

	if (!(wmi_devclass = devclass_find ("acpi_wmi"))) {
		device_printf(dev, "Couldn't find acpi_wmi devclass\n");
		return (EINVAL);
	}
	if (!(sc->wmi_dev = devclass_get_device(wmi_devclass, 0))) {
		device_printf(dev, "Couldn't find acpi_wmi device\n");
		return (EINVAL);
	}
	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;
	}

	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 &&
		    (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;
		}

		SYSCTL_ADD_PROC(sc->sysctl_ctx,
		SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
			acpi_hp_sysctls[i].name, acpi_hp_sysctls[i].access,
			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)
{
	int	ret;
	
	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
	struct acpi_hp_softc *sc = device_get_softc(dev);
	if (sc->has_cmi && sc->hpcmi_open_pid != 0) {
		ret = EBUSY;
	}
	else {
		if (sc->has_notify) {
			ACPI_WMI_REMOVE_EVENT_HANDLER(dev,
			    ACPI_HP_WMI_EVENT_GUID);
		}
		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);
		ret = 0;
	}

	return (ret);
}

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:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		val = ((val & HP_MASK_WLAN_ENABLED) != 0);
		break;
	case ACPI_HP_METHOD_WLAN_RADIO:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		val = ((val & HP_MASK_WLAN_RADIO) != 0);
		break;
	case ACPI_HP_METHOD_WLAN_ON_AIR:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		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:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0);
		break;
	case ACPI_HP_METHOD_BLUETOOTH_RADIO:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0);
		break;
	case ACPI_HP_METHOD_BLUETOOTH_ON_AIR:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		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:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		val = ((val & HP_MASK_WWAN_ENABLED) != 0);
		break;
	case ACPI_HP_METHOD_WWAN_RADIO:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		val = ((val & HP_MASK_WWAN_RADIO) != 0);
		break;
	case ACPI_HP_METHOD_WWAN_ON_AIR:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
		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:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_ALS_COMMAND, 0, 0);
		break;
	case ACPI_HP_METHOD_DISPLAY:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0);
		break;
	case ACPI_HP_METHOD_HDDTEMP:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0);
		break;
	case ACPI_HP_METHOD_DOCK:
		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
			ACPI_HP_WMI_DOCK_COMMAND, 0, 0);
		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:
			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
				    arg?0x101:0x100));
		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:
			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
				    arg?0x202:0x200));
		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:
			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
				    arg?0x404:0x400));
		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:
			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
				    ACPI_HP_WMI_ALS_COMMAND, 1,
				    arg?1:0));
		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) {
		if (*((UINT8 *) obj->Buffer.Pointer) == 0x5) {
			acpi_hp_evaluate_auto_on_off(sc);
		}
	}
	acpi_hp_free_buffer(&response);
}

static int
acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val)
{
	UINT32		params[5] = { 0x55434553,
			    is_write?2:1,
			    command,
			    is_write?4:0,
			    val};
	UINT32*		result;
	ACPI_OBJECT	*obj;
	ACPI_BUFFER	in = { sizeof(params), &params };
	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
	int retval;
	
	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;
	retval = result[2];
	if (result[1] > 0) {
		retval = result[1];
	}
	acpi_hp_free_buffer(&out);

	return (retval);
}

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		size = 255;
	int		has_enums = 0;
	int		valuebase = 0;
	char		string_buffer[size];
	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);
	}

	if (obj->Package.Count >= 8 &&
	    obj->Package.Elements[7].Type == ACPI_TYPE_INTEGER) {
	    valuebase = 8 + obj->Package.Elements[7].Integer.Value;
	}

	/* check if this matches our expectations based on limited knowledge */
	if (valuebase > 7 && obj->Package.Count > valuebase + 1 &&
	    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[valuebase].Type == ACPI_TYPE_STRING &&
	    obj->Package.Elements[valuebase+1].Type == ACPI_TYPE_INTEGER &&
	    obj->Package.Count > valuebase + 
	        obj->Package.Elements[valuebase+1].Integer.Value
	   ) {
		enumbase = valuebase + 1;
		if (detail & ACPI_HP_CMI_DETAIL_PATHS) {
			strlcat(outbuf, acpi_hp_get_string_from_object(
				&obj->Package.Elements[2], string_buffer, size),
				outsize);
			outlen += 48;
			while (strlen(outbuf) < outlen)
				strlcat(outbuf, " ", outsize);
		}
		strlcat(outbuf, acpi_hp_get_string_from_object(
				&obj->Package.Elements[0], string_buffer, size),
				outsize);
		outlen += 43;
		while (strlen(outbuf) < outlen)
			strlcat(outbuf, " ", outsize);
		strlcat(outbuf, acpi_hp_get_string_from_object(
				&obj->Package.Elements[valuebase], string_buffer, 
				size),
				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,
				    size);
				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 (((int)length/2)*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;
	int			linesize = 1025;
	char			line[linesize];

	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, linesize, &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, linesize,
				    &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);
}

--------------050804040908060506020603
Content-Type: text/plain;
 name="acpi_wmi.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="acpi_wmi.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>
__FBSDID("$FreeBSD$");

/*
 * Driver for acpi-wmi mapping, provides an interface for vendor specific
 * implementations (e.g. HP and Acer laptops).
 * Inspired by the ACPI-WMI mapping driver (c) 2008-2008 Carlos Corbacho which
 * implements this functionality for Linux.
 *
 * WMI and ACPI: http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
 * acpi-wmi for Linux: http://www.kernel.org
 */

#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/malloc.h>
#include <sys/sbuf.h>
#include <sys/module.h>
#include <sys/bus.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"

MALLOC_DEFINE(M_ACPIWMI, "acpiwmi", "ACPI-WMI mapping");

#define _COMPONENT	ACPI_OEM
ACPI_MODULE_NAME("ACPI_WMI");

#define ACPI_WMI_REGFLAG_EXPENSIVE	0x1 /* GUID flag: Expensive operation */
#define ACPI_WMI_REGFLAG_METHOD		0x2	/* GUID flag: Method call */
#define ACPI_WMI_REGFLAG_STRING		0x4	/* GUID flag: String */
#define ACPI_WMI_REGFLAG_EVENT		0x8	/* GUID flag: Event */

/*
 * acpi_wmi driver private structure
 */
struct acpi_wmi_softc {
	device_t	wmi_dev;	/* wmi device id */
	ACPI_HANDLE	wmi_handle;	/* handle of the PNP0C14 node */
	device_t	ec_dev;		/* acpi_ec0 */
	struct cdev	*wmistat_dev_t;	/* wmistat device handle */
	struct sbuf	wmistat_sbuf;	/* sbuf for /dev/wmistat output */
	pid_t		wmistat_open_pid; /* pid operating on /dev/wmistat */
	int		wmistat_bufptr;	/* /dev/wmistat ptr to buffer position */
};

/*
 * Struct that holds information about
 * about a single GUID entry in _WDG
 */
struct guid_info {
	char	guid[16];	/* 16 byte non human readable GUID */
	char	oid[2];		/* object id or event notify id (first byte) */
	UINT8	max_instance;	/* highest instance known for this GUID */
	UINT8	flags;		/* ACPI_WMI_REGFLAG_%s */
};

/* WExx event generation state (on/off) */
enum event_generation_state {
	EVENT_GENERATION_ON = 1,
	EVENT_GENERATION_OFF = 0
};


/*
 * Information about one entry in _WDG.
 * List of those is used to lookup information by GUID.
 */
struct wmi_info {
	TAILQ_ENTRY(wmi_info)	wmi_list;
	struct guid_info	ginfo;		/* information on guid */
	ACPI_NOTIFY_HANDLER	event_handler;/* client provided event handler */
	void			*event_handler_user_data; /* ev handler cookie  */
};

TAILQ_HEAD(wmi_info_list_head, wmi_info)
    wmi_info_list = TAILQ_HEAD_INITIALIZER(wmi_info_list);

ACPI_SERIAL_DECL(acpi_wmi, "ACPI-WMI Mapping");

/* public interface - declaration */
/* standard device interface*/
static int		acpi_wmi_probe(device_t dev);
static int		acpi_wmi_attach(device_t dev);
static int		acpi_wmi_detach(device_t dev);
/* see acpi_wmi_if.m */
static int		acpi_wmi_provides_guid_string_method(device_t dev,
			    const char *guid_string);
static ACPI_STATUS	acpi_wmi_evaluate_call_method(device_t dev,
			    const char *guid_string, UINT8 instance,
			    UINT32 method_id, const ACPI_BUFFER *in,
			    ACPI_BUFFER *out);
static ACPI_STATUS	acpi_wmi_install_event_handler_method(device_t dev,
			    const char *guid_string, ACPI_NOTIFY_HANDLER handler,
			    void *data);
static ACPI_STATUS	acpi_wmi_remove_event_handler_method(device_t dev,
			    const char *guid_string);
static ACPI_STATUS	acpi_wmi_get_event_data_method(device_t dev,
			    UINT32 event_id, ACPI_BUFFER *out);
static ACPI_STATUS	acpi_wmi_get_block_method(device_t dev,
			    const char *guid_string,
			    UINT8 instance, ACPI_BUFFER *out);
static ACPI_STATUS	acpi_wmi_set_block_method(device_t dev,
			    const char *guid_string,
			    UINT8 instance, const ACPI_BUFFER *in);
/* private interface - declaration */
/* callbacks */
static void		acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify,
			    void *context);
static ACPI_STATUS	acpi_wmi_ec_handler(UINT32 function,
			    ACPI_PHYSICAL_ADDRESS address, UINT32 width,
			    ACPI_INTEGER *value, void *context,
			    void *region_context);
/* helpers */
static ACPI_STATUS	acpi_wmi_read_wdg_blocks(ACPI_HANDLE h);
static ACPI_STATUS	acpi_wmi_toggle_we_event_generation(device_t dev,
			    struct wmi_info *winfo,
			    enum event_generation_state state);
static int		acpi_wmi_guid_string_to_guid(const UINT8 *guid_string,
			    UINT8 *guid);
static struct wmi_info* acpi_wmi_lookup_wmi_info_by_guid_string(
			    const char *guid_string);

static d_open_t acpi_wmi_wmistat_open;
static d_close_t acpi_wmi_wmistat_close;
static d_read_t acpi_wmi_wmistat_read;

/* handler /dev/wmistat device */
static struct cdevsw wmistat_cdevsw = {
	.d_version = D_VERSION,
	.d_open = acpi_wmi_wmistat_open,
	.d_close = acpi_wmi_wmistat_close,
	.d_read = acpi_wmi_wmistat_read,
	.d_name = "wmistat",
};


static device_method_t acpi_wmi_methods[] = {
	/* Device interface */
	DEVMETHOD(device_probe,	acpi_wmi_probe),
	DEVMETHOD(device_attach, acpi_wmi_attach),
	DEVMETHOD(device_detach, acpi_wmi_detach),

	/* acpi_wmi interface */
	DEVMETHOD(acpi_wmi_provides_guid_string,
		    acpi_wmi_provides_guid_string_method),
	DEVMETHOD(acpi_wmi_evaluate_call, acpi_wmi_evaluate_call_method),
	DEVMETHOD(acpi_wmi_install_event_handler,
		    acpi_wmi_install_event_handler_method),
	DEVMETHOD(acpi_wmi_remove_event_handler,
		    acpi_wmi_remove_event_handler_method),
	DEVMETHOD(acpi_wmi_get_event_data, acpi_wmi_get_event_data_method),
	DEVMETHOD(acpi_wmi_get_block, acpi_wmi_get_block_method),
	DEVMETHOD(acpi_wmi_set_block, acpi_wmi_set_block_method),

	{0, 0}
};

static driver_t acpi_wmi_driver = {
	"acpi_wmi",
	acpi_wmi_methods,
	sizeof(struct acpi_wmi_softc),
};

static devclass_t acpi_wmi_devclass;
DRIVER_MODULE(acpi_wmi, acpi, acpi_wmi_driver, acpi_wmi_devclass, 0, 0);
MODULE_VERSION(acpi_wmi, 1);
MODULE_DEPEND(acpi_wmi, acpi, 1, 1, 1);
static char *wmi_ids[] = {"PNP0C14", "PNP0c14", NULL};

/*
 * Probe for the PNP0C14 ACPI node
 */
static int
acpi_wmi_probe(device_t dev)
{
	if (acpi_disabled("wmi") ||
	    ACPI_ID_PROBE(device_get_parent(dev), dev, wmi_ids) == NULL)
		return (ENXIO);
	device_set_desc(dev, "ACPI-WMI mapping");

	return (0);
}

/*
 * Attach the device by:
 * - Looking for the first ACPI EC device
 * - Install the notify handler
 * - Install the EC address space handler
 * - Look for the _WDG node and read GUID information blocks
 */
static int
acpi_wmi_attach(device_t dev)
{
	struct acpi_wmi_softc *sc;
	int ret;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
	sc = device_get_softc(dev);
	ret = ENXIO;

	ACPI_SERIAL_BEGIN(acpi_wmi);
	sc->wmi_dev = dev;
	sc->wmi_handle = acpi_get_handle(dev);
	TAILQ_INIT(&wmi_info_list);
	/* XXX Only works with one EC, but nearly all systems only have one. */
	if ((sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0))
	    == NULL)
		device_printf(dev, "cannot find EC device\n");
	else if (ACPI_FAILURE((status = AcpiInstallNotifyHandler(sc->wmi_handle,
		    ACPI_DEVICE_NOTIFY, acpi_wmi_notify_handler, sc))))
		device_printf(sc->wmi_dev, "couldn't install notify handler - %s\n",
		    AcpiFormatException(status));
	else if (ACPI_FAILURE((status = AcpiInstallAddressSpaceHandler(
		    sc->wmi_handle, ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler,
		    NULL, sc)))) {
		device_printf(sc->wmi_dev, "couldn't install EC handler - %s\n",
		    AcpiFormatException(status));
		AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY,
		    acpi_wmi_notify_handler);
	} else if (ACPI_FAILURE((status = acpi_wmi_read_wdg_blocks(
		    sc->wmi_handle)))) {
		device_printf(sc->wmi_dev, "couldn't parse _WDG - %s\n",
		    AcpiFormatException(status));
		AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY,
		    acpi_wmi_notify_handler);
		AcpiRemoveAddressSpaceHandler(sc->wmi_handle, ACPI_ADR_SPACE_EC,
		    acpi_wmi_ec_handler);
	} else {
		sc->wmistat_dev_t = make_dev(&wmistat_cdevsw, 0, UID_ROOT,
		    GID_WHEEL, 0644, "wmistat");
		sc->wmistat_dev_t->si_drv1 = sc;
		sc->wmistat_open_pid = 0;
		sc->wmistat_bufptr = -1;
		ret = 0;
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (ret);
}

/*
 * Detach the driver by:
 * - Removing notification handler
 * - Removing address space handler
 * - Turning off event generation for all WExx event activated by
 *   child drivers
 */
static int
acpi_wmi_detach(device_t dev)
{
	struct wmi_info *winfo, *tmp;
	struct acpi_wmi_softc *sc;
	int ret;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
	sc = device_get_softc(dev);
	ACPI_SERIAL_BEGIN(acpi_wmi);

	if (sc->wmistat_open_pid != 0) {
		ret = EBUSY;
	} else {
		AcpiRemoveNotifyHandler(sc->wmi_handle, ACPI_DEVICE_NOTIFY,
		    acpi_wmi_notify_handler);
		AcpiRemoveAddressSpaceHandler(sc->wmi_handle,
		    ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler);
		TAILQ_FOREACH_SAFE(winfo, &wmi_info_list, wmi_list, tmp) {
			if (winfo->event_handler)
				acpi_wmi_toggle_we_event_generation(dev,
				    winfo, EVENT_GENERATION_OFF);
			TAILQ_REMOVE(&wmi_info_list, winfo, wmi_list);
			free(winfo, M_ACPIWMI);
		}
		if (sc->wmistat_bufptr != -1) {
			sbuf_delete(&sc->wmistat_sbuf);
			sc->wmistat_bufptr = -1;
		}
		sc->wmistat_open_pid = 0;
		destroy_dev(sc->wmistat_dev_t);
		ret = 0;
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (ret);
}


/*
 * Check if the given GUID string (human readable format
 * AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP)
 * exists within _WDG
 */
static int
acpi_wmi_provides_guid_string_method(device_t dev, const char *guid_string)
{
	struct wmi_info *winfo;
	int ret;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
	ACPI_SERIAL_BEGIN(acpi_wmi);
	winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string);
	ret = (winfo == NULL)?0:winfo->ginfo.max_instance+1;
	ACPI_SERIAL_END(acpi_wmi);

	return (ret);
}

/*
 * Call a method "method_id" on the given GUID block
 * write result into user provided output buffer
 */
static ACPI_STATUS
acpi_wmi_evaluate_call_method(device_t dev, const char *guid_string,
    UINT8 instance, UINT32 method_id, const ACPI_BUFFER *in, ACPI_BUFFER *out)
{
	ACPI_OBJECT params[3];
	ACPI_OBJECT_LIST input;
	char method[5] = "WMxx";
	struct wmi_info *winfo;
	struct acpi_wmi_softc *sc;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	sc = device_get_softc(dev);
	ACPI_SERIAL_BEGIN(acpi_wmi);
	if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string))
		    == NULL)
		status = AE_NOT_FOUND;
	else if (!(winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD))
		status = AE_BAD_DATA;
	else if (instance > winfo->ginfo.max_instance)
		status = AE_BAD_PARAMETER;
	else {
		params[0].Type = ACPI_TYPE_INTEGER;
		params[0].Integer.Value = instance;
		params[1].Type = ACPI_TYPE_INTEGER;
		params[1].Integer.Value = method_id;
		input.Pointer = params;
		input.Count = 2;
		if (in) {
			params[2].Type =
			    (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING)
			    ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER;
			params[2].Buffer.Length = in->Length;
			params[2].Buffer.Pointer = in->Pointer;
			input.Count = 3;
		}
		method[2] = winfo->ginfo.oid[0];
		method[3] = winfo->ginfo.oid[1];
		status = AcpiEvaluateObject(sc->wmi_handle, method,
			    &input, out);
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (status);
}

/*
 * Install a user provided event_handler on the given GUID
 * provided *data will be passed on callback
 * If there is already an existing event handler registered it will be silently
 * discarded
 */
static ACPI_STATUS
acpi_wmi_install_event_handler_method(device_t dev, const char *guid_string,
    ACPI_NOTIFY_HANDLER event_handler, void *data)
{
	struct wmi_info *winfo;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	status = AE_OK;
	ACPI_SERIAL_BEGIN(acpi_wmi);
	if (guid_string == NULL || event_handler == NULL)
		status = AE_BAD_PARAMETER;
	else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string))
		    == NULL)
		status = AE_NOT_EXIST;
	else if (winfo->event_handler != NULL ||
		(status = acpi_wmi_toggle_we_event_generation(dev, winfo,
		    EVENT_GENERATION_ON)) == AE_OK) {
		winfo->event_handler = event_handler;
		winfo->event_handler_user_data = data;
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (status);
}

/*
 * Remove a previously installed event handler from the given GUID
 * If there was none installed, this call is silently discarded and
 * reported as AE_OK
 */
static ACPI_STATUS
acpi_wmi_remove_event_handler_method(device_t dev, const char *guid_string)
{
	struct wmi_info *winfo;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	status = AE_OK;
	ACPI_SERIAL_BEGIN(acpi_wmi);
	if (guid_string &&
	    (winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string))
	    != NULL && winfo->event_handler) {
		status = acpi_wmi_toggle_we_event_generation(dev, winfo,
			    EVENT_GENERATION_OFF);
		winfo->event_handler = NULL;
		winfo->event_handler_user_data = NULL;
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (status);
}

/*
 * Get details on an event received through a callback registered
 * through ACPI_WMI_REMOVE_EVENT_HANDLER into a user provided output buffer.
 * (event_id equals "notify" passed in the callback)
 */
static ACPI_STATUS
acpi_wmi_get_event_data_method(device_t dev, UINT32 event_id, ACPI_BUFFER *out)
{
	ACPI_OBJECT_LIST input;
	ACPI_OBJECT params[1];
	struct acpi_wmi_softc *sc;
	struct wmi_info *winfo;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	sc = device_get_softc(dev);
	status = AE_NOT_FOUND;	
	ACPI_SERIAL_BEGIN(acpi_wmi);
	params[0].Type = ACPI_TYPE_INTEGER;
	params[0].Integer.Value = event_id;
	input.Pointer = params;
	input.Count = 1;
	TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) {
		if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) &&
		    ((UINT8) winfo->ginfo.oid[0] == event_id)) {
			status = AcpiEvaluateObject(sc->wmi_handle, "_WED",
				    &input, out);
			break;
		}
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (status);
}

/*
 * Read a block of data from the given GUID (using WQxx (query))
 * Will be returned in a user provided buffer (out).
 * If the method is marked as expensive (ACPI_WMI_REGFLAG_EXPENSIVE)
 * we will first call the WCxx control method to lock the node to
 * lock the node for data collection and release it afterwards.
 * (Failed WCxx calls are ignored to "support" broken implementations)
 */
static ACPI_STATUS
acpi_wmi_get_block_method(device_t dev, const char *guid_string, UINT8 instance,
	ACPI_BUFFER *out)
{
	char wc_method[5] = "WCxx";
	char wq_method[5] = "WQxx";
	ACPI_OBJECT_LIST wc_input;
	ACPI_OBJECT_LIST wq_input;
	ACPI_OBJECT wc_params[1];
	ACPI_OBJECT wq_params[1];
	ACPI_HANDLE wc_handle;
	struct acpi_wmi_softc *sc;
	struct wmi_info *winfo;
	ACPI_STATUS status;
	ACPI_STATUS wc_status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	sc = device_get_softc(dev);
	wc_status = AE_ERROR;
	ACPI_SERIAL_BEGIN(acpi_wmi);
	if (guid_string == NULL || out == NULL)
		status = AE_BAD_PARAMETER;
	else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string))
		    == NULL)
		status = AE_ERROR;
	else if (instance > winfo->ginfo.max_instance)
		status = AE_BAD_PARAMETER;
	else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) ||
	    (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD))
		status = AE_ERROR;
	else {
		wq_params[0].Type = ACPI_TYPE_INTEGER;
		wq_params[0].Integer.Value = instance;
		wq_input.Pointer = wq_params;
		wq_input.Count = 1;
		if (winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE) {
			wc_params[0].Type = ACPI_TYPE_INTEGER;
			wc_params[0].Integer.Value = 1;
			wc_input.Pointer = wc_params;
			wc_input.Count = 1;
			wc_method[2] = winfo->ginfo.oid[0];
			wc_method[3] = winfo->ginfo.oid[1];
			wc_status = AcpiGetHandle(sc->wmi_handle, wc_method,
				    &wc_handle);
			if (ACPI_SUCCESS(wc_status))
				wc_status = AcpiEvaluateObject(wc_handle,
						wc_method, &wc_input, NULL);
		}
		wq_method[2] = winfo->ginfo.oid[0];
		wq_method[3] = winfo->ginfo.oid[1];
		status = AcpiEvaluateObject(sc->wmi_handle, wq_method,
			    &wq_input, out);
		if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EXPENSIVE)
		    && ACPI_SUCCESS(wc_status)) {
			wc_params[0].Integer.Value = 0;
			status = AcpiEvaluateObject(wc_handle, wc_method,
				    &wc_input, NULL);  /* XXX this might be
				    			 the wrong status to
				    			 return? */
		}
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (status);
}

/*
 * Write a block of data to the given GUID (using WSxx)
 */
static ACPI_STATUS
acpi_wmi_set_block_method(device_t dev, const char *guid_string, UINT8 instance,
	const ACPI_BUFFER *in)
{
	char method[5] = "WSxx";
	ACPI_OBJECT_LIST input;
	ACPI_OBJECT params[2];
	struct wmi_info *winfo;
	struct acpi_wmi_softc *sc;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	sc = device_get_softc(dev);
	ACPI_SERIAL_BEGIN(acpi_wmi);
	if (guid_string == NULL || in == NULL)
		status = AE_BAD_DATA;
	else if ((winfo = acpi_wmi_lookup_wmi_info_by_guid_string(guid_string))
		    == NULL)
		status = AE_ERROR;
	else if (instance > winfo->ginfo.max_instance)
		status = AE_BAD_PARAMETER;
	else if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) ||
		    (winfo->ginfo.flags & ACPI_WMI_REGFLAG_METHOD))
		status = AE_ERROR;
	else {
		params[0].Type = ACPI_TYPE_INTEGER;
		params[0].Integer.Value = instance;
		input.Pointer = params;
		input.Count = 2;
		params[1].Type = (winfo->ginfo.flags & ACPI_WMI_REGFLAG_STRING)
		    ?ACPI_TYPE_STRING:ACPI_TYPE_BUFFER;
		params[1].Buffer.Length = in->Length;
		params[1].Buffer.Pointer = in->Pointer;
		method[2] = winfo->ginfo.oid[0];
		method[3] = winfo->ginfo.oid[1];
		status = AcpiEvaluateObject(sc->wmi_handle, method,
			    &input, NULL);
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (status);
}

/*
 * Handle events received and dispatch them to
 * stakeholders that registered through ACPI_WMI_INSTALL_EVENT_HANDLER
 */
static void
acpi_wmi_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
{
	ACPI_NOTIFY_HANDLER handler;
	void *handler_data;
	struct wmi_info *winfo;

	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);

	handler = NULL;
	handler_data = NULL;
	ACPI_SERIAL_BEGIN(acpi_wmi);
	TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) {
		if ((winfo->ginfo.flags & ACPI_WMI_REGFLAG_EVENT) &&
				((UINT8) winfo->ginfo.oid[0] == notify)) {
			if (winfo->event_handler) {
				handler = winfo->event_handler;
				handler_data = winfo->event_handler_user_data;
				break;
			}
		}
	}
	ACPI_SERIAL_END(acpi_wmi);
	if (handler) {
		handler(h, notify, handler_data);
	}
}

/*
 * Handle EC address space notifications reveived on the WDG node
 * (this mimics EcAddressSpaceHandler in acpi_ec.c)
 */
static ACPI_STATUS
acpi_wmi_ec_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
    UINT32 width, ACPI_INTEGER *value, void *context,
    void *region_context)
{
	struct acpi_wmi_softc *sc;
	int i;
	ACPI_INTEGER ec_data;
	UINT8 ec_addr;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address);

	sc = (struct acpi_wmi_softc *)context;
	if (width % 8 != 0 || value == NULL || context == NULL)
		return (AE_BAD_PARAMETER);
	if (address + (width / 8) - 1 > 0xFF)
		return (AE_BAD_ADDRESS);
	if (function == ACPI_READ)
		*value = 0;
	ec_addr = address;
	status = AE_ERROR;

	for (i = 0; i < width; i += 8, ++ec_addr) {
		switch (function) {
		case ACPI_READ:
			status = ACPI_EC_READ(sc->ec_dev, ec_addr, &ec_data, 1);
			if (ACPI_SUCCESS(status))
				*value |= ((ACPI_INTEGER)ec_data) << i;
		break;
		case ACPI_WRITE:
			ec_data = (UINT8)((*value) >> i);
			status = ACPI_EC_WRITE(sc->ec_dev, ec_addr, ec_data, 1);
			break;
		default:
			device_printf(sc->wmi_dev,
			    "invalid acpi_wmi_ec_handler function %d\n",
			    function);
			status = AE_BAD_PARAMETER;
			break;
		}
		if (ACPI_FAILURE(status))
			break;
	}

	return (status);
}

/*
 * Read GUID blocks from the _WDG node
 * into wmi_info_list.
 */
static ACPI_STATUS
acpi_wmi_read_wdg_blocks(ACPI_HANDLE h)
{
	ACPI_BUFFER out = {ACPI_ALLOCATE_BUFFER, NULL};
	struct guid_info *ginfo;
	ACPI_OBJECT *obj;
	struct wmi_info *winfo;
	UINT32 i;
	UINT32 wdg_block_count;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	ACPI_SERIAL_ASSERT(acpi_wmi);
	if (ACPI_FAILURE(status = AcpiEvaluateObject(h, "_WDG", NULL, &out)))
		return (status);
	obj = (ACPI_OBJECT*) out.Pointer;
	wdg_block_count = obj->Buffer.Length / sizeof(struct guid_info);
	if ((ginfo = malloc(obj->Buffer.Length, M_ACPIWMI, M_NOWAIT))
		    == NULL) {
		AcpiOsFree(out.Pointer);
		return (AE_NO_MEMORY);
	}
	memcpy(ginfo, obj->Buffer.Pointer, obj->Buffer.Length);
	for (i = 0; i < wdg_block_count; ++i) {
		if ((winfo = malloc(sizeof(struct wmi_info), M_ACPIWMI,
			    M_NOWAIT | M_ZERO)) == NULL) {
			AcpiOsFree(out.Pointer);
			free(ginfo, M_ACPIWMI);
			return (AE_NO_MEMORY);
		}
		winfo->ginfo = ginfo[i];
		TAILQ_INSERT_TAIL(&wmi_info_list, winfo, wmi_list);
	}
	AcpiOsFree(out.Pointer);
	free(ginfo, M_ACPIWMI);

	return (status);
}

/*
 * Toggle event generation in for the given GUID (passed by winfo)
 * Turn on to get notified (through acpi_wmi_notify_handler) if events happen
 * on the given GUID.
 */
static ACPI_STATUS
acpi_wmi_toggle_we_event_generation(device_t dev, struct wmi_info *winfo,
    enum event_generation_state state)
{
	char method[5] = "WExx";
	ACPI_OBJECT_LIST input;
	ACPI_OBJECT params[1];
	struct acpi_wmi_softc *sc;
	ACPI_STATUS status;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	sc = device_get_softc(dev);
	ACPI_SERIAL_ASSERT(acpi_wmi);
	params[0].Type = ACPI_TYPE_INTEGER;
	params[0].Integer.Value = state==EVENT_GENERATION_ON?1:0;
	input.Pointer = params;
	input.Count = 1;
	
	UINT8 hi = ((UINT8) winfo->ginfo.oid[0]) >> 4;
	UINT8 lo = ((UINT8) winfo->ginfo.oid[0]) & 0xf;
	method[2] = (hi > 9 ? hi + 55: hi + 48);
	method[3] = (lo > 9 ? lo + 55: lo + 48);
	status = AcpiEvaluateObject(sc->wmi_handle, method, &input, NULL);
	if (status == AE_NOT_FOUND) status = AE_OK;

	return (status);
}

/*
 * 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_wmi_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);
}

/*
 * Convert a human readable 36 character GUID into a 16byte
 * machine readable one.
 * The basic algorithm looks as follows:
 * Input:  AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP
 * Output: DCBAFEHGIJKLMNOP
 * (AA BB CC etc. represent two digit hex numbers == bytes)
 * Return != 0 if passed guid string is invalid
 */
static int
acpi_wmi_guid_string_to_guid(const UINT8 *guid_string, UINT8 *guid)
{
	static const int mapping[20] = {3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1,
	    8, 9, -1, 10, 11, 12, 13, 14, 15};
	int i;

	for (i = 0; i < 20; ++i, ++guid_string) {
		if (mapping[i] >= 0) {
			if (acpi_wmi_hex_to_int(guid_string,
			    &guid[mapping[i]]))
				return (-1);
			++guid_string;
		} else if (*guid_string != '-')
			return (-1);
	}

	return (0);
}

/*
 * Lookup a wmi_info structure in wmi_list based on a
 * human readable GUID
 * Return NULL if the GUID is unknown in the _WDG
 */
static struct wmi_info*
acpi_wmi_lookup_wmi_info_by_guid_string(const char *guid_string)
{
	char guid[16];
	struct wmi_info *winfo;

	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

	ACPI_SERIAL_ASSERT(acpi_wmi);

	if (!acpi_wmi_guid_string_to_guid(guid_string, guid)) {
		TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) {
			if (!memcmp(winfo->ginfo.guid, guid, 16)) {
				return (winfo);
			}
		}
	}

	return (NULL);
}

/*
 * open wmistat device
 */
static int
acpi_wmi_wmistat_open(struct cdev* dev, int flags, int mode, struct thread *td)
{
	struct acpi_wmi_softc *sc;
	int ret;

	if (dev == NULL || dev->si_drv1 == NULL)
		return (EBADF);
	sc = dev->si_drv1;

	ACPI_SERIAL_BEGIN(acpi_wmi);
	if (sc->wmistat_open_pid != 0) {
		ret = EBUSY;
	}
	else {
		if (sbuf_new(&sc->wmistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND)
			    == NULL) {
			ret = ENXIO;
		} else {
			sc->wmistat_open_pid = td->td_proc->p_pid;
			sc->wmistat_bufptr = 0;
			ret = 0;
		}
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (ret);
}

/*
 * close wmistat device
 */
static int
acpi_wmi_wmistat_close(struct cdev* dev, int flags, int mode,
    struct thread *td)
{
	struct acpi_wmi_softc *sc;
	int ret;

	if (dev == NULL || dev->si_drv1 == NULL)
		return (EBADF);
	sc = dev->si_drv1;

	ACPI_SERIAL_BEGIN(acpi_wmi);
	if (sc->wmistat_open_pid == 0) {
		ret = EBADF;
	}
	else {
		if (sc->wmistat_bufptr != -1) {
			sbuf_delete(&sc->wmistat_sbuf);
			sc->wmistat_bufptr = -1;
		}
		sc->wmistat_open_pid = 0;
		ret = 0;
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (ret);
}

/*
 * Read from wmistat guid information
 */
static int
acpi_wmi_wmistat_read(struct cdev *dev, struct uio *buf, int flag)
{
	struct acpi_wmi_softc *sc;
	struct wmi_info *winfo;
	int l;
	int ret;
	UINT8* guid;

	if (dev == NULL || dev->si_drv1 == NULL)
		return (EBADF);
	sc = dev->si_drv1;
	
	ACPI_SERIAL_BEGIN(acpi_wmi);
	if (sc->wmistat_open_pid != buf->uio_td->td_proc->p_pid ||
			sc->wmistat_bufptr == -1) {
		ret = EBADF;
	}
	else {
		if (!sbuf_done(&sc->wmistat_sbuf)) {
			sbuf_printf(&sc->wmistat_sbuf, "GUID                 "
				    "                 INST EXPE METH STR "
				    "EVENT OID\n");
			TAILQ_FOREACH(winfo, &wmi_info_list, wmi_list) {
				guid = (UINT8*)winfo->ginfo.guid;
				sbuf_printf(&sc->wmistat_sbuf,
					    "{%02X%02X%02X%02X-%02X%02X-"
					    "%02X%02X-%02X%02X-%02X%02X"
					    "%02X%02X%02X%02X} %3d %-5s",
					guid[3], guid[2], guid[1], guid[0],
					guid[5], guid[4],
					guid[7], guid[6],
					guid[8], guid[9],
					guid[10], guid[11], guid[12],
					guid[13], guid[14], guid[15],
					winfo->ginfo.max_instance,
					(winfo->ginfo.flags&
						ACPI_WMI_REGFLAG_EXPENSIVE)?
						"YES":"NO"
					);
				if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_METHOD)
					sbuf_printf(&sc->wmistat_sbuf,
						    "WM%c%c ",
						    winfo->ginfo.oid[0],
						    winfo->ginfo.oid[1]);
				else
					sbuf_printf(&sc->wmistat_sbuf, "NO   ");
				sbuf_printf(&sc->wmistat_sbuf, "%-4s",
					    (winfo->ginfo.flags&
					    ACPI_WMI_REGFLAG_STRING)?"YES":"NO"
					);
				if (winfo->ginfo.flags&ACPI_WMI_REGFLAG_EVENT)
					sbuf_printf(&sc->wmistat_sbuf,
						    "0x%02X%s -\n",
						    (UINT8)winfo->ginfo.oid[0],
						    winfo->event_handler==NULL?
						    " ":"+");
				else
					sbuf_printf(&sc->wmistat_sbuf,
						    "NO    %c%c\n",
						    winfo->ginfo.oid[0],
						    winfo->ginfo.oid[1]);
			}
			sbuf_finish(&sc->wmistat_sbuf);
		}
		if (sbuf_len(&sc->wmistat_sbuf) <= 0) {
			sbuf_delete(&sc->wmistat_sbuf);
			sc->wmistat_bufptr = -1;
			sc->wmistat_open_pid = 0;
			ret = ENOMEM;
		} else {
			l = min(buf->uio_resid, sbuf_len(&sc->wmistat_sbuf) -
				    sc->wmistat_bufptr);
			ret = (l > 0)?uiomove(sbuf_data(&sc->wmistat_sbuf) +
				    sc->wmistat_bufptr, l, buf) : 0;
			sc->wmistat_bufptr += l;
		}
	}
	ACPI_SERIAL_END(acpi_wmi);

	return (ret);
}

--------------050804040908060506020603
Content-Type: text/plain;
 name="acpi_wmi_if.m"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="acpi_wmi_if.m"

#-
# Copyright (c) 2009 Michael Gmelin
# 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.
#
# $FreeBSD: src/sys/dev/acpic_support/acpi_wmi_if.m,v 1.0 2008/11/23 16:33:00 rmg Exp $
#

#include <sys/bus.h>
#include <sys/types.h>
#include <contrib/dev/acpica/include/acpi.h>

INTERFACE acpi_wmi;

#
# Default implementation for acpi_wmi_generic_provides_guid_string().
#
CODE {
	static int
	acpi_wmi_generic_provides_guid_string(device_t dev, const char* guid_string)
	{
		return 0;
	}
};


#
# Check if given GUID exists in WMI
# Returns number of instances (max_instace+1) or 0 if guid doesn't exist
#
# device_t dev:	Device to probe
# const char* guid_string: String form of the GUID
#
METHOD int provides_guid_string {
	device_t	dev;
	const char*	guid_string;
} DEFAULT acpi_wmi_generic_provides_guid_string;

#
# Evaluate a WMI method call
#
# device_t dev:  Device to use
# const char* guid_string:  String form of the GUID
# UINT8 instance: instance id
# UINT32 method_id: method to call
# const ACPI_BUFFER* in: input data
# ACPI_BUFFER* out: output buffer
#
METHOD ACPI_STATUS evaluate_call {
	device_t	dev;
	const char	*guid_string;
	UINT8		instance;
	UINT32		method_id;
	const ACPI_BUFFER *in;
	ACPI_BUFFER	*out;
};

#
# Get content of a WMI block
#
# device_t dev:  Device to use
# const char* guid_string:  String form of the GUID
# UINT8 instance: instance id
# ACPI_BUFFER* out: output buffer
#
METHOD ACPI_STATUS get_block {
	device_t	dev;
	const char	*guid_string;
	UINT8		instance;
	ACPI_BUFFER	*out;
};
#
# Write to a WMI data block
#
# device_t dev:  Device to use
# const char* guid_string:  String form of the GUID
# UINT8 instance: instance id
# const ACPI_BUFFER* in: input data
#
METHOD ACPI_STATUS set_block {
	device_t	dev;
	const char	*guid_string;
	UINT8		instance;
	const ACPI_BUFFER *in;
};

#
# Install wmi event handler
#
# device_t dev:  Device to use
# const char* guid_string:  String form of the GUID
# ACPI_NOTIFY_HANDLER handler: Handler
# void* data: Payload
#
METHOD ACPI_STATUS install_event_handler {
	device_t	dev;
	const char	*guid_string;
	ACPI_NOTIFY_HANDLER handler;
	void		*data;
};

#
# Remove wmi event handler
#
# device_t dev:  Device to use
# const char* guid_string:  String form of the GUID
#
METHOD ACPI_STATUS remove_event_handler {
	device_t	dev;
	const char	*guid_string;
};


#
# Get event data associated to an event
#
# device_t dev:  Device to use
# UINT32 event_id: event id
# ACPI_BUFFER* out: output buffer
#
METHOD ACPI_STATUS get_event_data {
	device_t	dev;
	UINT32		event_id;
	ACPI_BUFFER	*out;
};

--------------050804040908060506020603
Content-Type: application/octet-stream;
 name="damnit.tgz"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename="damnit.tgz"

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
--------------050804040908060506020603--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4A4A5ED1.2060600>