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	{0, 0}
328};
329
330static driver_t	acpi_hp_driver = {
331	"acpi_hp",
332	acpi_hp_methods,
333	sizeof(struct acpi_hp_softc),
334};
335
336static devclass_t acpi_hp_devclass;
337
338DRIVER_MODULE(acpi_hp, acpi_wmi, acpi_hp_driver, acpi_hp_devclass,
339		0, 0);
340MODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1);
341MODULE_DEPEND(acpi_hp, acpi, 1, 1, 1);
342
343static void
344acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc)
345{
346	int	wireless;
347	int	new_wlan_status;
348	int	new_bluetooth_status;
349	int	new_wwan_status;
350
351	wireless = acpi_hp_exec_wmi_command(sc->wmi_dev,
352		    ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
353	new_wlan_status = -1;
354	new_bluetooth_status = -1;
355	new_wwan_status = -1;
356
357	if (sc->verbose)
358		device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless);
359	if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO)
360	    &&  (wireless & HP_MASK_WLAN_ENABLED)) {
361		acpi_hp_exec_wmi_command(sc->wmi_dev,
362		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100);
363		new_wlan_status = 0;
364	}
365	else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO)
366		&&  !(wireless & HP_MASK_WLAN_ENABLED)) {
367		acpi_hp_exec_wmi_command(sc->wmi_dev,
368		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101);
369		new_wlan_status = 1;
370	}
371	if (sc->bluetooth_disable_if_radio_off &&
372	    !(wireless & HP_MASK_BLUETOOTH_RADIO) &&
373	    (wireless & HP_MASK_BLUETOOTH_ENABLED)) {
374		acpi_hp_exec_wmi_command(sc->wmi_dev,
375		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200);
376		new_bluetooth_status = 0;
377	}
378	else if (sc->bluetooth_enable_if_radio_on &&
379		(wireless & HP_MASK_BLUETOOTH_RADIO) &&
380		!(wireless & HP_MASK_BLUETOOTH_ENABLED)) {
381		acpi_hp_exec_wmi_command(sc->wmi_dev,
382		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202);
383		new_bluetooth_status = 1;
384	}
385	if (sc->wwan_disable_if_radio_off &&
386	    !(wireless & HP_MASK_WWAN_RADIO) &&
387	    (wireless & HP_MASK_WWAN_ENABLED)) {
388		acpi_hp_exec_wmi_command(sc->wmi_dev,
389		ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400);
390		new_wwan_status = 0;
391	}
392	else if (sc->wwan_enable_if_radio_on &&
393		(wireless & HP_MASK_WWAN_RADIO) &&
394		!(wireless & HP_MASK_WWAN_ENABLED)) {
395		acpi_hp_exec_wmi_command(sc->wmi_dev,
396		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404);
397		new_wwan_status = 1;
398	}
399
400	if (new_wlan_status == -1) {
401		new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR);
402		if ((new_wlan_status?1:0) != sc->was_wlan_on_air) {
403			sc->was_wlan_on_air = sc->was_wlan_on_air?0:1;
404			if (sc->verbose)
405				device_printf(sc->wmi_dev,
406			    	    "WLAN on air changed to %i "
407			    	    "(new_wlan_status is %i)\n",
408			    	    sc->was_wlan_on_air, new_wlan_status);
409			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
410			    0xc0+sc->was_wlan_on_air);
411		}
412	}
413	if (new_bluetooth_status == -1) {
414		new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR);
415		if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) {
416			sc->was_bluetooth_on_air = sc->was_bluetooth_on_air?
417			    0:1;
418			if (sc->verbose)
419				device_printf(sc->wmi_dev,
420				    "BLUETOOTH on air changed"
421				    " to %i (new_bluetooth_status is %i)\n",
422				    sc->was_bluetooth_on_air,
423				    new_bluetooth_status);
424			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
425			    0xd0+sc->was_bluetooth_on_air);
426		}
427	}
428	if (new_wwan_status == -1) {
429		new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR);
430		if ((new_wwan_status?1:0) != sc->was_wwan_on_air) {
431			sc->was_wwan_on_air = sc->was_wwan_on_air?0:1;
432			if (sc->verbose)
433				device_printf(sc->wmi_dev,
434				    "WWAN on air changed to %i"
435			    	    " (new_wwan_status is %i)\n",
436				    sc->was_wwan_on_air, new_wwan_status);
437			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
438			    0xe0+sc->was_wwan_on_air);
439		}
440	}
441}
442
443static void
444acpi_hp_identify(driver_t *driver, device_t parent)
445{
446
447	/* Don't do anything if driver is disabled. */
448	if (acpi_disabled("hp"))
449		return;
450
451	/* Add only a single device instance. */
452	if (device_find_child(parent, "acpi_hp", -1) != NULL)
453		return;
454
455	if (BUS_ADD_CHILD(parent, 0, "acpi_hp", -1) == NULL)
456		device_printf(parent, "add acpi_hp child failed\n");
457}
458
459static int
460acpi_hp_probe(device_t dev)
461{
462
463	device_set_desc(dev, "HP ACPI-WMI Mapping");
464	return (0);
465}
466
467static int
468acpi_hp_attach(device_t dev)
469{
470	struct acpi_hp_softc	*sc;
471	int			arg;
472
473	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
474
475	sc = device_get_softc(dev);
476	sc->dev = dev;
477	sc->has_notify = 0;
478	sc->has_cmi = 0;
479	sc->bluetooth_enable_if_radio_on = 0;
480	sc->bluetooth_disable_if_radio_off = 0;
481	sc->wlan_enable_if_radio_on = 0;
482	sc->wlan_disable_if_radio_off = 0;
483	sc->wlan_enable_if_radio_on = 0;
484	sc->wlan_disable_if_radio_off = 0;
485	sc->was_wlan_on_air = 0;
486	sc->was_bluetooth_on_air = 0;
487	sc->was_wwan_on_air = 0;
488	sc->cmi_detail = 0;
489	sc->cmi_order_size = -1;
490	sc->verbose = 0;
491	memset(sc->cmi_order, 0, sizeof(sc->cmi_order));
492
493	sc->wmi_dev = device_get_parent(dev);
494	if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
495	    ACPI_HP_WMI_BIOS_GUID)) {
496		device_printf(dev,
497		    "WMI device does not provide the HP BIOS GUID\n");
498		return (EINVAL);
499	}
500	if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
501	    ACPI_HP_WMI_EVENT_GUID)) {
502		device_printf(dev,
503		    "HP event GUID detected, installing event handler\n");
504		if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
505		    ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) {
506			device_printf(dev,
507			    "Could not install notification handler!\n");
508		}
509		else {
510			sc->has_notify = 1;
511		}
512	}
513	if ((sc->has_cmi =
514	    ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID)
515	    )) {
516		device_printf(dev, "HP CMI GUID detected\n");
517	}
518
519	if (sc->has_cmi) {
520		sc->hpcmi_dev_t = make_dev(&hpcmi_cdevsw, 0, UID_ROOT,
521			    GID_WHEEL, 0644, "hpcmi");
522		sc->hpcmi_dev_t->si_drv1 = sc;
523		sc->hpcmi_open_pid = 0;
524		sc->hpcmi_bufptr = -1;
525	}
526
527	ACPI_SERIAL_BEGIN(hp);
528
529	sc->sysctl_ctx = device_get_sysctl_ctx(dev);
530	sc->sysctl_tree = device_get_sysctl_tree(dev);
531	for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) {
532		arg = 0;
533		if ((!sc->has_notify &&
534		    (acpi_hp_sysctls[i].method ==
535			ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON ||
536		    acpi_hp_sysctls[i].method ==
537			ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF ||
538		    acpi_hp_sysctls[i].method ==
539			ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON ||
540		    acpi_hp_sysctls[i].method ==
541			ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF ||
542		    acpi_hp_sysctls[i].method ==
543			ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON ||
544		    acpi_hp_sysctls[i].method ==
545			ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) ||
546		    (arg = acpi_hp_sysctl_get(sc,
547		    acpi_hp_sysctls[i].method)) < 0) {
548			continue;
549		}
550		if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) {
551			sc->was_wlan_on_air = arg;
552		}
553		else if (acpi_hp_sysctls[i].method ==
554			    ACPI_HP_METHOD_BLUETOOTH_ON_AIR) {
555			sc->was_bluetooth_on_air = arg;
556		}
557		else if (acpi_hp_sysctls[i].method ==
558			    ACPI_HP_METHOD_WWAN_ON_AIR) {
559			sc->was_wwan_on_air = arg;
560		}
561
562		SYSCTL_ADD_PROC(sc->sysctl_ctx,
563		SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
564			acpi_hp_sysctls[i].name, acpi_hp_sysctls[i].access,
565			sc, i, acpi_hp_sysctl, "I",
566			acpi_hp_sysctls[i].description);
567	}
568	ACPI_SERIAL_END(hp);
569
570	return (0);
571}
572
573static int
574acpi_hp_detach(device_t dev)
575{
576	struct acpi_hp_softc *sc;
577
578	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
579	sc = device_get_softc(dev);
580	if (sc->has_cmi && sc->hpcmi_open_pid != 0)
581		return (EBUSY);
582
583	if (sc->has_notify)
584		ACPI_WMI_REMOVE_EVENT_HANDLER(dev, ACPI_HP_WMI_EVENT_GUID);
585
586	if (sc->has_cmi) {
587		if (sc->hpcmi_bufptr != -1) {
588			sbuf_delete(&sc->hpcmi_sbuf);
589			sc->hpcmi_bufptr = -1;
590		}
591		sc->hpcmi_open_pid = 0;
592		destroy_dev(sc->hpcmi_dev_t);
593	}
594
595	return (0);
596}
597
598static int
599acpi_hp_sysctl(SYSCTL_HANDLER_ARGS)
600{
601	struct acpi_hp_softc	*sc;
602	int			arg;
603	int			oldarg;
604	int			error = 0;
605	int			function;
606	int			method;
607
608	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
609
610	sc = (struct acpi_hp_softc *)oidp->oid_arg1;
611	function = oidp->oid_arg2;
612	method = acpi_hp_sysctls[function].method;
613
614	ACPI_SERIAL_BEGIN(hp);
615	arg = acpi_hp_sysctl_get(sc, method);
616	oldarg = arg;
617	error = sysctl_handle_int(oidp, &arg, 0, req);
618	if (!error && req->newptr != NULL) {
619		error = acpi_hp_sysctl_set(sc, method, arg, oldarg);
620	}
621	ACPI_SERIAL_END(hp);
622
623	return (error);
624}
625
626static int
627acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method)
628{
629	int	val = 0;
630
631	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
632	ACPI_SERIAL_ASSERT(hp);
633
634	switch (method) {
635	case ACPI_HP_METHOD_WLAN_ENABLED:
636		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
637			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
638		val = ((val & HP_MASK_WLAN_ENABLED) != 0);
639		break;
640	case ACPI_HP_METHOD_WLAN_RADIO:
641		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
642			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
643		val = ((val & HP_MASK_WLAN_RADIO) != 0);
644		break;
645	case ACPI_HP_METHOD_WLAN_ON_AIR:
646		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
647			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
648		val = ((val & HP_MASK_WLAN_ON_AIR) != 0);
649		break;
650	case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
651		val = sc->wlan_enable_if_radio_on;
652		break;
653	case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
654		val = sc->wlan_disable_if_radio_off;
655		break;
656	case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
657		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
658			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
659		val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0);
660		break;
661	case ACPI_HP_METHOD_BLUETOOTH_RADIO:
662		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
663			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
664		val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0);
665		break;
666	case ACPI_HP_METHOD_BLUETOOTH_ON_AIR:
667		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
668			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
669		val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0);
670		break;
671	case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
672		val = sc->bluetooth_enable_if_radio_on;
673		break;
674	case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
675		val = sc->bluetooth_disable_if_radio_off;
676		break;
677	case ACPI_HP_METHOD_WWAN_ENABLED:
678		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
679			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
680		val = ((val & HP_MASK_WWAN_ENABLED) != 0);
681		break;
682	case ACPI_HP_METHOD_WWAN_RADIO:
683		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
684			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
685		val = ((val & HP_MASK_WWAN_RADIO) != 0);
686		break;
687	case ACPI_HP_METHOD_WWAN_ON_AIR:
688		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
689			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
690		val = ((val & HP_MASK_WWAN_ON_AIR) != 0);
691		break;
692	case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
693		val = sc->wwan_enable_if_radio_on;
694		break;
695	case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
696		val = sc->wwan_disable_if_radio_off;
697		break;
698	case ACPI_HP_METHOD_ALS:
699		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
700			ACPI_HP_WMI_ALS_COMMAND, 0, 0);
701		break;
702	case ACPI_HP_METHOD_DISPLAY:
703		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
704			ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0);
705		break;
706	case ACPI_HP_METHOD_HDDTEMP:
707		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
708			ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0);
709		break;
710	case ACPI_HP_METHOD_DOCK:
711		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
712			ACPI_HP_WMI_DOCK_COMMAND, 0, 0);
713		break;
714	case ACPI_HP_METHOD_CMI_DETAIL:
715		val = sc->cmi_detail;
716		break;
717	case ACPI_HP_METHOD_VERBOSE:
718		val = sc->verbose;
719		break;
720	}
721
722	return (val);
723}
724
725static int
726acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg)
727{
728	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
729	ACPI_SERIAL_ASSERT(hp);
730
731	if (method != ACPI_HP_METHOD_CMI_DETAIL &&
732	    method != ACPI_HP_METHOD_VERBOSE)
733		arg = arg?1:0;
734
735	if (arg != oldarg) {
736		switch (method) {
737		case ACPI_HP_METHOD_WLAN_ENABLED:
738			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
739				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
740				    arg?0x101:0x100));
741		case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
742			sc->wlan_enable_if_radio_on = arg;
743			acpi_hp_evaluate_auto_on_off(sc);
744			break;
745		case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
746			sc->wlan_disable_if_radio_off = arg;
747			acpi_hp_evaluate_auto_on_off(sc);
748			break;
749		case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
750			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
751				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
752				    arg?0x202:0x200));
753		case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
754			sc->bluetooth_enable_if_radio_on = arg;
755			acpi_hp_evaluate_auto_on_off(sc);
756			break;
757		case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
758			sc->bluetooth_disable_if_radio_off = arg?1:0;
759			acpi_hp_evaluate_auto_on_off(sc);
760			break;
761		case ACPI_HP_METHOD_WWAN_ENABLED:
762			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
763				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
764				    arg?0x404:0x400));
765		case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
766			sc->wwan_enable_if_radio_on = arg?1:0;
767			acpi_hp_evaluate_auto_on_off(sc);
768			break;
769		case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
770			sc->wwan_disable_if_radio_off = arg?1:0;
771			acpi_hp_evaluate_auto_on_off(sc);
772			break;
773		case ACPI_HP_METHOD_ALS:
774			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
775				    ACPI_HP_WMI_ALS_COMMAND, 1,
776				    arg?1:0));
777		case ACPI_HP_METHOD_CMI_DETAIL:
778			sc->cmi_detail = arg;
779			if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) !=
780			    (oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) {
781			    sc->cmi_order_size = -1;
782			}
783			break;
784		case ACPI_HP_METHOD_VERBOSE:
785			sc->verbose = arg;
786			break;
787		}
788	}
789
790	return (0);
791}
792
793static __inline void
794acpi_hp_free_buffer(ACPI_BUFFER* buf) {
795	if (buf && buf->Pointer) {
796		AcpiOsFree(buf->Pointer);
797	}
798}
799
800static void
801acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context)
802{
803	device_t dev = context;
804	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
805
806	struct acpi_hp_softc *sc = device_get_softc(dev);
807	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
808	ACPI_OBJECT *obj;
809	ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
810	obj = (ACPI_OBJECT*) response.Pointer;
811	if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) {
812		if (*((UINT8 *) obj->Buffer.Pointer) == 0x5) {
813			acpi_hp_evaluate_auto_on_off(sc);
814		}
815	}
816	acpi_hp_free_buffer(&response);
817}
818
819static int
820acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val)
821{
822	UINT32		params[5] = { 0x55434553,
823			    is_write?2:1,
824			    command,
825			    is_write?4:0,
826			    val};
827	UINT32*		result;
828	ACPI_OBJECT	*obj;
829	ACPI_BUFFER	in = { sizeof(params), &params };
830	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
831	int retval;
832
833	if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID,
834		    0, 0x3, &in, &out))) {
835		acpi_hp_free_buffer(&out);
836		return (-EINVAL);
837	}
838	obj = out.Pointer;
839	if (!obj || obj->Type != ACPI_TYPE_BUFFER) {
840		acpi_hp_free_buffer(&out);
841		return (-EINVAL);
842	}
843	result = (UINT32*) obj->Buffer.Pointer;
844	retval = result[2];
845	if (result[1] > 0) {
846		retval = result[1];
847	}
848	acpi_hp_free_buffer(&out);
849
850	return (retval);
851}
852
853static __inline char*
854acpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) {
855	int	length;
856
857	dst[0] = 0;
858	if (obj->Type == ACPI_TYPE_STRING) {
859		length = obj->String.Length+1;
860		if (length > size) {
861			length = size - 1;
862		}
863		strlcpy(dst, obj->String.Pointer, length);
864		acpi_hp_hex_decode(dst);
865	}
866
867	return (dst);
868}
869
870
871/*
872 * Read BIOS Setting block in instance "instance".
873 * The block returned is ACPI_TYPE_PACKAGE which should contain the following
874 * elements:
875 * Index Meaning
876 * 0        Setting Name [string]
877 * 1        Value (comma separated, asterisk marks the current value) [string]
878 * 2        Path within the bios hierarchy [string]
879 * 3        IsReadOnly [int]
880 * 4        DisplayInUI [int]
881 * 5        RequiresPhysicalPresence [int]
882 * 6        Sequence for ordering within the bios settings (absolute) [int]
883 * 7        Length of prerequisites array [int]
884 * 8..8+[7] PrerequisiteN [string]
885 * 9+[7]    Current value (in case of enum) [string] / Array length [int]
886 * 10+[7]   Enum length [int] / Array values
887 * 11+[7]ff Enum value at index x [string]
888 */
889static int
890acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance,
891    char* outbuf, size_t outsize, UINT32* sequence, int detail)
892{
893	ACPI_OBJECT	*obj;
894	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
895	int		i;
896	int		outlen;
897	int		size = 255;
898	int		has_enums = 0;
899	int		valuebase = 0;
900	char		string_buffer[size];
901	int		enumbase;
902
903	outlen = 0;
904	outbuf[0] = 0;
905	if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) {
906		acpi_hp_free_buffer(&out);
907		return (-EINVAL);
908	}
909	obj = out.Pointer;
910	if (!obj || obj->Type != ACPI_TYPE_PACKAGE) {
911		acpi_hp_free_buffer(&out);
912		return (-EINVAL);
913	}
914
915	if (obj->Package.Count >= 8 &&
916	    obj->Package.Elements[7].Type == ACPI_TYPE_INTEGER) {
917	    valuebase = 8 + obj->Package.Elements[7].Integer.Value;
918	}
919
920	/* check if this matches our expectations based on limited knowledge */
921	if (valuebase > 7 && obj->Package.Count > valuebase + 1 &&
922	    obj->Package.Elements[0].Type == ACPI_TYPE_STRING &&
923	    obj->Package.Elements[1].Type == ACPI_TYPE_STRING &&
924	    obj->Package.Elements[2].Type == ACPI_TYPE_STRING &&
925	    obj->Package.Elements[3].Type == ACPI_TYPE_INTEGER &&
926	    obj->Package.Elements[4].Type == ACPI_TYPE_INTEGER &&
927	    obj->Package.Elements[5].Type == ACPI_TYPE_INTEGER &&
928	    obj->Package.Elements[6].Type == ACPI_TYPE_INTEGER &&
929	    obj->Package.Elements[valuebase].Type == ACPI_TYPE_STRING &&
930	    obj->Package.Elements[valuebase+1].Type == ACPI_TYPE_INTEGER &&
931	    obj->Package.Count > valuebase +
932	        obj->Package.Elements[valuebase+1].Integer.Value
933	   ) {
934		enumbase = valuebase + 1;
935		if (detail & ACPI_HP_CMI_DETAIL_PATHS) {
936			strlcat(outbuf, acpi_hp_get_string_from_object(
937				&obj->Package.Elements[2], string_buffer, size),
938				outsize);
939			outlen += 48;
940			while (strlen(outbuf) < outlen)
941				strlcat(outbuf, " ", outsize);
942		}
943		strlcat(outbuf, acpi_hp_get_string_from_object(
944				&obj->Package.Elements[0], string_buffer, size),
945				outsize);
946		outlen += 43;
947		while (strlen(outbuf) < outlen)
948			strlcat(outbuf, " ", outsize);
949		strlcat(outbuf, acpi_hp_get_string_from_object(
950				&obj->Package.Elements[valuebase], string_buffer,
951				size),
952				outsize);
953		outlen += 21;
954		while (strlen(outbuf) < outlen)
955			strlcat(outbuf, " ", outsize);
956		for (i = 0; i < strlen(outbuf); ++i)
957			if (outbuf[i] == '\\')
958				outbuf[i] = '/';
959		if (detail & ACPI_HP_CMI_DETAIL_ENUMS) {
960			for (i = enumbase + 1; i < enumbase + 1 +
961			    obj->Package.Elements[enumbase].Integer.Value;
962			    ++i) {
963				acpi_hp_get_string_from_object(
964				    &obj->Package.Elements[i], string_buffer,
965				    size);
966				if (strlen(string_buffer) > 1 ||
967				    (strlen(string_buffer) == 1 &&
968				    string_buffer[0] != ' ')) {
969					if (has_enums)
970						strlcat(outbuf, "/", outsize);
971					else
972						strlcat(outbuf, " (", outsize);
973					strlcat(outbuf, string_buffer, outsize);
974					has_enums = 1;
975				}
976			}
977		}
978		if (has_enums)
979			strlcat(outbuf, ")", outsize);
980		if (detail & ACPI_HP_CMI_DETAIL_FLAGS) {
981			strlcat(outbuf, obj->Package.Elements[3].Integer.Value?
982			    " [ReadOnly]":"", outsize);
983			strlcat(outbuf, obj->Package.Elements[4].Integer.Value?
984			    "":" [NOUI]", outsize);
985			strlcat(outbuf, obj->Package.Elements[5].Integer.Value?
986			    " [RPP]":"", outsize);
987		}
988		*sequence = (UINT32) obj->Package.Elements[6].Integer.Value;
989	}
990	acpi_hp_free_buffer(&out);
991
992	return (0);
993}
994
995
996
997/*
998 * Convert given two digit hex string (hexin) to an UINT8 referenced
999 * by byteout.
1000 * Return != 0 if the was a problem (invalid input)
1001 */
1002static __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout)
1003{
1004	unsigned int	hi;
1005	unsigned int	lo;
1006
1007	hi = hexin[0];
1008	lo = hexin[1];
1009	if ('0' <= hi && hi <= '9')
1010		hi -= '0';
1011	else if ('A' <= hi && hi <= 'F')
1012		hi -= ('A' - 10);
1013	else if ('a' <= hi && hi <= 'f')
1014		hi -= ('a' - 10);
1015	else
1016		return (1);
1017	if ('0' <= lo && lo <= '9')
1018		lo -= '0';
1019	else if ('A' <= lo && lo <= 'F')
1020		lo -= ('A' - 10);
1021	else if ('a' <= lo && lo <= 'f')
1022		lo -= ('a' - 10);
1023	else
1024		return (1);
1025	*byteout = (hi << 4) + lo;
1026
1027	return (0);
1028}
1029
1030
1031static void
1032acpi_hp_hex_decode(char* buffer)
1033{
1034	int	i;
1035	int	length = strlen(buffer);
1036	UINT8	*uin;
1037	UINT8	uout;
1038
1039	if (((int)length/2)*2 == length || length < 10) return;
1040
1041	for (i = 0; i<length; ++i) {
1042		if (!((i+1)%3)) {
1043			if (buffer[i] != ' ')
1044				return;
1045		}
1046		else
1047			if (!((buffer[i] >= '0' && buffer[i] <= '9') ||
1048		    	    (buffer[i] >= 'A' && buffer[i] <= 'F')))
1049				return;
1050	}
1051
1052	for (i = 0; i<length; i += 3) {
1053		uin = &buffer[i];
1054		uout = 0;
1055		acpi_hp_hex_to_int(uin, &uout);
1056		buffer[i/3] = (char) uout;
1057	}
1058	buffer[(length+1)/3] = 0;
1059}
1060
1061
1062/*
1063 * open hpcmi device
1064 */
1065static int
1066acpi_hp_hpcmi_open(struct cdev* dev, int flags, int mode, struct thread *td)
1067{
1068	struct acpi_hp_softc	*sc;
1069	int			ret;
1070
1071	if (dev == NULL || dev->si_drv1 == NULL)
1072		return (EBADF);
1073	sc = dev->si_drv1;
1074
1075	ACPI_SERIAL_BEGIN(hp);
1076	if (sc->hpcmi_open_pid != 0) {
1077		ret = EBUSY;
1078	}
1079	else {
1080		if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND)
1081		    == NULL) {
1082			ret = ENXIO;
1083		} else {
1084			sc->hpcmi_open_pid = td->td_proc->p_pid;
1085			sc->hpcmi_bufptr = 0;
1086			ret = 0;
1087		}
1088	}
1089	ACPI_SERIAL_END(hp);
1090
1091	return (ret);
1092}
1093
1094/*
1095 * close hpcmi device
1096 */
1097static int
1098acpi_hp_hpcmi_close(struct cdev* dev, int flags, int mode, struct thread *td)
1099{
1100	struct acpi_hp_softc	*sc;
1101	int			ret;
1102
1103	if (dev == NULL || dev->si_drv1 == NULL)
1104		return (EBADF);
1105	sc = dev->si_drv1;
1106
1107	ACPI_SERIAL_BEGIN(hp);
1108	if (sc->hpcmi_open_pid == 0) {
1109		ret = EBADF;
1110	}
1111	else {
1112		if (sc->hpcmi_bufptr != -1) {
1113			sbuf_delete(&sc->hpcmi_sbuf);
1114			sc->hpcmi_bufptr = -1;
1115		}
1116		sc->hpcmi_open_pid = 0;
1117		ret = 0;
1118	}
1119	ACPI_SERIAL_END(hp);
1120
1121	return (ret);
1122}
1123
1124/*
1125 * Read from hpcmi bios information
1126 */
1127static int
1128acpi_hp_hpcmi_read(struct cdev *dev, struct uio *buf, int flag)
1129{
1130	struct acpi_hp_softc	*sc;
1131	int			pos, i, l, ret;
1132	UINT8			instance;
1133	UINT8			maxInstance;
1134	UINT32			sequence;
1135	int			linesize = 1025;
1136	char			line[linesize];
1137
1138	if (dev == NULL || dev->si_drv1 == NULL)
1139		return (EBADF);
1140	sc = dev->si_drv1;
1141
1142	ACPI_SERIAL_BEGIN(hp);
1143	if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid
1144	    || sc->hpcmi_bufptr == -1) {
1145		ret = EBADF;
1146	}
1147	else {
1148		if (!sbuf_done(&sc->hpcmi_sbuf)) {
1149			if (sc->cmi_order_size < 0) {
1150				maxInstance = sc->has_cmi;
1151				if (!(sc->cmi_detail &
1152				    ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) &&
1153				    maxInstance > 0) {
1154					maxInstance--;
1155				}
1156				sc->cmi_order_size = 0;
1157				for (instance = 0; instance < maxInstance;
1158				    ++instance) {
1159					if (acpi_hp_get_cmi_block(sc->wmi_dev,
1160						ACPI_HP_WMI_CMI_GUID, instance,
1161						line, linesize, &sequence,
1162						sc->cmi_detail)) {
1163						instance = maxInstance;
1164					}
1165					else {
1166						pos = sc->cmi_order_size;
1167						for (i=0;
1168						  i<sc->cmi_order_size && i<127;
1169						     ++i) {
1170				if (sc->cmi_order[i].sequence > sequence) {
1171								pos = i;
1172								break;
1173							}
1174						}
1175						for (i=sc->cmi_order_size;
1176						    i>pos;
1177						    --i) {
1178						sc->cmi_order[i].sequence =
1179						    sc->cmi_order[i-1].sequence;
1180						sc->cmi_order[i].instance =
1181						    sc->cmi_order[i-1].instance;
1182						}
1183						sc->cmi_order[pos].sequence =
1184						    sequence;
1185						sc->cmi_order[pos].instance =
1186						    instance;
1187						sc->cmi_order_size++;
1188					}
1189				}
1190			}
1191			for (i=0; i<sc->cmi_order_size; ++i) {
1192				if (!acpi_hp_get_cmi_block(sc->wmi_dev,
1193				    ACPI_HP_WMI_CMI_GUID,
1194				    sc->cmi_order[i].instance, line, linesize,
1195				    &sequence, sc->cmi_detail)) {
1196					sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line);
1197				}
1198			}
1199			sbuf_finish(&sc->hpcmi_sbuf);
1200		}
1201		if (sbuf_len(&sc->hpcmi_sbuf) <= 0) {
1202			sbuf_delete(&sc->hpcmi_sbuf);
1203			sc->hpcmi_bufptr = -1;
1204			sc->hpcmi_open_pid = 0;
1205			ret = ENOMEM;
1206		} else {
1207			l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) -
1208			    sc->hpcmi_bufptr);
1209			ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) +
1210			    sc->hpcmi_bufptr, l, buf) : 0;
1211			sc->hpcmi_bufptr += l;
1212		}
1213	}
1214	ACPI_SERIAL_END(hp);
1215
1216	return (ret);
1217}
1218