acpi_hp.c revision 250053
1194701Srpaulo/*-
2194701Srpaulo * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de>
3194701Srpaulo * All rights reserved.
4194701Srpaulo *
5194701Srpaulo * Redistribution and use in source and binary forms, with or without
6194701Srpaulo * modification, are permitted provided that the following conditions
7194701Srpaulo * are met:
8194701Srpaulo * 1. Redistributions of source code must retain the above copyright
9194701Srpaulo *    notice, this list of conditions and the following disclaimer.
10194701Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11194701Srpaulo *    notice, this list of conditions and the following disclaimer in the
12194701Srpaulo *    documentation and/or other materials provided with the distribution.
13194701Srpaulo *
14194701Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15194701Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16194701Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17194701Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18194701Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19194701Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20194701Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21194701Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22194701Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23194701Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24194701Srpaulo * SUCH DAMAGE.
25194701Srpaulo */
26194701Srpaulo
27194701Srpaulo#include <sys/cdefs.h>
28194701Srpaulo__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_hp.c 250053 2013-04-29 18:54:31Z jhb $");
29194701Srpaulo
30194701Srpaulo/*
31194701Srpaulo * Driver for extra ACPI-controlled features found on HP laptops
32194701Srpaulo * that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p).
33194701Srpaulo * Allows to control and read status of integrated hardware and read
34194701Srpaulo * BIOS settings through CMI.
35194701Srpaulo * Inspired by the hp-wmi driver, which implements a subset of these
36194701Srpaulo * features (hotkeys) on Linux.
37194701Srpaulo *
38194701Srpaulo * HP CMI whitepaper:
39194701Srpaulo *     http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf
40194701Srpaulo * wmi-hp for Linux:
41194701Srpaulo *     http://www.kernel.org
42194701Srpaulo * WMI and ACPI:
43194701Srpaulo *     http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
44194701Srpaulo */
45194701Srpaulo
46194701Srpaulo#include "opt_acpi.h"
47194701Srpaulo#include <sys/param.h>
48194701Srpaulo#include <sys/conf.h>
49194701Srpaulo#include <sys/uio.h>
50194701Srpaulo#include <sys/proc.h>
51194701Srpaulo#include <sys/kernel.h>
52194701Srpaulo#include <sys/bus.h>
53194701Srpaulo#include <sys/sbuf.h>
54194701Srpaulo#include <sys/module.h>
55194701Srpaulo#include <sys/sysctl.h>
56194701Srpaulo
57194701Srpaulo#include <contrib/dev/acpica/include/acpi.h>
58194701Srpaulo#include <contrib/dev/acpica/include/accommon.h>
59194701Srpaulo#include <dev/acpica/acpivar.h>
60194701Srpaulo#include "acpi_wmi_if.h"
61194701Srpaulo
62194701Srpaulo#define _COMPONENT	ACPI_OEM
63194701SrpauloACPI_MODULE_NAME("HP")
64194701Srpaulo
65194701Srpaulo#define ACPI_HP_WMI_EVENT_GUID		"95F24279-4D7B-4334-9387-ACCDC67EF61C"
66194701Srpaulo#define ACPI_HP_WMI_BIOS_GUID		"5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
67194701Srpaulo#define ACPI_HP_WMI_CMI_GUID		"2D114B49-2DFB-4130-B8FE-4A3C09E75133"
68194701Srpaulo
69194701Srpaulo#define ACPI_HP_WMI_DISPLAY_COMMAND	0x1
70194701Srpaulo#define ACPI_HP_WMI_HDDTEMP_COMMAND	0x2
71194701Srpaulo#define ACPI_HP_WMI_ALS_COMMAND		0x3
72194701Srpaulo#define ACPI_HP_WMI_DOCK_COMMAND	0x4
73194701Srpaulo#define ACPI_HP_WMI_WIRELESS_COMMAND	0x5
74194701Srpaulo
75194701Srpaulo#define ACPI_HP_METHOD_WLAN_ENABLED			1
76194701Srpaulo#define ACPI_HP_METHOD_WLAN_RADIO			2
77194701Srpaulo#define ACPI_HP_METHOD_WLAN_ON_AIR			3
78194701Srpaulo#define ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON		4
79194701Srpaulo#define ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF	5
80194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_ENABLED		6
81194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_RADIO			7
82194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_ON_AIR			8
83194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON	9
84194701Srpaulo#define ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF	10
85194701Srpaulo#define ACPI_HP_METHOD_WWAN_ENABLED			11
86194701Srpaulo#define ACPI_HP_METHOD_WWAN_RADIO			12
87194701Srpaulo#define ACPI_HP_METHOD_WWAN_ON_AIR			13
88194701Srpaulo#define ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON		14
89194701Srpaulo#define ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF	15
90194701Srpaulo#define ACPI_HP_METHOD_ALS				16
91194701Srpaulo#define ACPI_HP_METHOD_DISPLAY				17
92194701Srpaulo#define ACPI_HP_METHOD_HDDTEMP				18
93194701Srpaulo#define ACPI_HP_METHOD_DOCK				19
94194701Srpaulo#define ACPI_HP_METHOD_CMI_DETAIL			20
95195325Srpaulo#define ACPI_HP_METHOD_VERBOSE				21
96194701Srpaulo
97194701Srpaulo#define HP_MASK_WWAN_ON_AIR			0x1000000
98194701Srpaulo#define HP_MASK_BLUETOOTH_ON_AIR		0x10000
99194701Srpaulo#define HP_MASK_WLAN_ON_AIR			0x100
100194701Srpaulo#define HP_MASK_WWAN_RADIO			0x8000000
101194701Srpaulo#define HP_MASK_BLUETOOTH_RADIO			0x80000
102194701Srpaulo#define HP_MASK_WLAN_RADIO			0x800
103194701Srpaulo#define HP_MASK_WWAN_ENABLED			0x2000000
104194701Srpaulo#define HP_MASK_BLUETOOTH_ENABLED		0x20000
105194701Srpaulo#define HP_MASK_WLAN_ENABLED			0x200
106194701Srpaulo
107194701Srpaulo#define ACPI_HP_CMI_DETAIL_PATHS		0x01
108194701Srpaulo#define ACPI_HP_CMI_DETAIL_ENUMS		0x02
109194701Srpaulo#define ACPI_HP_CMI_DETAIL_FLAGS		0x04
110195185Srpaulo#define ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE	0x08
111194701Srpaulo
112194701Srpaulostruct acpi_hp_inst_seq_pair {
113194701Srpaulo	UINT32	sequence;	/* sequence number as suggested by cmi bios */
114194701Srpaulo	UINT8	instance;	/* object instance on guid */
115194701Srpaulo};
116194701Srpaulo
117194701Srpaulostruct acpi_hp_softc {
118194701Srpaulo	device_t	dev;
119194701Srpaulo	device_t	wmi_dev;
120194701Srpaulo	int		has_notify;		/* notification GUID found */
121194701Srpaulo	int		has_cmi;		/* CMI GUID found */
122194701Srpaulo	int		cmi_detail;		/* CMI detail level
123194701Srpaulo						   (set by sysctl) */
124195325Srpaulo	int		verbose;		/* add debug output */
125194701Srpaulo	int		wlan_enable_if_radio_on;	/* set by sysctl */
126194701Srpaulo	int		wlan_disable_if_radio_off;	/* set by sysctl */
127194701Srpaulo	int		bluetooth_enable_if_radio_on;	/* set by sysctl */
128194701Srpaulo	int		bluetooth_disable_if_radio_off;	/* set by sysctl */
129194701Srpaulo	int		wwan_enable_if_radio_on;	/* set by sysctl */
130194701Srpaulo	int		wwan_disable_if_radio_off;	/* set by sysctl */
131194701Srpaulo	int		was_wlan_on_air;		/* last known WLAN
132194701Srpaulo							   on air status */
133194701Srpaulo	int		was_bluetooth_on_air;		/* last known BT
134194701Srpaulo							   on air status */
135194701Srpaulo	int		was_wwan_on_air;		/* last known WWAN
136194701Srpaulo							   on air status */
137194701Srpaulo	struct sysctl_ctx_list	*sysctl_ctx;
138194701Srpaulo	struct sysctl_oid	*sysctl_tree;
139194701Srpaulo	struct cdev	*hpcmi_dev_t;		/* hpcmi device handle */
140194701Srpaulo	struct sbuf	hpcmi_sbuf;		/* /dev/hpcmi output sbuf */
141194701Srpaulo	pid_t		hpcmi_open_pid;		/* pid operating on
142194701Srpaulo						   /dev/hpcmi */
143194701Srpaulo	int		hpcmi_bufptr;		/* current pointer position
144194701Srpaulo						   in /dev/hpcmi output buffer
145194701Srpaulo						 */
146194701Srpaulo	int		cmi_order_size;		/* size of cmi_order list */
147194701Srpaulo	struct acpi_hp_inst_seq_pair cmi_order[128];	/* list of CMI
148194701Srpaulo			     instances ordered by BIOS suggested sequence */
149194701Srpaulo};
150194701Srpaulo
151194701Srpaulostatic struct {
152194701Srpaulo	char	*name;
153194701Srpaulo	int	method;
154194701Srpaulo	char	*description;
155194701Srpaulo	int	access;
156194701Srpaulo} acpi_hp_sysctls[] = {
157194701Srpaulo	{
158194701Srpaulo		.name		= "wlan_enabled",
159194701Srpaulo		.method		= ACPI_HP_METHOD_WLAN_ENABLED,
160194701Srpaulo		.description	= "Enable/Disable WLAN (WiFi)",
161194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
162194701Srpaulo	},
163194701Srpaulo	{
164194701Srpaulo		.name		= "wlan_radio",
165194701Srpaulo		.method		= ACPI_HP_METHOD_WLAN_RADIO,
166194701Srpaulo		.description	= "WLAN radio status",
167194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
168194701Srpaulo	},
169194701Srpaulo	{
170194701Srpaulo		.name		= "wlan_on_air",
171194701Srpaulo		.method		= ACPI_HP_METHOD_WLAN_ON_AIR,
172194701Srpaulo		.description	= "WLAN radio ready to use (enabled and radio)",
173194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
174194701Srpaulo	},
175194701Srpaulo	{
176194701Srpaulo		.name		= "wlan_enable_if_radio_on",
177194701Srpaulo		.method		= ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON,
178194701Srpaulo		.description	= "Enable WLAN if radio is turned on",
179194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
180194701Srpaulo	},
181194701Srpaulo	{
182194701Srpaulo		.name		= "wlan_disable_if_radio_off",
183194701Srpaulo		.method		= ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF,
184194701Srpaulo		.description	= "Disable WLAN if radio is turned off",
185194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
186194701Srpaulo	},
187194701Srpaulo	{
188194701Srpaulo		.name		= "bt_enabled",
189194701Srpaulo		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLED,
190194701Srpaulo		.description	= "Enable/Disable Bluetooth",
191194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
192194701Srpaulo	},
193194701Srpaulo	{
194194701Srpaulo		.name		= "bt_radio",
195194701Srpaulo		.method		= ACPI_HP_METHOD_BLUETOOTH_RADIO,
196194701Srpaulo		.description	= "Bluetooth radio status",
197194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
198194701Srpaulo	},
199194701Srpaulo	{
200194701Srpaulo		.name		= "bt_on_air",
201194701Srpaulo		.method		= ACPI_HP_METHOD_BLUETOOTH_ON_AIR,
202194701Srpaulo		.description	= "Bluetooth radio ready to use"
203194701Srpaulo				    " (enabled and radio)",
204194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
205194701Srpaulo	},
206194701Srpaulo	{
207194701Srpaulo		.name		= "bt_enable_if_radio_on",
208194701Srpaulo		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON,
209194701Srpaulo		.description	= "Enable bluetooth if radio is turned on",
210194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
211194701Srpaulo	},
212194701Srpaulo	{
213194701Srpaulo		.name		= "bt_disable_if_radio_off",
214194701Srpaulo		.method		= ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF,
215194701Srpaulo		.description	= "Disable bluetooth if radio is turned off",
216194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
217194701Srpaulo	},
218194701Srpaulo	{
219194701Srpaulo		.name		= "wwan_enabled",
220194701Srpaulo		.method		= ACPI_HP_METHOD_WWAN_ENABLED,
221194701Srpaulo		.description	= "Enable/Disable WWAN (UMTS)",
222194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
223194701Srpaulo	},
224194701Srpaulo	{
225194701Srpaulo		.name		= "wwan_radio",
226194701Srpaulo		.method		= ACPI_HP_METHOD_WWAN_RADIO,
227194701Srpaulo		.description	= "WWAN radio status",
228194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
229194701Srpaulo	},
230194701Srpaulo	{
231194701Srpaulo		.name		= "wwan_on_air",
232194701Srpaulo		.method		= ACPI_HP_METHOD_WWAN_ON_AIR,
233194701Srpaulo		.description	= "WWAN radio ready to use (enabled and radio)",
234194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
235194701Srpaulo	},
236194701Srpaulo	{
237194701Srpaulo		.name		= "wwan_enable_if_radio_on",
238194701Srpaulo		.method		= ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON,
239194701Srpaulo		.description	= "Enable WWAN if radio is turned on",
240194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
241194701Srpaulo	},
242194701Srpaulo	{
243194701Srpaulo		.name		= "wwan_disable_if_radio_off",
244194701Srpaulo		.method		= ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF,
245194701Srpaulo		.description	= "Disable WWAN if radio is turned off",
246194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
247194701Srpaulo	},
248194701Srpaulo	{
249194701Srpaulo		.name		= "als_enabled",
250194701Srpaulo		.method		= ACPI_HP_METHOD_ALS,
251194701Srpaulo		.description	= "Enable/Disable ALS (Ambient light sensor)",
252194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
253194701Srpaulo	},
254194701Srpaulo	{
255194701Srpaulo		.name		= "display",
256194701Srpaulo		.method		= ACPI_HP_METHOD_DISPLAY,
257194701Srpaulo		.description	= "Display status",
258194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
259194701Srpaulo	},
260194701Srpaulo	{
261194701Srpaulo		.name		= "hdd_temperature",
262194701Srpaulo		.method		= ACPI_HP_METHOD_HDDTEMP,
263194701Srpaulo		.description	= "HDD temperature",
264194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
265194701Srpaulo	},
266194701Srpaulo	{
267194701Srpaulo		.name		= "is_docked",
268194701Srpaulo		.method		= ACPI_HP_METHOD_DOCK,
269194701Srpaulo		.description	= "Docking station status",
270194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RD
271194701Srpaulo	},
272194701Srpaulo	{
273194701Srpaulo		.name		= "cmi_detail",
274194701Srpaulo		.method		= ACPI_HP_METHOD_CMI_DETAIL,
275194701Srpaulo		.description	= "Details shown in CMI output "
276194701Srpaulo				    "(cat /dev/hpcmi)",
277194701Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
278194701Srpaulo	},
279195325Srpaulo	{
280195325Srpaulo		.name		= "verbose",
281195325Srpaulo		.method		= ACPI_HP_METHOD_VERBOSE,
282195325Srpaulo		.description	= "Verbosity level",
283195325Srpaulo		.access		= CTLTYPE_INT | CTLFLAG_RW
284195325Srpaulo	},
285194701Srpaulo
286194701Srpaulo	{ NULL, 0, NULL, 0 }
287194701Srpaulo};
288194701Srpaulo
289194701SrpauloACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping");
290194701Srpaulo
291212251Savgstatic void	acpi_hp_identify(driver_t *driver, device_t parent);
292194701Srpaulostatic int	acpi_hp_probe(device_t dev);
293194701Srpaulostatic int	acpi_hp_attach(device_t dev);
294194701Srpaulostatic int	acpi_hp_detach(device_t dev);
295194701Srpaulo
296194701Srpaulostatic void	acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc* sc);
297194701Srpaulostatic int	acpi_hp_sysctl(SYSCTL_HANDLER_ARGS);
298194701Srpaulostatic int	acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method,
299194701Srpaulo		    int arg, int oldarg);
300194701Srpaulostatic int	acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method);
301194701Srpaulostatic int	acpi_hp_exec_wmi_command(device_t wmi_dev, int command,
302194701Srpaulo		    int is_write, int val);
303194701Srpaulostatic void	acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context);
304194701Srpaulostatic int	acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid,
305194701Srpaulo		    UINT8 instance, char* outbuf, size_t outsize,
306194701Srpaulo		    UINT32* sequence, int detail);
307194701Srpaulostatic void	acpi_hp_hex_decode(char* buffer);
308194701Srpaulo
309194701Srpaulostatic d_open_t	acpi_hp_hpcmi_open;
310194701Srpaulostatic d_close_t acpi_hp_hpcmi_close;
311194701Srpaulostatic d_read_t	acpi_hp_hpcmi_read;
312194701Srpaulo
313194701Srpaulo/* handler /dev/hpcmi device */
314194701Srpaulostatic struct cdevsw hpcmi_cdevsw = {
315194701Srpaulo	.d_version = D_VERSION,
316194701Srpaulo	.d_open = acpi_hp_hpcmi_open,
317194701Srpaulo	.d_close = acpi_hp_hpcmi_close,
318194701Srpaulo	.d_read = acpi_hp_hpcmi_read,
319194701Srpaulo	.d_name = "hpcmi",
320194701Srpaulo};
321194701Srpaulo
322194701Srpaulostatic device_method_t acpi_hp_methods[] = {
323212251Savg	DEVMETHOD(device_identify, acpi_hp_identify),
324194701Srpaulo	DEVMETHOD(device_probe, acpi_hp_probe),
325194701Srpaulo	DEVMETHOD(device_attach, acpi_hp_attach),
326194701Srpaulo	DEVMETHOD(device_detach, acpi_hp_detach),
327246128Ssbz
328246128Ssbz	DEVMETHOD_END
329194701Srpaulo};
330194701Srpaulo
331194701Srpaulostatic driver_t	acpi_hp_driver = {
332194701Srpaulo	"acpi_hp",
333194701Srpaulo	acpi_hp_methods,
334194701Srpaulo	sizeof(struct acpi_hp_softc),
335194701Srpaulo};
336194701Srpaulo
337194701Srpaulostatic devclass_t acpi_hp_devclass;
338194701Srpaulo
339212457SavgDRIVER_MODULE(acpi_hp, acpi_wmi, acpi_hp_driver, acpi_hp_devclass,
340194701Srpaulo		0, 0);
341194701SrpauloMODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1);
342194701SrpauloMODULE_DEPEND(acpi_hp, acpi, 1, 1, 1);
343194701Srpaulo
344194701Srpaulostatic void
345194701Srpauloacpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc)
346194701Srpaulo{
347195325Srpaulo	int	wireless;
348195325Srpaulo	int	new_wlan_status;
349195325Srpaulo	int	new_bluetooth_status;
350195325Srpaulo	int	new_wwan_status;
351194701Srpaulo
352194701Srpaulo	wireless = acpi_hp_exec_wmi_command(sc->wmi_dev,
353194701Srpaulo		    ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
354194701Srpaulo	new_wlan_status = -1;
355194701Srpaulo	new_bluetooth_status = -1;
356194701Srpaulo	new_wwan_status = -1;
357194701Srpaulo
358195325Srpaulo	if (sc->verbose)
359195325Srpaulo		device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless);
360194701Srpaulo	if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO)
361194701Srpaulo	    &&  (wireless & HP_MASK_WLAN_ENABLED)) {
362194701Srpaulo		acpi_hp_exec_wmi_command(sc->wmi_dev,
363194701Srpaulo		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100);
364194701Srpaulo		new_wlan_status = 0;
365194701Srpaulo	}
366194701Srpaulo	else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO)
367194701Srpaulo		&&  !(wireless & HP_MASK_WLAN_ENABLED)) {
368194701Srpaulo		acpi_hp_exec_wmi_command(sc->wmi_dev,
369194701Srpaulo		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101);
370194701Srpaulo		new_wlan_status = 1;
371194701Srpaulo	}
372194701Srpaulo	if (sc->bluetooth_disable_if_radio_off &&
373194701Srpaulo	    !(wireless & HP_MASK_BLUETOOTH_RADIO) &&
374194701Srpaulo	    (wireless & HP_MASK_BLUETOOTH_ENABLED)) {
375194701Srpaulo		acpi_hp_exec_wmi_command(sc->wmi_dev,
376194701Srpaulo		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200);
377194701Srpaulo		new_bluetooth_status = 0;
378194701Srpaulo	}
379194701Srpaulo	else if (sc->bluetooth_enable_if_radio_on &&
380194701Srpaulo		(wireless & HP_MASK_BLUETOOTH_RADIO) &&
381194701Srpaulo		!(wireless & HP_MASK_BLUETOOTH_ENABLED)) {
382194701Srpaulo		acpi_hp_exec_wmi_command(sc->wmi_dev,
383194701Srpaulo		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202);
384194701Srpaulo		new_bluetooth_status = 1;
385194701Srpaulo	}
386194701Srpaulo	if (sc->wwan_disable_if_radio_off &&
387194701Srpaulo	    !(wireless & HP_MASK_WWAN_RADIO) &&
388194701Srpaulo	    (wireless & HP_MASK_WWAN_ENABLED)) {
389194701Srpaulo		acpi_hp_exec_wmi_command(sc->wmi_dev,
390194701Srpaulo		ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400);
391194701Srpaulo		new_wwan_status = 0;
392194701Srpaulo	}
393194701Srpaulo	else if (sc->wwan_enable_if_radio_on &&
394194701Srpaulo		(wireless & HP_MASK_WWAN_RADIO) &&
395194701Srpaulo		!(wireless & HP_MASK_WWAN_ENABLED)) {
396194701Srpaulo		acpi_hp_exec_wmi_command(sc->wmi_dev,
397194701Srpaulo		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404);
398194701Srpaulo		new_wwan_status = 1;
399194701Srpaulo	}
400194701Srpaulo
401194701Srpaulo	if (new_wlan_status == -1) {
402194701Srpaulo		new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR);
403194701Srpaulo		if ((new_wlan_status?1:0) != sc->was_wlan_on_air) {
404194701Srpaulo			sc->was_wlan_on_air = sc->was_wlan_on_air?0:1;
405195325Srpaulo			if (sc->verbose)
406195325Srpaulo				device_printf(sc->wmi_dev,
407195325Srpaulo			    	    "WLAN on air changed to %i "
408195325Srpaulo			    	    "(new_wlan_status is %i)\n",
409195325Srpaulo			    	    sc->was_wlan_on_air, new_wlan_status);
410212251Savg			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
411194701Srpaulo			    0xc0+sc->was_wlan_on_air);
412194701Srpaulo		}
413194701Srpaulo	}
414194701Srpaulo	if (new_bluetooth_status == -1) {
415194701Srpaulo		new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR);
416194701Srpaulo		if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) {
417194701Srpaulo			sc->was_bluetooth_on_air = sc->was_bluetooth_on_air?
418194701Srpaulo			    0:1;
419195325Srpaulo			if (sc->verbose)
420195325Srpaulo				device_printf(sc->wmi_dev,
421195325Srpaulo				    "BLUETOOTH on air changed"
422195325Srpaulo				    " to %i (new_bluetooth_status is %i)\n",
423195325Srpaulo				    sc->was_bluetooth_on_air,
424195325Srpaulo				    new_bluetooth_status);
425212251Savg			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
426194701Srpaulo			    0xd0+sc->was_bluetooth_on_air);
427194701Srpaulo		}
428194701Srpaulo	}
429194701Srpaulo	if (new_wwan_status == -1) {
430194701Srpaulo		new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR);
431194701Srpaulo		if ((new_wwan_status?1:0) != sc->was_wwan_on_air) {
432194701Srpaulo			sc->was_wwan_on_air = sc->was_wwan_on_air?0:1;
433195325Srpaulo			if (sc->verbose)
434195325Srpaulo				device_printf(sc->wmi_dev,
435195325Srpaulo				    "WWAN on air changed to %i"
436195325Srpaulo			    	    " (new_wwan_status is %i)\n",
437195325Srpaulo				    sc->was_wwan_on_air, new_wwan_status);
438212251Savg			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
439194701Srpaulo			    0xe0+sc->was_wwan_on_air);
440194701Srpaulo		}
441194701Srpaulo	}
442194701Srpaulo}
443194701Srpaulo
444212251Savgstatic void
445212251Savgacpi_hp_identify(driver_t *driver, device_t parent)
446212251Savg{
447212251Savg
448212251Savg	/* Don't do anything if driver is disabled. */
449212251Savg	if (acpi_disabled("hp"))
450212251Savg		return;
451212251Savg
452212251Savg	/* Add only a single device instance. */
453212251Savg	if (device_find_child(parent, "acpi_hp", -1) != NULL)
454212251Savg		return;
455212251Savg
456212457Savg	if (BUS_ADD_CHILD(parent, 0, "acpi_hp", -1) == NULL)
457212251Savg		device_printf(parent, "add acpi_hp child failed\n");
458212251Savg}
459212251Savg
460194701Srpaulostatic int
461194701Srpauloacpi_hp_probe(device_t dev)
462194701Srpaulo{
463212251Savg
464194701Srpaulo	device_set_desc(dev, "HP ACPI-WMI Mapping");
465194701Srpaulo	return (0);
466194701Srpaulo}
467194701Srpaulo
468194701Srpaulostatic int
469194701Srpauloacpi_hp_attach(device_t dev)
470194701Srpaulo{
471194701Srpaulo	struct acpi_hp_softc	*sc;
472195325Srpaulo	int			arg;
473194701Srpaulo
474194701Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
475194701Srpaulo
476194701Srpaulo	sc = device_get_softc(dev);
477194701Srpaulo	sc->dev = dev;
478194701Srpaulo	sc->has_notify = 0;
479194701Srpaulo	sc->has_cmi = 0;
480194701Srpaulo	sc->bluetooth_enable_if_radio_on = 0;
481194701Srpaulo	sc->bluetooth_disable_if_radio_off = 0;
482194701Srpaulo	sc->wlan_enable_if_radio_on = 0;
483194701Srpaulo	sc->wlan_disable_if_radio_off = 0;
484194701Srpaulo	sc->wlan_enable_if_radio_on = 0;
485194701Srpaulo	sc->wlan_disable_if_radio_off = 0;
486194701Srpaulo	sc->was_wlan_on_air = 0;
487194701Srpaulo	sc->was_bluetooth_on_air = 0;
488194701Srpaulo	sc->was_wwan_on_air = 0;
489194701Srpaulo	sc->cmi_detail = 0;
490194701Srpaulo	sc->cmi_order_size = -1;
491195325Srpaulo	sc->verbose = 0;
492194701Srpaulo	memset(sc->cmi_order, 0, sizeof(sc->cmi_order));
493194701Srpaulo
494212457Savg	sc->wmi_dev = device_get_parent(dev);
495194701Srpaulo	if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
496194701Srpaulo	    ACPI_HP_WMI_BIOS_GUID)) {
497194701Srpaulo		device_printf(dev,
498194701Srpaulo		    "WMI device does not provide the HP BIOS GUID\n");
499194701Srpaulo		return (EINVAL);
500194701Srpaulo	}
501194701Srpaulo	if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
502194701Srpaulo	    ACPI_HP_WMI_EVENT_GUID)) {
503194701Srpaulo		device_printf(dev,
504194701Srpaulo		    "HP event GUID detected, installing event handler\n");
505194701Srpaulo		if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
506194701Srpaulo		    ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) {
507194701Srpaulo			device_printf(dev,
508194701Srpaulo			    "Could not install notification handler!\n");
509194701Srpaulo		}
510194701Srpaulo		else {
511194701Srpaulo			sc->has_notify = 1;
512194701Srpaulo		}
513194701Srpaulo	}
514195185Srpaulo	if ((sc->has_cmi =
515195185Srpaulo	    ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID)
516195185Srpaulo	    )) {
517194701Srpaulo		device_printf(dev, "HP CMI GUID detected\n");
518194701Srpaulo	}
519194701Srpaulo
520194701Srpaulo	if (sc->has_cmi) {
521194701Srpaulo		sc->hpcmi_dev_t = make_dev(&hpcmi_cdevsw, 0, UID_ROOT,
522194701Srpaulo			    GID_WHEEL, 0644, "hpcmi");
523194701Srpaulo		sc->hpcmi_dev_t->si_drv1 = sc;
524194701Srpaulo		sc->hpcmi_open_pid = 0;
525194701Srpaulo		sc->hpcmi_bufptr = -1;
526194701Srpaulo	}
527194701Srpaulo
528194701Srpaulo	ACPI_SERIAL_BEGIN(hp);
529194701Srpaulo
530194701Srpaulo	sc->sysctl_ctx = device_get_sysctl_ctx(dev);
531194701Srpaulo	sc->sysctl_tree = device_get_sysctl_tree(dev);
532194701Srpaulo	for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) {
533194701Srpaulo		arg = 0;
534194701Srpaulo		if ((!sc->has_notify &&
535194701Srpaulo		    (acpi_hp_sysctls[i].method ==
536194701Srpaulo			ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON ||
537194701Srpaulo		    acpi_hp_sysctls[i].method ==
538194701Srpaulo			ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF ||
539194701Srpaulo		    acpi_hp_sysctls[i].method ==
540194701Srpaulo			ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON ||
541194701Srpaulo		    acpi_hp_sysctls[i].method ==
542194701Srpaulo			ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF ||
543194701Srpaulo		    acpi_hp_sysctls[i].method ==
544194701Srpaulo			ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON ||
545194701Srpaulo		    acpi_hp_sysctls[i].method ==
546194701Srpaulo			ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) ||
547194701Srpaulo		    (arg = acpi_hp_sysctl_get(sc,
548194701Srpaulo		    acpi_hp_sysctls[i].method)) < 0) {
549194701Srpaulo			continue;
550194701Srpaulo		}
551194701Srpaulo		if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) {
552194701Srpaulo			sc->was_wlan_on_air = arg;
553194701Srpaulo		}
554194701Srpaulo		else if (acpi_hp_sysctls[i].method ==
555194701Srpaulo			    ACPI_HP_METHOD_BLUETOOTH_ON_AIR) {
556194701Srpaulo			sc->was_bluetooth_on_air = arg;
557194701Srpaulo		}
558194701Srpaulo		else if (acpi_hp_sysctls[i].method ==
559194701Srpaulo			    ACPI_HP_METHOD_WWAN_ON_AIR) {
560194701Srpaulo			sc->was_wwan_on_air = arg;
561194701Srpaulo		}
562194701Srpaulo
563194701Srpaulo		SYSCTL_ADD_PROC(sc->sysctl_ctx,
564194701Srpaulo		SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
565194701Srpaulo			acpi_hp_sysctls[i].name, acpi_hp_sysctls[i].access,
566194701Srpaulo			sc, i, acpi_hp_sysctl, "I",
567194701Srpaulo			acpi_hp_sysctls[i].description);
568194701Srpaulo	}
569194701Srpaulo	ACPI_SERIAL_END(hp);
570194701Srpaulo
571194701Srpaulo	return (0);
572194701Srpaulo}
573194701Srpaulo
574194701Srpaulostatic int
575194701Srpauloacpi_hp_detach(device_t dev)
576194701Srpaulo{
577250053Sjhb	struct acpi_hp_softc *sc;
578194701Srpaulo
579194701Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
580250053Sjhb	sc = device_get_softc(dev);
581250053Sjhb	if (sc->has_cmi && sc->hpcmi_open_pid != 0)
582250053Sjhb		return (EBUSY);
583250053Sjhb
584250053Sjhb	if (sc->has_notify)
585250053Sjhb		ACPI_WMI_REMOVE_EVENT_HANDLER(dev, ACPI_HP_WMI_EVENT_GUID);
586250053Sjhb
587250053Sjhb	if (sc->has_cmi) {
588194701Srpaulo		if (sc->hpcmi_bufptr != -1) {
589194701Srpaulo			sbuf_delete(&sc->hpcmi_sbuf);
590194701Srpaulo			sc->hpcmi_bufptr = -1;
591194701Srpaulo		}
592194701Srpaulo		sc->hpcmi_open_pid = 0;
593194701Srpaulo		destroy_dev(sc->hpcmi_dev_t);
594194701Srpaulo	}
595194701Srpaulo
596250053Sjhb	return (0);
597194701Srpaulo}
598194701Srpaulo
599194701Srpaulostatic int
600194701Srpauloacpi_hp_sysctl(SYSCTL_HANDLER_ARGS)
601194701Srpaulo{
602195325Srpaulo	struct acpi_hp_softc	*sc;
603195325Srpaulo	int			arg;
604195325Srpaulo	int			oldarg;
605195325Srpaulo	int			error = 0;
606195325Srpaulo	int			function;
607195325Srpaulo	int			method;
608194701Srpaulo
609194701Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
610194701Srpaulo
611194701Srpaulo	sc = (struct acpi_hp_softc *)oidp->oid_arg1;
612194701Srpaulo	function = oidp->oid_arg2;
613194701Srpaulo	method = acpi_hp_sysctls[function].method;
614194701Srpaulo
615194701Srpaulo	ACPI_SERIAL_BEGIN(hp);
616194701Srpaulo	arg = acpi_hp_sysctl_get(sc, method);
617194701Srpaulo	oldarg = arg;
618194701Srpaulo	error = sysctl_handle_int(oidp, &arg, 0, req);
619194701Srpaulo	if (!error && req->newptr != NULL) {
620194701Srpaulo		error = acpi_hp_sysctl_set(sc, method, arg, oldarg);
621194701Srpaulo	}
622194701Srpaulo	ACPI_SERIAL_END(hp);
623194701Srpaulo
624194701Srpaulo	return (error);
625194701Srpaulo}
626194701Srpaulo
627194701Srpaulostatic int
628194701Srpauloacpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method)
629194701Srpaulo{
630195325Srpaulo	int	val = 0;
631194701Srpaulo
632194701Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
633194701Srpaulo	ACPI_SERIAL_ASSERT(hp);
634194701Srpaulo
635194701Srpaulo	switch (method) {
636194701Srpaulo	case ACPI_HP_METHOD_WLAN_ENABLED:
637194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
638194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
639194701Srpaulo		val = ((val & HP_MASK_WLAN_ENABLED) != 0);
640194701Srpaulo		break;
641194701Srpaulo	case ACPI_HP_METHOD_WLAN_RADIO:
642194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
643194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
644194701Srpaulo		val = ((val & HP_MASK_WLAN_RADIO) != 0);
645194701Srpaulo		break;
646194701Srpaulo	case ACPI_HP_METHOD_WLAN_ON_AIR:
647194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
648194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
649194701Srpaulo		val = ((val & HP_MASK_WLAN_ON_AIR) != 0);
650194701Srpaulo		break;
651194701Srpaulo	case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
652194701Srpaulo		val = sc->wlan_enable_if_radio_on;
653194701Srpaulo		break;
654194701Srpaulo	case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
655194701Srpaulo		val = sc->wlan_disable_if_radio_off;
656194701Srpaulo		break;
657194701Srpaulo	case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
658194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
659194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
660194701Srpaulo		val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0);
661194701Srpaulo		break;
662194701Srpaulo	case ACPI_HP_METHOD_BLUETOOTH_RADIO:
663194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
664194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
665194701Srpaulo		val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0);
666194701Srpaulo		break;
667194701Srpaulo	case ACPI_HP_METHOD_BLUETOOTH_ON_AIR:
668194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
669194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
670194701Srpaulo		val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0);
671194701Srpaulo		break;
672194701Srpaulo	case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
673194701Srpaulo		val = sc->bluetooth_enable_if_radio_on;
674194701Srpaulo		break;
675194701Srpaulo	case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
676194701Srpaulo		val = sc->bluetooth_disable_if_radio_off;
677194701Srpaulo		break;
678194701Srpaulo	case ACPI_HP_METHOD_WWAN_ENABLED:
679194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
680194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
681194701Srpaulo		val = ((val & HP_MASK_WWAN_ENABLED) != 0);
682194701Srpaulo		break;
683194701Srpaulo	case ACPI_HP_METHOD_WWAN_RADIO:
684194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
685194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
686194701Srpaulo		val = ((val & HP_MASK_WWAN_RADIO) != 0);
687194701Srpaulo		break;
688194701Srpaulo	case ACPI_HP_METHOD_WWAN_ON_AIR:
689194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
690194701Srpaulo			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
691194701Srpaulo		val = ((val & HP_MASK_WWAN_ON_AIR) != 0);
692194701Srpaulo		break;
693194701Srpaulo	case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
694194701Srpaulo		val = sc->wwan_enable_if_radio_on;
695194701Srpaulo		break;
696194701Srpaulo	case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
697194701Srpaulo		val = sc->wwan_disable_if_radio_off;
698194701Srpaulo		break;
699194701Srpaulo	case ACPI_HP_METHOD_ALS:
700194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
701194701Srpaulo			ACPI_HP_WMI_ALS_COMMAND, 0, 0);
702194701Srpaulo		break;
703194701Srpaulo	case ACPI_HP_METHOD_DISPLAY:
704194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
705194701Srpaulo			ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0);
706194701Srpaulo		break;
707194701Srpaulo	case ACPI_HP_METHOD_HDDTEMP:
708194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
709194701Srpaulo			ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0);
710194701Srpaulo		break;
711194701Srpaulo	case ACPI_HP_METHOD_DOCK:
712194701Srpaulo		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
713194701Srpaulo			ACPI_HP_WMI_DOCK_COMMAND, 0, 0);
714194701Srpaulo		break;
715194701Srpaulo	case ACPI_HP_METHOD_CMI_DETAIL:
716194701Srpaulo		val = sc->cmi_detail;
717194701Srpaulo		break;
718195325Srpaulo	case ACPI_HP_METHOD_VERBOSE:
719195325Srpaulo		val = sc->verbose;
720195325Srpaulo		break;
721194701Srpaulo	}
722194701Srpaulo
723194701Srpaulo	return (val);
724194701Srpaulo}
725194701Srpaulo
726194701Srpaulostatic int
727194701Srpauloacpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg)
728194701Srpaulo{
729194701Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
730194701Srpaulo	ACPI_SERIAL_ASSERT(hp);
731194701Srpaulo
732195325Srpaulo	if (method != ACPI_HP_METHOD_CMI_DETAIL &&
733195325Srpaulo	    method != ACPI_HP_METHOD_VERBOSE)
734194701Srpaulo		arg = arg?1:0;
735194701Srpaulo
736194701Srpaulo	if (arg != oldarg) {
737194701Srpaulo		switch (method) {
738194701Srpaulo		case ACPI_HP_METHOD_WLAN_ENABLED:
739194701Srpaulo			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
740194701Srpaulo				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
741194701Srpaulo				    arg?0x101:0x100));
742194701Srpaulo		case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
743194701Srpaulo			sc->wlan_enable_if_radio_on = arg;
744194701Srpaulo			acpi_hp_evaluate_auto_on_off(sc);
745194701Srpaulo			break;
746194701Srpaulo		case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
747194701Srpaulo			sc->wlan_disable_if_radio_off = arg;
748194701Srpaulo			acpi_hp_evaluate_auto_on_off(sc);
749194701Srpaulo			break;
750194701Srpaulo		case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
751194701Srpaulo			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
752194701Srpaulo				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
753194701Srpaulo				    arg?0x202:0x200));
754194701Srpaulo		case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
755194701Srpaulo			sc->bluetooth_enable_if_radio_on = arg;
756194701Srpaulo			acpi_hp_evaluate_auto_on_off(sc);
757194701Srpaulo			break;
758194701Srpaulo		case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
759194701Srpaulo			sc->bluetooth_disable_if_radio_off = arg?1:0;
760194701Srpaulo			acpi_hp_evaluate_auto_on_off(sc);
761194701Srpaulo			break;
762194701Srpaulo		case ACPI_HP_METHOD_WWAN_ENABLED:
763194701Srpaulo			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
764194701Srpaulo				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
765194701Srpaulo				    arg?0x404:0x400));
766194701Srpaulo		case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
767194701Srpaulo			sc->wwan_enable_if_radio_on = arg?1:0;
768194701Srpaulo			acpi_hp_evaluate_auto_on_off(sc);
769194701Srpaulo			break;
770194701Srpaulo		case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
771194701Srpaulo			sc->wwan_disable_if_radio_off = arg?1:0;
772194701Srpaulo			acpi_hp_evaluate_auto_on_off(sc);
773194701Srpaulo			break;
774194701Srpaulo		case ACPI_HP_METHOD_ALS:
775194701Srpaulo			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
776194701Srpaulo				    ACPI_HP_WMI_ALS_COMMAND, 1,
777194701Srpaulo				    arg?1:0));
778194701Srpaulo		case ACPI_HP_METHOD_CMI_DETAIL:
779194701Srpaulo			sc->cmi_detail = arg;
780195185Srpaulo			if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) !=
781195185Srpaulo			    (oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) {
782195185Srpaulo			    sc->cmi_order_size = -1;
783195185Srpaulo			}
784194701Srpaulo			break;
785195325Srpaulo		case ACPI_HP_METHOD_VERBOSE:
786195325Srpaulo			sc->verbose = arg;
787195325Srpaulo			break;
788194701Srpaulo		}
789194701Srpaulo	}
790194701Srpaulo
791194701Srpaulo	return (0);
792194701Srpaulo}
793194701Srpaulo
794194701Srpaulostatic __inline void
795194701Srpauloacpi_hp_free_buffer(ACPI_BUFFER* buf) {
796194701Srpaulo	if (buf && buf->Pointer) {
797194701Srpaulo		AcpiOsFree(buf->Pointer);
798194701Srpaulo	}
799194701Srpaulo}
800194701Srpaulo
801194701Srpaulostatic void
802194701Srpauloacpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context)
803194701Srpaulo{
804194701Srpaulo	device_t dev = context;
805194701Srpaulo	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
806194701Srpaulo
807194701Srpaulo	struct acpi_hp_softc *sc = device_get_softc(dev);
808194701Srpaulo	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
809194701Srpaulo	ACPI_OBJECT *obj;
810194701Srpaulo	ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
811194701Srpaulo	obj = (ACPI_OBJECT*) response.Pointer;
812194701Srpaulo	if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) {
813194701Srpaulo		if (*((UINT8 *) obj->Buffer.Pointer) == 0x5) {
814194701Srpaulo			acpi_hp_evaluate_auto_on_off(sc);
815194701Srpaulo		}
816194701Srpaulo	}
817194701Srpaulo	acpi_hp_free_buffer(&response);
818194701Srpaulo}
819194701Srpaulo
820194701Srpaulostatic int
821194701Srpauloacpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val)
822194701Srpaulo{
823195325Srpaulo	UINT32		params[5] = { 0x55434553,
824195325Srpaulo			    is_write?2:1,
825195325Srpaulo			    command,
826195325Srpaulo			    is_write?4:0,
827195325Srpaulo			    val};
828195325Srpaulo	UINT32*		result;
829195325Srpaulo	ACPI_OBJECT	*obj;
830195325Srpaulo	ACPI_BUFFER	in = { sizeof(params), &params };
831195325Srpaulo	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
832194701Srpaulo	int retval;
833194701Srpaulo
834194701Srpaulo	if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID,
835194701Srpaulo		    0, 0x3, &in, &out))) {
836194701Srpaulo		acpi_hp_free_buffer(&out);
837194701Srpaulo		return (-EINVAL);
838194701Srpaulo	}
839194701Srpaulo	obj = out.Pointer;
840194701Srpaulo	if (!obj || obj->Type != ACPI_TYPE_BUFFER) {
841194701Srpaulo		acpi_hp_free_buffer(&out);
842194701Srpaulo		return (-EINVAL);
843194701Srpaulo	}
844194701Srpaulo	result = (UINT32*) obj->Buffer.Pointer;
845194701Srpaulo	retval = result[2];
846194701Srpaulo	if (result[1] > 0) {
847194701Srpaulo		retval = result[1];
848194701Srpaulo	}
849194701Srpaulo	acpi_hp_free_buffer(&out);
850194701Srpaulo
851194701Srpaulo	return (retval);
852194701Srpaulo}
853194701Srpaulo
854194701Srpaulostatic __inline char*
855194701Srpauloacpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) {
856195325Srpaulo	int	length;
857195325Srpaulo
858194701Srpaulo	dst[0] = 0;
859194701Srpaulo	if (obj->Type == ACPI_TYPE_STRING) {
860194701Srpaulo		length = obj->String.Length+1;
861194701Srpaulo		if (length > size) {
862194701Srpaulo			length = size - 1;
863194701Srpaulo		}
864194701Srpaulo		strlcpy(dst, obj->String.Pointer, length);
865194701Srpaulo		acpi_hp_hex_decode(dst);
866194701Srpaulo	}
867194701Srpaulo
868194701Srpaulo	return (dst);
869194701Srpaulo}
870194701Srpaulo
871194701Srpaulo
872194701Srpaulo/*
873194701Srpaulo * Read BIOS Setting block in instance "instance".
874194701Srpaulo * The block returned is ACPI_TYPE_PACKAGE which should contain the following
875194701Srpaulo * elements:
876194701Srpaulo * Index Meaning
877195325Srpaulo * 0        Setting Name [string]
878195325Srpaulo * 1        Value (comma separated, asterisk marks the current value) [string]
879195325Srpaulo * 2        Path within the bios hierarchy [string]
880195325Srpaulo * 3        IsReadOnly [int]
881195325Srpaulo * 4        DisplayInUI [int]
882195325Srpaulo * 5        RequiresPhysicalPresence [int]
883195325Srpaulo * 6        Sequence for ordering within the bios settings (absolute) [int]
884195325Srpaulo * 7        Length of prerequisites array [int]
885195325Srpaulo * 8..8+[7] PrerequisiteN [string]
886195325Srpaulo * 9+[7]    Current value (in case of enum) [string] / Array length [int]
887195325Srpaulo * 10+[7]   Enum length [int] / Array values
888195325Srpaulo * 11+[7]ff Enum value at index x [string]
889194701Srpaulo */
890194701Srpaulostatic int
891194701Srpauloacpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance,
892194701Srpaulo    char* outbuf, size_t outsize, UINT32* sequence, int detail)
893194701Srpaulo{
894195325Srpaulo	ACPI_OBJECT	*obj;
895195325Srpaulo	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
896195325Srpaulo	int		i;
897195325Srpaulo	int		outlen;
898195325Srpaulo	int		size = 255;
899195325Srpaulo	int		has_enums = 0;
900195325Srpaulo	int		valuebase = 0;
901195325Srpaulo	char		string_buffer[size];
902195325Srpaulo	int		enumbase;
903194701Srpaulo
904194701Srpaulo	outlen = 0;
905194701Srpaulo	outbuf[0] = 0;
906194701Srpaulo	if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) {
907194701Srpaulo		acpi_hp_free_buffer(&out);
908194701Srpaulo		return (-EINVAL);
909194701Srpaulo	}
910194701Srpaulo	obj = out.Pointer;
911209055Sjkim	if (!obj || obj->Type != ACPI_TYPE_PACKAGE) {
912194701Srpaulo		acpi_hp_free_buffer(&out);
913194701Srpaulo		return (-EINVAL);
914194701Srpaulo	}
915194701Srpaulo
916195325Srpaulo	if (obj->Package.Count >= 8 &&
917195325Srpaulo	    obj->Package.Elements[7].Type == ACPI_TYPE_INTEGER) {
918195325Srpaulo	    valuebase = 8 + obj->Package.Elements[7].Integer.Value;
919195325Srpaulo	}
920195325Srpaulo
921194701Srpaulo	/* check if this matches our expectations based on limited knowledge */
922195325Srpaulo	if (valuebase > 7 && obj->Package.Count > valuebase + 1 &&
923194701Srpaulo	    obj->Package.Elements[0].Type == ACPI_TYPE_STRING &&
924194701Srpaulo	    obj->Package.Elements[1].Type == ACPI_TYPE_STRING &&
925194701Srpaulo	    obj->Package.Elements[2].Type == ACPI_TYPE_STRING &&
926194701Srpaulo	    obj->Package.Elements[3].Type == ACPI_TYPE_INTEGER &&
927194701Srpaulo	    obj->Package.Elements[4].Type == ACPI_TYPE_INTEGER &&
928194701Srpaulo	    obj->Package.Elements[5].Type == ACPI_TYPE_INTEGER &&
929194701Srpaulo	    obj->Package.Elements[6].Type == ACPI_TYPE_INTEGER &&
930195325Srpaulo	    obj->Package.Elements[valuebase].Type == ACPI_TYPE_STRING &&
931195325Srpaulo	    obj->Package.Elements[valuebase+1].Type == ACPI_TYPE_INTEGER &&
932195325Srpaulo	    obj->Package.Count > valuebase +
933195325Srpaulo	        obj->Package.Elements[valuebase+1].Integer.Value
934195325Srpaulo	   ) {
935195325Srpaulo		enumbase = valuebase + 1;
936194701Srpaulo		if (detail & ACPI_HP_CMI_DETAIL_PATHS) {
937194701Srpaulo			strlcat(outbuf, acpi_hp_get_string_from_object(
938194701Srpaulo				&obj->Package.Elements[2], string_buffer, size),
939194701Srpaulo				outsize);
940194701Srpaulo			outlen += 48;
941194701Srpaulo			while (strlen(outbuf) < outlen)
942194701Srpaulo				strlcat(outbuf, " ", outsize);
943194701Srpaulo		}
944194701Srpaulo		strlcat(outbuf, acpi_hp_get_string_from_object(
945194701Srpaulo				&obj->Package.Elements[0], string_buffer, size),
946194701Srpaulo				outsize);
947194701Srpaulo		outlen += 43;
948194701Srpaulo		while (strlen(outbuf) < outlen)
949194701Srpaulo			strlcat(outbuf, " ", outsize);
950195325Srpaulo		strlcat(outbuf, acpi_hp_get_string_from_object(
951195325Srpaulo				&obj->Package.Elements[valuebase], string_buffer,
952195325Srpaulo				size),
953195325Srpaulo				outsize);
954194701Srpaulo		outlen += 21;
955194701Srpaulo		while (strlen(outbuf) < outlen)
956194701Srpaulo			strlcat(outbuf, " ", outsize);
957194701Srpaulo		for (i = 0; i < strlen(outbuf); ++i)
958194701Srpaulo			if (outbuf[i] == '\\')
959194701Srpaulo				outbuf[i] = '/';
960194701Srpaulo		if (detail & ACPI_HP_CMI_DETAIL_ENUMS) {
961195325Srpaulo			for (i = enumbase + 1; i < enumbase + 1 +
962194701Srpaulo			    obj->Package.Elements[enumbase].Integer.Value;
963194701Srpaulo			    ++i) {
964194701Srpaulo				acpi_hp_get_string_from_object(
965194701Srpaulo				    &obj->Package.Elements[i], string_buffer,
966194701Srpaulo				    size);
967194701Srpaulo				if (strlen(string_buffer) > 1 ||
968194701Srpaulo				    (strlen(string_buffer) == 1 &&
969194701Srpaulo				    string_buffer[0] != ' ')) {
970194701Srpaulo					if (has_enums)
971194701Srpaulo						strlcat(outbuf, "/", outsize);
972194701Srpaulo					else
973194701Srpaulo						strlcat(outbuf, " (", outsize);
974194701Srpaulo					strlcat(outbuf, string_buffer, outsize);
975194701Srpaulo					has_enums = 1;
976194701Srpaulo				}
977194701Srpaulo			}
978194701Srpaulo		}
979194701Srpaulo		if (has_enums)
980194701Srpaulo			strlcat(outbuf, ")", outsize);
981194701Srpaulo		if (detail & ACPI_HP_CMI_DETAIL_FLAGS) {
982194701Srpaulo			strlcat(outbuf, obj->Package.Elements[3].Integer.Value?
983194701Srpaulo			    " [ReadOnly]":"", outsize);
984194701Srpaulo			strlcat(outbuf, obj->Package.Elements[4].Integer.Value?
985194701Srpaulo			    "":" [NOUI]", outsize);
986194701Srpaulo			strlcat(outbuf, obj->Package.Elements[5].Integer.Value?
987194701Srpaulo			    " [RPP]":"", outsize);
988194701Srpaulo		}
989194701Srpaulo		*sequence = (UINT32) obj->Package.Elements[6].Integer.Value;
990194701Srpaulo	}
991194701Srpaulo	acpi_hp_free_buffer(&out);
992194701Srpaulo
993194701Srpaulo	return (0);
994194701Srpaulo}
995194701Srpaulo
996194701Srpaulo
997194701Srpaulo
998194701Srpaulo/*
999194701Srpaulo * Convert given two digit hex string (hexin) to an UINT8 referenced
1000194701Srpaulo * by byteout.
1001194701Srpaulo * Return != 0 if the was a problem (invalid input)
1002194701Srpaulo */
1003194701Srpaulostatic __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout)
1004194701Srpaulo{
1005195325Srpaulo	unsigned int	hi;
1006195325Srpaulo	unsigned int	lo;
1007194701Srpaulo
1008194701Srpaulo	hi = hexin[0];
1009194701Srpaulo	lo = hexin[1];
1010194701Srpaulo	if ('0' <= hi && hi <= '9')
1011194701Srpaulo		hi -= '0';
1012194701Srpaulo	else if ('A' <= hi && hi <= 'F')
1013194701Srpaulo		hi -= ('A' - 10);
1014194701Srpaulo	else if ('a' <= hi && hi <= 'f')
1015194701Srpaulo		hi -= ('a' - 10);
1016194701Srpaulo	else
1017194701Srpaulo		return (1);
1018194701Srpaulo	if ('0' <= lo && lo <= '9')
1019194701Srpaulo		lo -= '0';
1020194701Srpaulo	else if ('A' <= lo && lo <= 'F')
1021194701Srpaulo		lo -= ('A' - 10);
1022194701Srpaulo	else if ('a' <= lo && lo <= 'f')
1023194701Srpaulo		lo -= ('a' - 10);
1024194701Srpaulo	else
1025194701Srpaulo		return (1);
1026194701Srpaulo	*byteout = (hi << 4) + lo;
1027194701Srpaulo
1028194701Srpaulo	return (0);
1029194701Srpaulo}
1030194701Srpaulo
1031194701Srpaulo
1032194701Srpaulostatic void
1033194701Srpauloacpi_hp_hex_decode(char* buffer)
1034194701Srpaulo{
1035195325Srpaulo	int	i;
1036195325Srpaulo	int	length = strlen(buffer);
1037195325Srpaulo	UINT8	*uin;
1038195325Srpaulo	UINT8	uout;
1039194701Srpaulo
1040194701Srpaulo	if (((int)length/2)*2 == length || length < 10) return;
1041194701Srpaulo
1042194701Srpaulo	for (i = 0; i<length; ++i) {
1043194701Srpaulo		if (!((i+1)%3)) {
1044194701Srpaulo			if (buffer[i] != ' ')
1045194701Srpaulo				return;
1046194701Srpaulo		}
1047194701Srpaulo		else
1048194701Srpaulo			if (!((buffer[i] >= '0' && buffer[i] <= '9') ||
1049194701Srpaulo		    	    (buffer[i] >= 'A' && buffer[i] <= 'F')))
1050194701Srpaulo				return;
1051194701Srpaulo	}
1052194701Srpaulo
1053194701Srpaulo	for (i = 0; i<length; i += 3) {
1054194701Srpaulo		uin = &buffer[i];
1055194701Srpaulo		uout = 0;
1056194701Srpaulo		acpi_hp_hex_to_int(uin, &uout);
1057194701Srpaulo		buffer[i/3] = (char) uout;
1058194701Srpaulo	}
1059194701Srpaulo	buffer[(length+1)/3] = 0;
1060194701Srpaulo}
1061194701Srpaulo
1062194701Srpaulo
1063194701Srpaulo/*
1064194701Srpaulo * open hpcmi device
1065194701Srpaulo */
1066194701Srpaulostatic int
1067194701Srpauloacpi_hp_hpcmi_open(struct cdev* dev, int flags, int mode, struct thread *td)
1068194701Srpaulo{
1069195325Srpaulo	struct acpi_hp_softc	*sc;
1070195325Srpaulo	int			ret;
1071194701Srpaulo
1072194701Srpaulo	if (dev == NULL || dev->si_drv1 == NULL)
1073194701Srpaulo		return (EBADF);
1074194701Srpaulo	sc = dev->si_drv1;
1075194701Srpaulo
1076194701Srpaulo	ACPI_SERIAL_BEGIN(hp);
1077194701Srpaulo	if (sc->hpcmi_open_pid != 0) {
1078194701Srpaulo		ret = EBUSY;
1079194701Srpaulo	}
1080194701Srpaulo	else {
1081194701Srpaulo		if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND)
1082194701Srpaulo		    == NULL) {
1083194701Srpaulo			ret = ENXIO;
1084194701Srpaulo		} else {
1085194701Srpaulo			sc->hpcmi_open_pid = td->td_proc->p_pid;
1086194701Srpaulo			sc->hpcmi_bufptr = 0;
1087194701Srpaulo			ret = 0;
1088194701Srpaulo		}
1089194701Srpaulo	}
1090194701Srpaulo	ACPI_SERIAL_END(hp);
1091194701Srpaulo
1092194701Srpaulo	return (ret);
1093194701Srpaulo}
1094194701Srpaulo
1095194701Srpaulo/*
1096194701Srpaulo * close hpcmi device
1097194701Srpaulo */
1098194701Srpaulostatic int
1099194701Srpauloacpi_hp_hpcmi_close(struct cdev* dev, int flags, int mode, struct thread *td)
1100194701Srpaulo{
1101195325Srpaulo	struct acpi_hp_softc	*sc;
1102195325Srpaulo	int			ret;
1103194701Srpaulo
1104194701Srpaulo	if (dev == NULL || dev->si_drv1 == NULL)
1105194701Srpaulo		return (EBADF);
1106194701Srpaulo	sc = dev->si_drv1;
1107194701Srpaulo
1108194701Srpaulo	ACPI_SERIAL_BEGIN(hp);
1109194701Srpaulo	if (sc->hpcmi_open_pid == 0) {
1110194701Srpaulo		ret = EBADF;
1111194701Srpaulo	}
1112194701Srpaulo	else {
1113194701Srpaulo		if (sc->hpcmi_bufptr != -1) {
1114194701Srpaulo			sbuf_delete(&sc->hpcmi_sbuf);
1115194701Srpaulo			sc->hpcmi_bufptr = -1;
1116194701Srpaulo		}
1117194701Srpaulo		sc->hpcmi_open_pid = 0;
1118194701Srpaulo		ret = 0;
1119194701Srpaulo	}
1120194701Srpaulo	ACPI_SERIAL_END(hp);
1121194701Srpaulo
1122194701Srpaulo	return (ret);
1123194701Srpaulo}
1124194701Srpaulo
1125194701Srpaulo/*
1126194701Srpaulo * Read from hpcmi bios information
1127194701Srpaulo */
1128194701Srpaulostatic int
1129194701Srpauloacpi_hp_hpcmi_read(struct cdev *dev, struct uio *buf, int flag)
1130194701Srpaulo{
1131195325Srpaulo	struct acpi_hp_softc	*sc;
1132195325Srpaulo	int			pos, i, l, ret;
1133195325Srpaulo	UINT8			instance;
1134195325Srpaulo	UINT8			maxInstance;
1135195325Srpaulo	UINT32			sequence;
1136195325Srpaulo	int			linesize = 1025;
1137195325Srpaulo	char			line[linesize];
1138194701Srpaulo
1139194701Srpaulo	if (dev == NULL || dev->si_drv1 == NULL)
1140194701Srpaulo		return (EBADF);
1141194701Srpaulo	sc = dev->si_drv1;
1142194701Srpaulo
1143194701Srpaulo	ACPI_SERIAL_BEGIN(hp);
1144194701Srpaulo	if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid
1145194701Srpaulo	    || sc->hpcmi_bufptr == -1) {
1146194701Srpaulo		ret = EBADF;
1147194701Srpaulo	}
1148194701Srpaulo	else {
1149194701Srpaulo		if (!sbuf_done(&sc->hpcmi_sbuf)) {
1150194701Srpaulo			if (sc->cmi_order_size < 0) {
1151195185Srpaulo				maxInstance = sc->has_cmi;
1152195185Srpaulo				if (!(sc->cmi_detail &
1153195185Srpaulo				    ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) &&
1154195185Srpaulo				    maxInstance > 0) {
1155195185Srpaulo					maxInstance--;
1156195185Srpaulo				}
1157194701Srpaulo				sc->cmi_order_size = 0;
1158195185Srpaulo				for (instance = 0; instance < maxInstance;
1159194701Srpaulo				    ++instance) {
1160194701Srpaulo					if (acpi_hp_get_cmi_block(sc->wmi_dev,
1161194701Srpaulo						ACPI_HP_WMI_CMI_GUID, instance,
1162194701Srpaulo						line, linesize, &sequence,
1163194701Srpaulo						sc->cmi_detail)) {
1164195185Srpaulo						instance = maxInstance;
1165194701Srpaulo					}
1166194701Srpaulo					else {
1167194701Srpaulo						pos = sc->cmi_order_size;
1168194701Srpaulo						for (i=0;
1169194701Srpaulo						  i<sc->cmi_order_size && i<127;
1170194701Srpaulo						     ++i) {
1171194701Srpaulo				if (sc->cmi_order[i].sequence > sequence) {
1172194701Srpaulo								pos = i;
1173194701Srpaulo								break;
1174194701Srpaulo							}
1175194701Srpaulo						}
1176194701Srpaulo						for (i=sc->cmi_order_size;
1177194701Srpaulo						    i>pos;
1178194701Srpaulo						    --i) {
1179194701Srpaulo						sc->cmi_order[i].sequence =
1180194701Srpaulo						    sc->cmi_order[i-1].sequence;
1181194701Srpaulo						sc->cmi_order[i].instance =
1182194701Srpaulo						    sc->cmi_order[i-1].instance;
1183194701Srpaulo						}
1184194701Srpaulo						sc->cmi_order[pos].sequence =
1185194701Srpaulo						    sequence;
1186194701Srpaulo						sc->cmi_order[pos].instance =
1187194701Srpaulo						    instance;
1188194701Srpaulo						sc->cmi_order_size++;
1189194701Srpaulo					}
1190194701Srpaulo				}
1191194701Srpaulo			}
1192194701Srpaulo			for (i=0; i<sc->cmi_order_size; ++i) {
1193194701Srpaulo				if (!acpi_hp_get_cmi_block(sc->wmi_dev,
1194194701Srpaulo				    ACPI_HP_WMI_CMI_GUID,
1195194701Srpaulo				    sc->cmi_order[i].instance, line, linesize,
1196194701Srpaulo				    &sequence, sc->cmi_detail)) {
1197194701Srpaulo					sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line);
1198194701Srpaulo				}
1199194701Srpaulo			}
1200194701Srpaulo			sbuf_finish(&sc->hpcmi_sbuf);
1201194701Srpaulo		}
1202194701Srpaulo		if (sbuf_len(&sc->hpcmi_sbuf) <= 0) {
1203194701Srpaulo			sbuf_delete(&sc->hpcmi_sbuf);
1204194701Srpaulo			sc->hpcmi_bufptr = -1;
1205194701Srpaulo			sc->hpcmi_open_pid = 0;
1206194701Srpaulo			ret = ENOMEM;
1207194701Srpaulo		} else {
1208194701Srpaulo			l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) -
1209194701Srpaulo			    sc->hpcmi_bufptr);
1210194701Srpaulo			ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) +
1211194701Srpaulo			    sc->hpcmi_bufptr, l, buf) : 0;
1212194701Srpaulo			sc->hpcmi_bufptr += l;
1213194701Srpaulo		}
1214194701Srpaulo	}
1215194701Srpaulo	ACPI_SERIAL_END(hp);
1216194701Srpaulo
1217194701Srpaulo	return (ret);
1218194701Srpaulo}
1219