1/*-
2 * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30/*
31 * Driver for extra ACPI-controlled features found on HP laptops
32 * that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p).
33 * Allows to control and read status of integrated hardware and read
34 * BIOS settings through CMI.
35 * Inspired by the hp-wmi driver, which implements a subset of these
36 * features (hotkeys) on Linux.
37 *
38 * HP CMI whitepaper:
39 *     http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf
40 * wmi-hp for Linux:
41 *     http://www.kernel.org
42 * WMI and ACPI:
43 *     http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
44 */
45
46#include "opt_acpi.h"
47#include <sys/param.h>
48#include <sys/conf.h>
49#include <sys/uio.h>
50#include <sys/proc.h>
51#include <sys/kernel.h>
52#include <sys/bus.h>
53#include <sys/sbuf.h>
54#include <sys/module.h>
55#include <sys/sysctl.h>
56
57#include <contrib/dev/acpica/include/acpi.h>
58#include <contrib/dev/acpica/include/accommon.h>
59#include <dev/acpica/acpivar.h>
60#include "acpi_wmi_if.h"
61
62#define _COMPONENT	ACPI_OEM
63ACPI_MODULE_NAME("HP")
64
65#define ACPI_HP_WMI_EVENT_GUID		"95F24279-4D7B-4334-9387-ACCDC67EF61C"
66#define ACPI_HP_WMI_BIOS_GUID		"5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
67#define ACPI_HP_WMI_CMI_GUID		"2D114B49-2DFB-4130-B8FE-4A3C09E75133"
68
69#define ACPI_HP_WMI_DISPLAY_COMMAND	0x1
70#define ACPI_HP_WMI_HDDTEMP_COMMAND	0x2
71#define ACPI_HP_WMI_ALS_COMMAND		0x3
72#define ACPI_HP_WMI_DOCK_COMMAND	0x4
73#define ACPI_HP_WMI_WIRELESS_COMMAND	0x5
74
75#define ACPI_HP_METHOD_WLAN_ENABLED			1
76#define ACPI_HP_METHOD_WLAN_RADIO			2
77#define ACPI_HP_METHOD_WLAN_ON_AIR			3
78#define ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON		4
79#define ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF	5
80#define ACPI_HP_METHOD_BLUETOOTH_ENABLED		6
81#define ACPI_HP_METHOD_BLUETOOTH_RADIO			7
82#define ACPI_HP_METHOD_BLUETOOTH_ON_AIR			8
83#define ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON	9
84#define ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF	10
85#define ACPI_HP_METHOD_WWAN_ENABLED			11
86#define ACPI_HP_METHOD_WWAN_RADIO			12
87#define ACPI_HP_METHOD_WWAN_ON_AIR			13
88#define ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON		14
89#define ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF	15
90#define ACPI_HP_METHOD_ALS				16
91#define ACPI_HP_METHOD_DISPLAY				17
92#define ACPI_HP_METHOD_HDDTEMP				18
93#define ACPI_HP_METHOD_DOCK				19
94#define ACPI_HP_METHOD_CMI_DETAIL			20
95#define ACPI_HP_METHOD_VERBOSE				21
96
97#define HP_MASK_WWAN_ON_AIR			0x1000000
98#define HP_MASK_BLUETOOTH_ON_AIR		0x10000
99#define HP_MASK_WLAN_ON_AIR			0x100
100#define HP_MASK_WWAN_RADIO			0x8000000
101#define HP_MASK_BLUETOOTH_RADIO			0x80000
102#define HP_MASK_WLAN_RADIO			0x800
103#define HP_MASK_WWAN_ENABLED			0x2000000
104#define HP_MASK_BLUETOOTH_ENABLED		0x20000
105#define HP_MASK_WLAN_ENABLED			0x200
106
107#define ACPI_HP_CMI_DETAIL_PATHS		0x01
108#define ACPI_HP_CMI_DETAIL_ENUMS		0x02
109#define ACPI_HP_CMI_DETAIL_FLAGS		0x04
110#define ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE	0x08
111
112struct acpi_hp_inst_seq_pair {
113	UINT32	sequence;	/* sequence number as suggested by cmi bios */
114	UINT8	instance;	/* object instance on guid */
115};
116
117struct acpi_hp_softc {
118	device_t	dev;
119	device_t	wmi_dev;
120	int		has_notify;		/* notification GUID found */
121	int		has_cmi;		/* CMI GUID found */
122	int		cmi_detail;		/* CMI detail level
123						   (set by sysctl) */
124	int		verbose;		/* add debug output */
125	int		wlan_enable_if_radio_on;	/* set by sysctl */
126	int		wlan_disable_if_radio_off;	/* set by sysctl */
127	int		bluetooth_enable_if_radio_on;	/* set by sysctl */
128	int		bluetooth_disable_if_radio_off;	/* set by sysctl */
129	int		wwan_enable_if_radio_on;	/* set by sysctl */
130	int		wwan_disable_if_radio_off;	/* set by sysctl */
131	int		was_wlan_on_air;		/* last known WLAN
132							   on air status */
133	int		was_bluetooth_on_air;		/* last known BT
134							   on air status */
135	int		was_wwan_on_air;		/* last known WWAN
136							   on air status */
137	struct sysctl_ctx_list	*sysctl_ctx;
138	struct sysctl_oid	*sysctl_tree;
139	struct cdev	*hpcmi_dev_t;		/* hpcmi device handle */
140	struct sbuf	hpcmi_sbuf;		/* /dev/hpcmi output sbuf */
141	pid_t		hpcmi_open_pid;		/* pid operating on
142						   /dev/hpcmi */
143	int		hpcmi_bufptr;		/* current pointer position
144						   in /dev/hpcmi output buffer
145						 */
146	int		cmi_order_size;		/* size of cmi_order list */
147	struct acpi_hp_inst_seq_pair cmi_order[128];	/* list of CMI
148			     instances ordered by BIOS suggested sequence */
149};
150
151static struct {
152	char	*name;
153	int	method;
154	char	*description;
155	int	access;
156} acpi_hp_sysctls[] = {
157	{
158		.name		= "wlan_enabled",
159		.method		= ACPI_HP_METHOD_WLAN_ENABLED,
160		.description	= "Enable/Disable WLAN (WiFi)",
161		.access		= CTLTYPE_INT | CTLFLAG_RW
162	},
163	{
164		.name		= "wlan_radio",
165		.method		= ACPI_HP_METHOD_WLAN_RADIO,
166		.description	= "WLAN radio status",
167		.access		= CTLTYPE_INT | CTLFLAG_RD
168	},
169	{
170		.name		= "wlan_on_air",
171		.method		= ACPI_HP_METHOD_WLAN_ON_AIR,
172		.description	= "WLAN radio ready to use (enabled and radio)",
173		.access		= CTLTYPE_INT | CTLFLAG_RD
174	},
175	{
176		.name		= "wlan_enable_if_radio_on",
177		.method		= ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON,
178		.description	= "Enable WLAN if radio is turned on",
179		.access		= CTLTYPE_INT | CTLFLAG_RW
180	},
181	{
182		.name		= "wlan_disable_if_radio_off",
183		.method		= ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF,
184		.description	= "Disable WLAN if radio is turned off",
185		.access		= CTLTYPE_INT | CTLFLAG_RW
186	},
187	{
188		.name		= "bt_enabled",
189		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLED,
190		.description	= "Enable/Disable Bluetooth",
191		.access		= CTLTYPE_INT | CTLFLAG_RW
192	},
193	{
194		.name		= "bt_radio",
195		.method		= ACPI_HP_METHOD_BLUETOOTH_RADIO,
196		.description	= "Bluetooth radio status",
197		.access		= CTLTYPE_INT | CTLFLAG_RD
198	},
199	{
200		.name		= "bt_on_air",
201		.method		= ACPI_HP_METHOD_BLUETOOTH_ON_AIR,
202		.description	= "Bluetooth radio ready to use"
203				    " (enabled and radio)",
204		.access		= CTLTYPE_INT | CTLFLAG_RD
205	},
206	{
207		.name		= "bt_enable_if_radio_on",
208		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON,
209		.description	= "Enable bluetooth if radio is turned on",
210		.access		= CTLTYPE_INT | CTLFLAG_RW
211	},
212	{
213		.name		= "bt_disable_if_radio_off",
214		.method		= ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF,
215		.description	= "Disable bluetooth if radio is turned off",
216		.access		= CTLTYPE_INT | CTLFLAG_RW
217	},
218	{
219		.name		= "wwan_enabled",
220		.method		= ACPI_HP_METHOD_WWAN_ENABLED,
221		.description	= "Enable/Disable WWAN (UMTS)",
222		.access		= CTLTYPE_INT | CTLFLAG_RW
223	},
224	{
225		.name		= "wwan_radio",
226		.method		= ACPI_HP_METHOD_WWAN_RADIO,
227		.description	= "WWAN radio status",
228		.access		= CTLTYPE_INT | CTLFLAG_RD
229	},
230	{
231		.name		= "wwan_on_air",
232		.method		= ACPI_HP_METHOD_WWAN_ON_AIR,
233		.description	= "WWAN radio ready to use (enabled and radio)",
234		.access		= CTLTYPE_INT | CTLFLAG_RD
235	},
236	{
237		.name		= "wwan_enable_if_radio_on",
238		.method		= ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON,
239		.description	= "Enable WWAN if radio is turned on",
240		.access		= CTLTYPE_INT | CTLFLAG_RW
241	},
242	{
243		.name		= "wwan_disable_if_radio_off",
244		.method		= ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF,
245		.description	= "Disable WWAN if radio is turned off",
246		.access		= CTLTYPE_INT | CTLFLAG_RW
247	},
248	{
249		.name		= "als_enabled",
250		.method		= ACPI_HP_METHOD_ALS,
251		.description	= "Enable/Disable ALS (Ambient light sensor)",
252		.access		= CTLTYPE_INT | CTLFLAG_RW
253	},
254	{
255		.name		= "display",
256		.method		= ACPI_HP_METHOD_DISPLAY,
257		.description	= "Display status",
258		.access		= CTLTYPE_INT | CTLFLAG_RD
259	},
260	{
261		.name		= "hdd_temperature",
262		.method		= ACPI_HP_METHOD_HDDTEMP,
263		.description	= "HDD temperature",
264		.access		= CTLTYPE_INT | CTLFLAG_RD
265	},
266	{
267		.name		= "is_docked",
268		.method		= ACPI_HP_METHOD_DOCK,
269		.description	= "Docking station status",
270		.access		= CTLTYPE_INT | CTLFLAG_RD
271	},
272	{
273		.name		= "cmi_detail",
274		.method		= ACPI_HP_METHOD_CMI_DETAIL,
275		.description	= "Details shown in CMI output "
276				    "(cat /dev/hpcmi)",
277		.access		= CTLTYPE_INT | CTLFLAG_RW
278	},
279	{
280		.name		= "verbose",
281		.method		= ACPI_HP_METHOD_VERBOSE,
282		.description	= "Verbosity level",
283		.access		= CTLTYPE_INT | CTLFLAG_RW
284	},
285
286	{ NULL, 0, NULL, 0 }
287};
288
289ACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping");
290
291static void	acpi_hp_identify(driver_t *driver, device_t parent);
292static int	acpi_hp_probe(device_t dev);
293static int	acpi_hp_attach(device_t dev);
294static int	acpi_hp_detach(device_t dev);
295
296static void	acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc* sc);
297static int	acpi_hp_sysctl(SYSCTL_HANDLER_ARGS);
298static int	acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method,
299		    int arg, int oldarg);
300static int	acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method);
301static int	acpi_hp_exec_wmi_command(device_t wmi_dev, int command,
302		    int is_write, int val);
303static void	acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context);
304static int	acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid,
305		    UINT8 instance, char* outbuf, size_t outsize,
306		    UINT32* sequence, int detail);
307static void	acpi_hp_hex_decode(char* buffer);
308
309static d_open_t	acpi_hp_hpcmi_open;
310static d_close_t acpi_hp_hpcmi_close;
311static d_read_t	acpi_hp_hpcmi_read;
312
313/* handler /dev/hpcmi device */
314static struct cdevsw hpcmi_cdevsw = {
315	.d_version = D_VERSION,
316	.d_open = acpi_hp_hpcmi_open,
317	.d_close = acpi_hp_hpcmi_close,
318	.d_read = acpi_hp_hpcmi_read,
319	.d_name = "hpcmi",
320};
321
322static device_method_t acpi_hp_methods[] = {
323	DEVMETHOD(device_identify, acpi_hp_identify),
324	DEVMETHOD(device_probe, acpi_hp_probe),
325	DEVMETHOD(device_attach, acpi_hp_attach),
326	DEVMETHOD(device_detach, acpi_hp_detach),
327
328	DEVMETHOD_END
329};
330
331static driver_t	acpi_hp_driver = {
332	"acpi_hp",
333	acpi_hp_methods,
334	sizeof(struct acpi_hp_softc),
335};
336
337static devclass_t acpi_hp_devclass;
338
339DRIVER_MODULE(acpi_hp, acpi_wmi, acpi_hp_driver, acpi_hp_devclass,
340		0, 0);
341MODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1);
342MODULE_DEPEND(acpi_hp, acpi, 1, 1, 1);
343
344static void
345acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc)
346{
347	int	wireless;
348	int	new_wlan_status;
349	int	new_bluetooth_status;
350	int	new_wwan_status;
351
352	wireless = acpi_hp_exec_wmi_command(sc->wmi_dev,
353		    ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
354	new_wlan_status = -1;
355	new_bluetooth_status = -1;
356	new_wwan_status = -1;
357
358	if (sc->verbose)
359		device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless);
360	if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO)
361	    &&  (wireless & HP_MASK_WLAN_ENABLED)) {
362		acpi_hp_exec_wmi_command(sc->wmi_dev,
363		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100);
364		new_wlan_status = 0;
365	}
366	else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO)
367		&&  !(wireless & HP_MASK_WLAN_ENABLED)) {
368		acpi_hp_exec_wmi_command(sc->wmi_dev,
369		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101);
370		new_wlan_status = 1;
371	}
372	if (sc->bluetooth_disable_if_radio_off &&
373	    !(wireless & HP_MASK_BLUETOOTH_RADIO) &&
374	    (wireless & HP_MASK_BLUETOOTH_ENABLED)) {
375		acpi_hp_exec_wmi_command(sc->wmi_dev,
376		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200);
377		new_bluetooth_status = 0;
378	}
379	else if (sc->bluetooth_enable_if_radio_on &&
380		(wireless & HP_MASK_BLUETOOTH_RADIO) &&
381		!(wireless & HP_MASK_BLUETOOTH_ENABLED)) {
382		acpi_hp_exec_wmi_command(sc->wmi_dev,
383		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202);
384		new_bluetooth_status = 1;
385	}
386	if (sc->wwan_disable_if_radio_off &&
387	    !(wireless & HP_MASK_WWAN_RADIO) &&
388	    (wireless & HP_MASK_WWAN_ENABLED)) {
389		acpi_hp_exec_wmi_command(sc->wmi_dev,
390		ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400);
391		new_wwan_status = 0;
392	}
393	else if (sc->wwan_enable_if_radio_on &&
394		(wireless & HP_MASK_WWAN_RADIO) &&
395		!(wireless & HP_MASK_WWAN_ENABLED)) {
396		acpi_hp_exec_wmi_command(sc->wmi_dev,
397		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404);
398		new_wwan_status = 1;
399	}
400
401	if (new_wlan_status == -1) {
402		new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR);
403		if ((new_wlan_status?1:0) != sc->was_wlan_on_air) {
404			sc->was_wlan_on_air = sc->was_wlan_on_air?0:1;
405			if (sc->verbose)
406				device_printf(sc->wmi_dev,
407			    	    "WLAN on air changed to %i "
408			    	    "(new_wlan_status is %i)\n",
409			    	    sc->was_wlan_on_air, new_wlan_status);
410			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
411			    0xc0+sc->was_wlan_on_air);
412		}
413	}
414	if (new_bluetooth_status == -1) {
415		new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR);
416		if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) {
417			sc->was_bluetooth_on_air = sc->was_bluetooth_on_air?
418			    0:1;
419			if (sc->verbose)
420				device_printf(sc->wmi_dev,
421				    "BLUETOOTH on air changed"
422				    " to %i (new_bluetooth_status is %i)\n",
423				    sc->was_bluetooth_on_air,
424				    new_bluetooth_status);
425			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
426			    0xd0+sc->was_bluetooth_on_air);
427		}
428	}
429	if (new_wwan_status == -1) {
430		new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR);
431		if ((new_wwan_status?1:0) != sc->was_wwan_on_air) {
432			sc->was_wwan_on_air = sc->was_wwan_on_air?0:1;
433			if (sc->verbose)
434				device_printf(sc->wmi_dev,
435				    "WWAN on air changed to %i"
436			    	    " (new_wwan_status is %i)\n",
437				    sc->was_wwan_on_air, new_wwan_status);
438			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
439			    0xe0+sc->was_wwan_on_air);
440		}
441	}
442}
443
444static void
445acpi_hp_identify(driver_t *driver, device_t parent)
446{
447
448	/* Don't do anything if driver is disabled. */
449	if (acpi_disabled("hp"))
450		return;
451
452	/* Add only a single device instance. */
453	if (device_find_child(parent, "acpi_hp", -1) != NULL)
454		return;
455
456	if (BUS_ADD_CHILD(parent, 0, "acpi_hp", -1) == NULL)
457		device_printf(parent, "add acpi_hp child failed\n");
458}
459
460static int
461acpi_hp_probe(device_t dev)
462{
463
464	device_set_desc(dev, "HP ACPI-WMI Mapping");
465	return (0);
466}
467
468static int
469acpi_hp_attach(device_t dev)
470{
471	struct acpi_hp_softc	*sc;
472	int			arg;
473
474	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
475
476	sc = device_get_softc(dev);
477	sc->dev = dev;
478	sc->has_notify = 0;
479	sc->has_cmi = 0;
480	sc->bluetooth_enable_if_radio_on = 0;
481	sc->bluetooth_disable_if_radio_off = 0;
482	sc->wlan_enable_if_radio_on = 0;
483	sc->wlan_disable_if_radio_off = 0;
484	sc->wlan_enable_if_radio_on = 0;
485	sc->wlan_disable_if_radio_off = 0;
486	sc->was_wlan_on_air = 0;
487	sc->was_bluetooth_on_air = 0;
488	sc->was_wwan_on_air = 0;
489	sc->cmi_detail = 0;
490	sc->cmi_order_size = -1;
491	sc->verbose = 0;
492	memset(sc->cmi_order, 0, sizeof(sc->cmi_order));
493
494	sc->wmi_dev = device_get_parent(dev);
495	if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
496	    ACPI_HP_WMI_BIOS_GUID)) {
497		device_printf(dev,
498		    "WMI device does not provide the HP BIOS GUID\n");
499		return (EINVAL);
500	}
501	if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
502	    ACPI_HP_WMI_EVENT_GUID)) {
503		device_printf(dev,
504		    "HP event GUID detected, installing event handler\n");
505		if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
506		    ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) {
507			device_printf(dev,
508			    "Could not install notification handler!\n");
509		}
510		else {
511			sc->has_notify = 1;
512		}
513	}
514	if ((sc->has_cmi =
515	    ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID)
516	    )) {
517		device_printf(dev, "HP CMI GUID detected\n");
518	}
519
520	if (sc->has_cmi) {
521		sc->hpcmi_dev_t = make_dev(&hpcmi_cdevsw, 0, UID_ROOT,
522			    GID_WHEEL, 0644, "hpcmi");
523		sc->hpcmi_dev_t->si_drv1 = sc;
524		sc->hpcmi_open_pid = 0;
525		sc->hpcmi_bufptr = -1;
526	}
527
528	ACPI_SERIAL_BEGIN(hp);
529
530	sc->sysctl_ctx = device_get_sysctl_ctx(dev);
531	sc->sysctl_tree = device_get_sysctl_tree(dev);
532	for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) {
533		arg = 0;
534		if ((!sc->has_notify &&
535		    (acpi_hp_sysctls[i].method ==
536			ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON ||
537		    acpi_hp_sysctls[i].method ==
538			ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF ||
539		    acpi_hp_sysctls[i].method ==
540			ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON ||
541		    acpi_hp_sysctls[i].method ==
542			ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF ||
543		    acpi_hp_sysctls[i].method ==
544			ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON ||
545		    acpi_hp_sysctls[i].method ==
546			ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) ||
547		    (arg = acpi_hp_sysctl_get(sc,
548		    acpi_hp_sysctls[i].method)) < 0) {
549			continue;
550		}
551		if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) {
552			sc->was_wlan_on_air = arg;
553		}
554		else if (acpi_hp_sysctls[i].method ==
555			    ACPI_HP_METHOD_BLUETOOTH_ON_AIR) {
556			sc->was_bluetooth_on_air = arg;
557		}
558		else if (acpi_hp_sysctls[i].method ==
559			    ACPI_HP_METHOD_WWAN_ON_AIR) {
560			sc->was_wwan_on_air = arg;
561		}
562
563		SYSCTL_ADD_PROC(sc->sysctl_ctx,
564		SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
565			acpi_hp_sysctls[i].name, acpi_hp_sysctls[i].access,
566			sc, i, acpi_hp_sysctl, "I",
567			acpi_hp_sysctls[i].description);
568	}
569	ACPI_SERIAL_END(hp);
570
571	return (0);
572}
573
574static int
575acpi_hp_detach(device_t dev)
576{
577	struct acpi_hp_softc *sc;
578
579	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
580	sc = device_get_softc(dev);
581	if (sc->has_cmi && sc->hpcmi_open_pid != 0)
582		return (EBUSY);
583
584	if (sc->has_notify)
585		ACPI_WMI_REMOVE_EVENT_HANDLER(dev, ACPI_HP_WMI_EVENT_GUID);
586
587	if (sc->has_cmi) {
588		if (sc->hpcmi_bufptr != -1) {
589			sbuf_delete(&sc->hpcmi_sbuf);
590			sc->hpcmi_bufptr = -1;
591		}
592		sc->hpcmi_open_pid = 0;
593		destroy_dev(sc->hpcmi_dev_t);
594	}
595
596	return (0);
597}
598
599static int
600acpi_hp_sysctl(SYSCTL_HANDLER_ARGS)
601{
602	struct acpi_hp_softc	*sc;
603	int			arg;
604	int			oldarg;
605	int			error = 0;
606	int			function;
607	int			method;
608
609	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
610
611	sc = (struct acpi_hp_softc *)oidp->oid_arg1;
612	function = oidp->oid_arg2;
613	method = acpi_hp_sysctls[function].method;
614
615	ACPI_SERIAL_BEGIN(hp);
616	arg = acpi_hp_sysctl_get(sc, method);
617	oldarg = arg;
618	error = sysctl_handle_int(oidp, &arg, 0, req);
619	if (!error && req->newptr != NULL) {
620		error = acpi_hp_sysctl_set(sc, method, arg, oldarg);
621	}
622	ACPI_SERIAL_END(hp);
623
624	return (error);
625}
626
627static int
628acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method)
629{
630	int	val = 0;
631
632	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
633	ACPI_SERIAL_ASSERT(hp);
634
635	switch (method) {
636	case ACPI_HP_METHOD_WLAN_ENABLED:
637		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
638			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
639		val = ((val & HP_MASK_WLAN_ENABLED) != 0);
640		break;
641	case ACPI_HP_METHOD_WLAN_RADIO:
642		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
643			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
644		val = ((val & HP_MASK_WLAN_RADIO) != 0);
645		break;
646	case ACPI_HP_METHOD_WLAN_ON_AIR:
647		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
648			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
649		val = ((val & HP_MASK_WLAN_ON_AIR) != 0);
650		break;
651	case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
652		val = sc->wlan_enable_if_radio_on;
653		break;
654	case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
655		val = sc->wlan_disable_if_radio_off;
656		break;
657	case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
658		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
659			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
660		val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0);
661		break;
662	case ACPI_HP_METHOD_BLUETOOTH_RADIO:
663		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
664			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
665		val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0);
666		break;
667	case ACPI_HP_METHOD_BLUETOOTH_ON_AIR:
668		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
669			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
670		val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0);
671		break;
672	case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
673		val = sc->bluetooth_enable_if_radio_on;
674		break;
675	case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
676		val = sc->bluetooth_disable_if_radio_off;
677		break;
678	case ACPI_HP_METHOD_WWAN_ENABLED:
679		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
680			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
681		val = ((val & HP_MASK_WWAN_ENABLED) != 0);
682		break;
683	case ACPI_HP_METHOD_WWAN_RADIO:
684		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
685			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
686		val = ((val & HP_MASK_WWAN_RADIO) != 0);
687		break;
688	case ACPI_HP_METHOD_WWAN_ON_AIR:
689		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
690			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
691		val = ((val & HP_MASK_WWAN_ON_AIR) != 0);
692		break;
693	case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
694		val = sc->wwan_enable_if_radio_on;
695		break;
696	case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
697		val = sc->wwan_disable_if_radio_off;
698		break;
699	case ACPI_HP_METHOD_ALS:
700		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
701			ACPI_HP_WMI_ALS_COMMAND, 0, 0);
702		break;
703	case ACPI_HP_METHOD_DISPLAY:
704		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
705			ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0);
706		break;
707	case ACPI_HP_METHOD_HDDTEMP:
708		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
709			ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0);
710		break;
711	case ACPI_HP_METHOD_DOCK:
712		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
713			ACPI_HP_WMI_DOCK_COMMAND, 0, 0);
714		break;
715	case ACPI_HP_METHOD_CMI_DETAIL:
716		val = sc->cmi_detail;
717		break;
718	case ACPI_HP_METHOD_VERBOSE:
719		val = sc->verbose;
720		break;
721	}
722
723	return (val);
724}
725
726static int
727acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg)
728{
729	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
730	ACPI_SERIAL_ASSERT(hp);
731
732	if (method != ACPI_HP_METHOD_CMI_DETAIL &&
733	    method != ACPI_HP_METHOD_VERBOSE)
734		arg = arg?1:0;
735
736	if (arg != oldarg) {
737		switch (method) {
738		case ACPI_HP_METHOD_WLAN_ENABLED:
739			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
740				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
741				    arg?0x101:0x100));
742		case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
743			sc->wlan_enable_if_radio_on = arg;
744			acpi_hp_evaluate_auto_on_off(sc);
745			break;
746		case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
747			sc->wlan_disable_if_radio_off = arg;
748			acpi_hp_evaluate_auto_on_off(sc);
749			break;
750		case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
751			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
752				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
753				    arg?0x202:0x200));
754		case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
755			sc->bluetooth_enable_if_radio_on = arg;
756			acpi_hp_evaluate_auto_on_off(sc);
757			break;
758		case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
759			sc->bluetooth_disable_if_radio_off = arg?1:0;
760			acpi_hp_evaluate_auto_on_off(sc);
761			break;
762		case ACPI_HP_METHOD_WWAN_ENABLED:
763			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
764				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
765				    arg?0x404:0x400));
766		case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
767			sc->wwan_enable_if_radio_on = arg?1:0;
768			acpi_hp_evaluate_auto_on_off(sc);
769			break;
770		case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
771			sc->wwan_disable_if_radio_off = arg?1:0;
772			acpi_hp_evaluate_auto_on_off(sc);
773			break;
774		case ACPI_HP_METHOD_ALS:
775			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
776				    ACPI_HP_WMI_ALS_COMMAND, 1,
777				    arg?1:0));
778		case ACPI_HP_METHOD_CMI_DETAIL:
779			sc->cmi_detail = arg;
780			if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) !=
781			    (oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) {
782			    sc->cmi_order_size = -1;
783			}
784			break;
785		case ACPI_HP_METHOD_VERBOSE:
786			sc->verbose = arg;
787			break;
788		}
789	}
790
791	return (0);
792}
793
794static __inline void
795acpi_hp_free_buffer(ACPI_BUFFER* buf) {
796	if (buf && buf->Pointer) {
797		AcpiOsFree(buf->Pointer);
798	}
799}
800
801static void
802acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context)
803{
804	device_t dev = context;
805	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
806
807	struct acpi_hp_softc *sc = device_get_softc(dev);
808	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
809	ACPI_OBJECT *obj;
810	ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
811	obj = (ACPI_OBJECT*) response.Pointer;
812	if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) {
813		if (*((UINT8 *) obj->Buffer.Pointer) == 0x5) {
814			acpi_hp_evaluate_auto_on_off(sc);
815		}
816	}
817	acpi_hp_free_buffer(&response);
818}
819
820static int
821acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val)
822{
823	UINT32		params[5] = { 0x55434553,
824			    is_write?2:1,
825			    command,
826			    is_write?4:0,
827			    val};
828	UINT32*		result;
829	ACPI_OBJECT	*obj;
830	ACPI_BUFFER	in = { sizeof(params), &params };
831	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
832	int retval;
833
834	if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID,
835		    0, 0x3, &in, &out))) {
836		acpi_hp_free_buffer(&out);
837		return (-EINVAL);
838	}
839	obj = out.Pointer;
840	if (!obj || obj->Type != ACPI_TYPE_BUFFER) {
841		acpi_hp_free_buffer(&out);
842		return (-EINVAL);
843	}
844	result = (UINT32*) obj->Buffer.Pointer;
845	retval = result[2];
846	if (result[1] > 0) {
847		retval = result[1];
848	}
849	acpi_hp_free_buffer(&out);
850
851	return (retval);
852}
853
854static __inline char*
855acpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) {
856	int	length;
857
858	dst[0] = 0;
859	if (obj->Type == ACPI_TYPE_STRING) {
860		length = obj->String.Length+1;
861		if (length > size) {
862			length = size - 1;
863		}
864		strlcpy(dst, obj->String.Pointer, length);
865		acpi_hp_hex_decode(dst);
866	}
867
868	return (dst);
869}
870
871
872/*
873 * Read BIOS Setting block in instance "instance".
874 * The block returned is ACPI_TYPE_PACKAGE which should contain the following
875 * elements:
876 * Index Meaning
877 * 0        Setting Name [string]
878 * 1        Value (comma separated, asterisk marks the current value) [string]
879 * 2        Path within the bios hierarchy [string]
880 * 3        IsReadOnly [int]
881 * 4        DisplayInUI [int]
882 * 5        RequiresPhysicalPresence [int]
883 * 6        Sequence for ordering within the bios settings (absolute) [int]
884 * 7        Length of prerequisites array [int]
885 * 8..8+[7] PrerequisiteN [string]
886 * 9+[7]    Current value (in case of enum) [string] / Array length [int]
887 * 10+[7]   Enum length [int] / Array values
888 * 11+[7]ff Enum value at index x [string]
889 */
890static int
891acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance,
892    char* outbuf, size_t outsize, UINT32* sequence, int detail)
893{
894	ACPI_OBJECT	*obj;
895	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
896	int		i;
897	int		outlen;
898	int		size = 255;
899	int		has_enums = 0;
900	int		valuebase = 0;
901	char		string_buffer[size];
902	int		enumbase;
903
904	outlen = 0;
905	outbuf[0] = 0;
906	if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) {
907		acpi_hp_free_buffer(&out);
908		return (-EINVAL);
909	}
910	obj = out.Pointer;
911	if (!obj || obj->Type != ACPI_TYPE_PACKAGE) {
912		acpi_hp_free_buffer(&out);
913		return (-EINVAL);
914	}
915
916	if (obj->Package.Count >= 8 &&
917	    obj->Package.Elements[7].Type == ACPI_TYPE_INTEGER) {
918	    valuebase = 8 + obj->Package.Elements[7].Integer.Value;
919	}
920
921	/* check if this matches our expectations based on limited knowledge */
922	if (valuebase > 7 && obj->Package.Count > valuebase + 1 &&
923	    obj->Package.Elements[0].Type == ACPI_TYPE_STRING &&
924	    obj->Package.Elements[1].Type == ACPI_TYPE_STRING &&
925	    obj->Package.Elements[2].Type == ACPI_TYPE_STRING &&
926	    obj->Package.Elements[3].Type == ACPI_TYPE_INTEGER &&
927	    obj->Package.Elements[4].Type == ACPI_TYPE_INTEGER &&
928	    obj->Package.Elements[5].Type == ACPI_TYPE_INTEGER &&
929	    obj->Package.Elements[6].Type == ACPI_TYPE_INTEGER &&
930	    obj->Package.Elements[valuebase].Type == ACPI_TYPE_STRING &&
931	    obj->Package.Elements[valuebase+1].Type == ACPI_TYPE_INTEGER &&
932	    obj->Package.Count > valuebase +
933	        obj->Package.Elements[valuebase+1].Integer.Value
934	   ) {
935		enumbase = valuebase + 1;
936		if (detail & ACPI_HP_CMI_DETAIL_PATHS) {
937			strlcat(outbuf, acpi_hp_get_string_from_object(
938				&obj->Package.Elements[2], string_buffer, size),
939				outsize);
940			outlen += 48;
941			while (strlen(outbuf) < outlen)
942				strlcat(outbuf, " ", outsize);
943		}
944		strlcat(outbuf, acpi_hp_get_string_from_object(
945				&obj->Package.Elements[0], string_buffer, size),
946				outsize);
947		outlen += 43;
948		while (strlen(outbuf) < outlen)
949			strlcat(outbuf, " ", outsize);
950		strlcat(outbuf, acpi_hp_get_string_from_object(
951				&obj->Package.Elements[valuebase], string_buffer,
952				size),
953				outsize);
954		outlen += 21;
955		while (strlen(outbuf) < outlen)
956			strlcat(outbuf, " ", outsize);
957		for (i = 0; i < strlen(outbuf); ++i)
958			if (outbuf[i] == '\\')
959				outbuf[i] = '/';
960		if (detail & ACPI_HP_CMI_DETAIL_ENUMS) {
961			for (i = enumbase + 1; i < enumbase + 1 +
962			    obj->Package.Elements[enumbase].Integer.Value;
963			    ++i) {
964				acpi_hp_get_string_from_object(
965				    &obj->Package.Elements[i], string_buffer,
966				    size);
967				if (strlen(string_buffer) > 1 ||
968				    (strlen(string_buffer) == 1 &&
969				    string_buffer[0] != ' ')) {
970					if (has_enums)
971						strlcat(outbuf, "/", outsize);
972					else
973						strlcat(outbuf, " (", outsize);
974					strlcat(outbuf, string_buffer, outsize);
975					has_enums = 1;
976				}
977			}
978		}
979		if (has_enums)
980			strlcat(outbuf, ")", outsize);
981		if (detail & ACPI_HP_CMI_DETAIL_FLAGS) {
982			strlcat(outbuf, obj->Package.Elements[3].Integer.Value?
983			    " [ReadOnly]":"", outsize);
984			strlcat(outbuf, obj->Package.Elements[4].Integer.Value?
985			    "":" [NOUI]", outsize);
986			strlcat(outbuf, obj->Package.Elements[5].Integer.Value?
987			    " [RPP]":"", outsize);
988		}
989		*sequence = (UINT32) obj->Package.Elements[6].Integer.Value;
990	}
991	acpi_hp_free_buffer(&out);
992
993	return (0);
994}
995
996
997
998/*
999 * Convert given two digit hex string (hexin) to an UINT8 referenced
1000 * by byteout.
1001 * Return != 0 if the was a problem (invalid input)
1002 */
1003static __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout)
1004{
1005	unsigned int	hi;
1006	unsigned int	lo;
1007
1008	hi = hexin[0];
1009	lo = hexin[1];
1010	if ('0' <= hi && hi <= '9')
1011		hi -= '0';
1012	else if ('A' <= hi && hi <= 'F')
1013		hi -= ('A' - 10);
1014	else if ('a' <= hi && hi <= 'f')
1015		hi -= ('a' - 10);
1016	else
1017		return (1);
1018	if ('0' <= lo && lo <= '9')
1019		lo -= '0';
1020	else if ('A' <= lo && lo <= 'F')
1021		lo -= ('A' - 10);
1022	else if ('a' <= lo && lo <= 'f')
1023		lo -= ('a' - 10);
1024	else
1025		return (1);
1026	*byteout = (hi << 4) + lo;
1027
1028	return (0);
1029}
1030
1031
1032static void
1033acpi_hp_hex_decode(char* buffer)
1034{
1035	int	i;
1036	int	length = strlen(buffer);
1037	UINT8	*uin;
1038	UINT8	uout;
1039
1040	if (((int)length/2)*2 == length || length < 10) return;
1041
1042	for (i = 0; i<length; ++i) {
1043		if (!((i+1)%3)) {
1044			if (buffer[i] != ' ')
1045				return;
1046		}
1047		else
1048			if (!((buffer[i] >= '0' && buffer[i] <= '9') ||
1049		    	    (buffer[i] >= 'A' && buffer[i] <= 'F')))
1050				return;
1051	}
1052
1053	for (i = 0; i<length; i += 3) {
1054		uin = &buffer[i];
1055		uout = 0;
1056		acpi_hp_hex_to_int(uin, &uout);
1057		buffer[i/3] = (char) uout;
1058	}
1059	buffer[(length+1)/3] = 0;
1060}
1061
1062
1063/*
1064 * open hpcmi device
1065 */
1066static int
1067acpi_hp_hpcmi_open(struct cdev* dev, int flags, int mode, struct thread *td)
1068{
1069	struct acpi_hp_softc	*sc;
1070	int			ret;
1071
1072	if (dev == NULL || dev->si_drv1 == NULL)
1073		return (EBADF);
1074	sc = dev->si_drv1;
1075
1076	ACPI_SERIAL_BEGIN(hp);
1077	if (sc->hpcmi_open_pid != 0) {
1078		ret = EBUSY;
1079	}
1080	else {
1081		if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND)
1082		    == NULL) {
1083			ret = ENXIO;
1084		} else {
1085			sc->hpcmi_open_pid = td->td_proc->p_pid;
1086			sc->hpcmi_bufptr = 0;
1087			ret = 0;
1088		}
1089	}
1090	ACPI_SERIAL_END(hp);
1091
1092	return (ret);
1093}
1094
1095/*
1096 * close hpcmi device
1097 */
1098static int
1099acpi_hp_hpcmi_close(struct cdev* dev, int flags, int mode, struct thread *td)
1100{
1101	struct acpi_hp_softc	*sc;
1102	int			ret;
1103
1104	if (dev == NULL || dev->si_drv1 == NULL)
1105		return (EBADF);
1106	sc = dev->si_drv1;
1107
1108	ACPI_SERIAL_BEGIN(hp);
1109	if (sc->hpcmi_open_pid == 0) {
1110		ret = EBADF;
1111	}
1112	else {
1113		if (sc->hpcmi_bufptr != -1) {
1114			sbuf_delete(&sc->hpcmi_sbuf);
1115			sc->hpcmi_bufptr = -1;
1116		}
1117		sc->hpcmi_open_pid = 0;
1118		ret = 0;
1119	}
1120	ACPI_SERIAL_END(hp);
1121
1122	return (ret);
1123}
1124
1125/*
1126 * Read from hpcmi bios information
1127 */
1128static int
1129acpi_hp_hpcmi_read(struct cdev *dev, struct uio *buf, int flag)
1130{
1131	struct acpi_hp_softc	*sc;
1132	int			pos, i, l, ret;
1133	UINT8			instance;
1134	UINT8			maxInstance;
1135	UINT32			sequence;
1136	int			linesize = 1025;
1137	char			line[linesize];
1138
1139	if (dev == NULL || dev->si_drv1 == NULL)
1140		return (EBADF);
1141	sc = dev->si_drv1;
1142
1143	ACPI_SERIAL_BEGIN(hp);
1144	if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid
1145	    || sc->hpcmi_bufptr == -1) {
1146		ret = EBADF;
1147	}
1148	else {
1149		if (!sbuf_done(&sc->hpcmi_sbuf)) {
1150			if (sc->cmi_order_size < 0) {
1151				maxInstance = sc->has_cmi;
1152				if (!(sc->cmi_detail &
1153				    ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) &&
1154				    maxInstance > 0) {
1155					maxInstance--;
1156				}
1157				sc->cmi_order_size = 0;
1158				for (instance = 0; instance < maxInstance;
1159				    ++instance) {
1160					if (acpi_hp_get_cmi_block(sc->wmi_dev,
1161						ACPI_HP_WMI_CMI_GUID, instance,
1162						line, linesize, &sequence,
1163						sc->cmi_detail)) {
1164						instance = maxInstance;
1165					}
1166					else {
1167						pos = sc->cmi_order_size;
1168						for (i=0;
1169						  i<sc->cmi_order_size && i<127;
1170						     ++i) {
1171				if (sc->cmi_order[i].sequence > sequence) {
1172								pos = i;
1173								break;
1174							}
1175						}
1176						for (i=sc->cmi_order_size;
1177						    i>pos;
1178						    --i) {
1179						sc->cmi_order[i].sequence =
1180						    sc->cmi_order[i-1].sequence;
1181						sc->cmi_order[i].instance =
1182						    sc->cmi_order[i-1].instance;
1183						}
1184						sc->cmi_order[pos].sequence =
1185						    sequence;
1186						sc->cmi_order[pos].instance =
1187						    instance;
1188						sc->cmi_order_size++;
1189					}
1190				}
1191			}
1192			for (i=0; i<sc->cmi_order_size; ++i) {
1193				if (!acpi_hp_get_cmi_block(sc->wmi_dev,
1194				    ACPI_HP_WMI_CMI_GUID,
1195				    sc->cmi_order[i].instance, line, linesize,
1196				    &sequence, sc->cmi_detail)) {
1197					sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line);
1198				}
1199			}
1200			sbuf_finish(&sc->hpcmi_sbuf);
1201		}
1202		if (sbuf_len(&sc->hpcmi_sbuf) <= 0) {
1203			sbuf_delete(&sc->hpcmi_sbuf);
1204			sc->hpcmi_bufptr = -1;
1205			sc->hpcmi_open_pid = 0;
1206			ret = ENOMEM;
1207		} else {
1208			l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) -
1209			    sc->hpcmi_bufptr);
1210			ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) +
1211			    sc->hpcmi_bufptr, l, buf) : 0;
1212			sc->hpcmi_bufptr += l;
1213		}
1214	}
1215	ACPI_SERIAL_END(hp);
1216
1217	return (ret);
1218}
1219