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