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