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