xref: /dflybsd-src/sys/dev/acpica/acpi_hp/acpi_hp.c (revision ed183f8c2f9bb14cbccb8377f3cdd29e0971d8a0)
15db2f26eSSascha Wildner /*-
25db2f26eSSascha Wildner  * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de>
35db2f26eSSascha Wildner  * All rights reserved.
45db2f26eSSascha Wildner  *
55db2f26eSSascha Wildner  * Redistribution and use in source and binary forms, with or without
65db2f26eSSascha Wildner  * modification, are permitted provided that the following conditions
75db2f26eSSascha Wildner  * are met:
85db2f26eSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
95db2f26eSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
105db2f26eSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
115db2f26eSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
125db2f26eSSascha Wildner  *    documentation and/or other materials provided with the distribution.
135db2f26eSSascha Wildner  *
145db2f26eSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
155db2f26eSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
165db2f26eSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
175db2f26eSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
185db2f26eSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
195db2f26eSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
205db2f26eSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
215db2f26eSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
225db2f26eSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
235db2f26eSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
245db2f26eSSascha Wildner  * SUCH DAMAGE.
255db2f26eSSascha Wildner  *
265db2f26eSSascha Wildner  * $FreeBSD: src/sys/dev/acpi_support/acpi_hp.c,v 1.7 2010/09/11 08:09:14 avg Exp $
275db2f26eSSascha Wildner  */
285db2f26eSSascha Wildner 
295db2f26eSSascha Wildner /*
305db2f26eSSascha Wildner  * Driver for extra ACPI-controlled features found on HP laptops
315db2f26eSSascha Wildner  * that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p).
325db2f26eSSascha Wildner  * Allows to control and read status of integrated hardware and read
335db2f26eSSascha Wildner  * BIOS settings through CMI.
345db2f26eSSascha Wildner  * Inspired by the hp-wmi driver, which implements a subset of these
355db2f26eSSascha Wildner  * features (hotkeys) on Linux.
365db2f26eSSascha Wildner  *
375db2f26eSSascha Wildner  * HP CMI whitepaper:
385db2f26eSSascha Wildner  *     http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf
395db2f26eSSascha Wildner  * wmi-hp for Linux:
405db2f26eSSascha Wildner  *     http://www.kernel.org
415db2f26eSSascha Wildner  * WMI and ACPI:
425db2f26eSSascha Wildner  *     http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
435db2f26eSSascha Wildner  */
445db2f26eSSascha Wildner 
455db2f26eSSascha Wildner #include "opt_acpi.h"
465db2f26eSSascha Wildner #include <sys/param.h>
475db2f26eSSascha Wildner #include <sys/conf.h>
485db2f26eSSascha Wildner #include <sys/uio.h>
495db2f26eSSascha Wildner #include <sys/proc.h>
505db2f26eSSascha Wildner #include <sys/kernel.h>
515db2f26eSSascha Wildner #include <sys/bus.h>
525db2f26eSSascha Wildner #include <sys/sbuf.h>
535db2f26eSSascha Wildner #include <sys/module.h>
545db2f26eSSascha Wildner #include <sys/sysctl.h>
555db2f26eSSascha Wildner #include <sys/device.h>
565db2f26eSSascha Wildner 
575db2f26eSSascha Wildner #include "acpi.h"
585db2f26eSSascha Wildner #include "accommon.h"
595db2f26eSSascha Wildner #include <dev/acpica/acpivar.h>
605db2f26eSSascha Wildner #include "acpi_wmi_if.h"
615db2f26eSSascha Wildner 
625db2f26eSSascha Wildner #define _COMPONENT	ACPI_OEM
635db2f26eSSascha Wildner ACPI_MODULE_NAME("HP")
645db2f26eSSascha Wildner 
655db2f26eSSascha Wildner #define ACPI_HP_WMI_EVENT_GUID		"95F24279-4D7B-4334-9387-ACCDC67EF61C"
665db2f26eSSascha Wildner #define ACPI_HP_WMI_BIOS_GUID		"5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
675db2f26eSSascha Wildner #define ACPI_HP_WMI_CMI_GUID		"2D114B49-2DFB-4130-B8FE-4A3C09E75133"
685db2f26eSSascha Wildner 
695db2f26eSSascha Wildner #define ACPI_HP_WMI_DISPLAY_COMMAND	0x1
705db2f26eSSascha Wildner #define ACPI_HP_WMI_HDDTEMP_COMMAND	0x2
715db2f26eSSascha Wildner #define ACPI_HP_WMI_ALS_COMMAND		0x3
725db2f26eSSascha Wildner #define ACPI_HP_WMI_DOCK_COMMAND	0x4
735db2f26eSSascha Wildner #define ACPI_HP_WMI_WIRELESS_COMMAND	0x5
745db2f26eSSascha Wildner 
755db2f26eSSascha Wildner #define ACPI_HP_METHOD_WLAN_ENABLED			1
765db2f26eSSascha Wildner #define ACPI_HP_METHOD_WLAN_RADIO			2
775db2f26eSSascha Wildner #define ACPI_HP_METHOD_WLAN_ON_AIR			3
785db2f26eSSascha Wildner #define ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON		4
795db2f26eSSascha Wildner #define ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF	5
805db2f26eSSascha Wildner #define ACPI_HP_METHOD_BLUETOOTH_ENABLED		6
815db2f26eSSascha Wildner #define ACPI_HP_METHOD_BLUETOOTH_RADIO			7
825db2f26eSSascha Wildner #define ACPI_HP_METHOD_BLUETOOTH_ON_AIR			8
835db2f26eSSascha Wildner #define ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON	9
845db2f26eSSascha Wildner #define ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF	10
855db2f26eSSascha Wildner #define ACPI_HP_METHOD_WWAN_ENABLED			11
865db2f26eSSascha Wildner #define ACPI_HP_METHOD_WWAN_RADIO			12
875db2f26eSSascha Wildner #define ACPI_HP_METHOD_WWAN_ON_AIR			13
885db2f26eSSascha Wildner #define ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON		14
895db2f26eSSascha Wildner #define ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF	15
905db2f26eSSascha Wildner #define ACPI_HP_METHOD_ALS				16
915db2f26eSSascha Wildner #define ACPI_HP_METHOD_DISPLAY				17
925db2f26eSSascha Wildner #define ACPI_HP_METHOD_HDDTEMP				18
935db2f26eSSascha Wildner #define ACPI_HP_METHOD_DOCK				19
945db2f26eSSascha Wildner #define ACPI_HP_METHOD_CMI_DETAIL			20
955db2f26eSSascha Wildner #define ACPI_HP_METHOD_VERBOSE				21
965db2f26eSSascha Wildner 
975db2f26eSSascha Wildner #define HP_MASK_WWAN_ON_AIR			0x1000000
985db2f26eSSascha Wildner #define HP_MASK_BLUETOOTH_ON_AIR		0x10000
995db2f26eSSascha Wildner #define HP_MASK_WLAN_ON_AIR			0x100
1005db2f26eSSascha Wildner #define HP_MASK_WWAN_RADIO			0x8000000
1015db2f26eSSascha Wildner #define HP_MASK_BLUETOOTH_RADIO			0x80000
1025db2f26eSSascha Wildner #define HP_MASK_WLAN_RADIO			0x800
1035db2f26eSSascha Wildner #define HP_MASK_WWAN_ENABLED			0x2000000
1045db2f26eSSascha Wildner #define HP_MASK_BLUETOOTH_ENABLED		0x20000
1055db2f26eSSascha Wildner #define HP_MASK_WLAN_ENABLED			0x200
1065db2f26eSSascha Wildner 
1075db2f26eSSascha Wildner #define ACPI_HP_CMI_DETAIL_PATHS		0x01
1085db2f26eSSascha Wildner #define ACPI_HP_CMI_DETAIL_ENUMS		0x02
1095db2f26eSSascha Wildner #define ACPI_HP_CMI_DETAIL_FLAGS		0x04
1105db2f26eSSascha Wildner #define ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE	0x08
1115db2f26eSSascha Wildner 
1125db2f26eSSascha Wildner struct acpi_hp_inst_seq_pair {
1135db2f26eSSascha Wildner 	UINT32	sequence;	/* sequence number as suggested by cmi bios */
1145db2f26eSSascha Wildner 	UINT8	instance;	/* object instance on guid */
1155db2f26eSSascha Wildner };
1165db2f26eSSascha Wildner 
1175db2f26eSSascha Wildner struct acpi_hp_softc {
1185db2f26eSSascha Wildner 	device_t	dev;
1195db2f26eSSascha Wildner 	device_t	wmi_dev;
1205db2f26eSSascha Wildner 	int		has_notify;		/* notification GUID found */
1215db2f26eSSascha Wildner 	int		has_cmi;		/* CMI GUID found */
1225db2f26eSSascha Wildner 	int		cmi_detail;		/* CMI detail level
1235db2f26eSSascha Wildner 						   (set by sysctl) */
1245db2f26eSSascha Wildner 	int		verbose;		/* add debug output */
1255db2f26eSSascha Wildner 	int		wlan_enable_if_radio_on;	/* set by sysctl */
1265db2f26eSSascha Wildner 	int		wlan_disable_if_radio_off;	/* set by sysctl */
1275db2f26eSSascha Wildner 	int		bluetooth_enable_if_radio_on;	/* set by sysctl */
1285db2f26eSSascha Wildner 	int		bluetooth_disable_if_radio_off;	/* set by sysctl */
1295db2f26eSSascha Wildner 	int		wwan_enable_if_radio_on;	/* set by sysctl */
1305db2f26eSSascha Wildner 	int		wwan_disable_if_radio_off;	/* set by sysctl */
1315db2f26eSSascha Wildner 	int		was_wlan_on_air;		/* last known WLAN
1325db2f26eSSascha Wildner 							   on air status */
1335db2f26eSSascha Wildner 	int		was_bluetooth_on_air;		/* last known BT
1345db2f26eSSascha Wildner 							   on air status */
1355db2f26eSSascha Wildner 	int		was_wwan_on_air;		/* last known WWAN
1365db2f26eSSascha Wildner 							   on air status */
1375db2f26eSSascha Wildner 	struct sysctl_ctx_list	sysctl_ctx;
1385db2f26eSSascha Wildner 	struct sysctl_oid	*sysctl_tree;
1395db2f26eSSascha Wildner 	struct cdev	*hpcmi_dev_t;		/* hpcmi device handle */
1405db2f26eSSascha Wildner 	struct sbuf	hpcmi_sbuf;		/* /dev/hpcmi output sbuf */
1415db2f26eSSascha Wildner 	pid_t		hpcmi_open_pid;		/* pid operating on
1425db2f26eSSascha Wildner 						   /dev/hpcmi */
1435db2f26eSSascha Wildner 	int		hpcmi_bufptr;		/* current pointer position
1445db2f26eSSascha Wildner 						   in /dev/hpcmi output buffer
1455db2f26eSSascha Wildner 						 */
1465db2f26eSSascha Wildner 	int		cmi_order_size;		/* size of cmi_order list */
1475db2f26eSSascha Wildner 	struct acpi_hp_inst_seq_pair cmi_order[128];	/* list of CMI
1485db2f26eSSascha Wildner 			     instances ordered by BIOS suggested sequence */
1495db2f26eSSascha Wildner };
1505db2f26eSSascha Wildner 
1515db2f26eSSascha Wildner static struct {
1525db2f26eSSascha Wildner 	char	*name;
1535db2f26eSSascha Wildner 	int	method;
1545db2f26eSSascha Wildner 	char	*description;
1555db2f26eSSascha Wildner 	int	access;
1565db2f26eSSascha Wildner } acpi_hp_sysctls[] = {
1575db2f26eSSascha Wildner 	{
1585db2f26eSSascha Wildner 		.name		= "wlan_enabled",
1595db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WLAN_ENABLED,
1605db2f26eSSascha Wildner 		.description	= "Enable/Disable WLAN (WiFi)",
1615db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
1625db2f26eSSascha Wildner 	},
1635db2f26eSSascha Wildner 	{
1645db2f26eSSascha Wildner 		.name		= "wlan_radio",
1655db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WLAN_RADIO,
1665db2f26eSSascha Wildner 		.description	= "WLAN radio status",
1675db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
1685db2f26eSSascha Wildner 	},
1695db2f26eSSascha Wildner 	{
1705db2f26eSSascha Wildner 		.name		= "wlan_on_air",
1715db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WLAN_ON_AIR,
1725db2f26eSSascha Wildner 		.description	= "WLAN radio ready to use (enabled and radio)",
1735db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
1745db2f26eSSascha Wildner 	},
1755db2f26eSSascha Wildner 	{
1765db2f26eSSascha Wildner 		.name		= "wlan_enable_if_radio_on",
1775db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON,
1785db2f26eSSascha Wildner 		.description	= "Enable WLAN if radio is turned on",
1795db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
1805db2f26eSSascha Wildner 	},
1815db2f26eSSascha Wildner 	{
1825db2f26eSSascha Wildner 		.name		= "wlan_disable_if_radio_off",
1835db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF,
1845db2f26eSSascha Wildner 		.description	= "Disable WLAN if radio is turned off",
1855db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
1865db2f26eSSascha Wildner 	},
1875db2f26eSSascha Wildner 	{
1885db2f26eSSascha Wildner 		.name		= "bt_enabled",
1895db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLED,
1905db2f26eSSascha Wildner 		.description	= "Enable/Disable Bluetooth",
1915db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
1925db2f26eSSascha Wildner 	},
1935db2f26eSSascha Wildner 	{
1945db2f26eSSascha Wildner 		.name		= "bt_radio",
1955db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_BLUETOOTH_RADIO,
1965db2f26eSSascha Wildner 		.description	= "Bluetooth radio status",
1975db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
1985db2f26eSSascha Wildner 	},
1995db2f26eSSascha Wildner 	{
2005db2f26eSSascha Wildner 		.name		= "bt_on_air",
2015db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_BLUETOOTH_ON_AIR,
2025db2f26eSSascha Wildner 		.description	= "Bluetooth radio ready to use"
2035db2f26eSSascha Wildner 				    " (enabled and radio)",
2045db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
2055db2f26eSSascha Wildner 	},
2065db2f26eSSascha Wildner 	{
2075db2f26eSSascha Wildner 		.name		= "bt_enable_if_radio_on",
2085db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON,
2095db2f26eSSascha Wildner 		.description	= "Enable bluetooth if radio is turned on",
2105db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2115db2f26eSSascha Wildner 	},
2125db2f26eSSascha Wildner 	{
2135db2f26eSSascha Wildner 		.name		= "bt_disable_if_radio_off",
2145db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF,
2155db2f26eSSascha Wildner 		.description	= "Disable bluetooth if radio is turned off",
2165db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2175db2f26eSSascha Wildner 	},
2185db2f26eSSascha Wildner 	{
2195db2f26eSSascha Wildner 		.name		= "wwan_enabled",
2205db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WWAN_ENABLED,
2215db2f26eSSascha Wildner 		.description	= "Enable/Disable WWAN (UMTS)",
2225db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2235db2f26eSSascha Wildner 	},
2245db2f26eSSascha Wildner 	{
2255db2f26eSSascha Wildner 		.name		= "wwan_radio",
2265db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WWAN_RADIO,
2275db2f26eSSascha Wildner 		.description	= "WWAN radio status",
2285db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
2295db2f26eSSascha Wildner 	},
2305db2f26eSSascha Wildner 	{
2315db2f26eSSascha Wildner 		.name		= "wwan_on_air",
2325db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WWAN_ON_AIR,
2335db2f26eSSascha Wildner 		.description	= "WWAN radio ready to use (enabled and radio)",
2345db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
2355db2f26eSSascha Wildner 	},
2365db2f26eSSascha Wildner 	{
2375db2f26eSSascha Wildner 		.name		= "wwan_enable_if_radio_on",
2385db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON,
2395db2f26eSSascha Wildner 		.description	= "Enable WWAN if radio is turned on",
2405db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2415db2f26eSSascha Wildner 	},
2425db2f26eSSascha Wildner 	{
2435db2f26eSSascha Wildner 		.name		= "wwan_disable_if_radio_off",
2445db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF,
2455db2f26eSSascha Wildner 		.description	= "Disable WWAN if radio is turned off",
2465db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2475db2f26eSSascha Wildner 	},
2485db2f26eSSascha Wildner 	{
2495db2f26eSSascha Wildner 		.name		= "als_enabled",
2505db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_ALS,
2515db2f26eSSascha Wildner 		.description	= "Enable/Disable ALS (Ambient light sensor)",
2525db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2535db2f26eSSascha Wildner 	},
2545db2f26eSSascha Wildner 	{
2555db2f26eSSascha Wildner 		.name		= "display",
2565db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_DISPLAY,
2575db2f26eSSascha Wildner 		.description	= "Display status",
2585db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
2595db2f26eSSascha Wildner 	},
2605db2f26eSSascha Wildner 	{
2615db2f26eSSascha Wildner 		.name		= "hdd_temperature",
2625db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_HDDTEMP,
2635db2f26eSSascha Wildner 		.description	= "HDD temperature",
2645db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
2655db2f26eSSascha Wildner 	},
2665db2f26eSSascha Wildner 	{
2675db2f26eSSascha Wildner 		.name		= "is_docked",
2685db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_DOCK,
2695db2f26eSSascha Wildner 		.description	= "Docking station status",
2705db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RD
2715db2f26eSSascha Wildner 	},
2725db2f26eSSascha Wildner 	{
2735db2f26eSSascha Wildner 		.name		= "cmi_detail",
2745db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_CMI_DETAIL,
2755db2f26eSSascha Wildner 		.description	= "Details shown in CMI output "
2765db2f26eSSascha Wildner 				    "(cat /dev/hpcmi)",
2775db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2785db2f26eSSascha Wildner 	},
2795db2f26eSSascha Wildner 	{
2805db2f26eSSascha Wildner 		.name		= "verbose",
2815db2f26eSSascha Wildner 		.method		= ACPI_HP_METHOD_VERBOSE,
2825db2f26eSSascha Wildner 		.description	= "Verbosity level",
2835db2f26eSSascha Wildner 		.access		= CTLTYPE_INT | CTLFLAG_RW
2845db2f26eSSascha Wildner 	},
2855db2f26eSSascha Wildner 
2865db2f26eSSascha Wildner 	{ NULL, 0, NULL, 0 }
2875db2f26eSSascha Wildner };
2885db2f26eSSascha Wildner 
2895db2f26eSSascha Wildner ACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping");
2905db2f26eSSascha Wildner 
2915db2f26eSSascha Wildner static void	acpi_hp_identify(driver_t *driver, device_t parent);
2925db2f26eSSascha Wildner static int	acpi_hp_probe(device_t dev);
2935db2f26eSSascha Wildner static int	acpi_hp_attach(device_t dev);
2945db2f26eSSascha Wildner static int	acpi_hp_detach(device_t dev);
2955db2f26eSSascha Wildner 
2965db2f26eSSascha Wildner static void	acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc* sc);
2975db2f26eSSascha Wildner static int	acpi_hp_sysctl(SYSCTL_HANDLER_ARGS);
2985db2f26eSSascha Wildner static int	acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method,
2995db2f26eSSascha Wildner 		    int arg, int oldarg);
3005db2f26eSSascha Wildner static int	acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method);
3015db2f26eSSascha Wildner static int	acpi_hp_exec_wmi_command(device_t wmi_dev, int command,
3025db2f26eSSascha Wildner 		    int is_write, int val);
3035db2f26eSSascha Wildner static void	acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context);
3045db2f26eSSascha Wildner static int	acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid,
3055db2f26eSSascha Wildner 		    UINT8 instance, char* outbuf, size_t outsize,
3065db2f26eSSascha Wildner 		    UINT32* sequence, int detail);
3075db2f26eSSascha Wildner static void	acpi_hp_hex_decode(char* buffer);
3085db2f26eSSascha Wildner 
3095db2f26eSSascha Wildner static d_open_t	acpi_hp_hpcmi_open;
3105db2f26eSSascha Wildner static d_close_t acpi_hp_hpcmi_close;
3115db2f26eSSascha Wildner static d_read_t	acpi_hp_hpcmi_read;
3125db2f26eSSascha Wildner 
3135db2f26eSSascha Wildner /* handler /dev/hpcmi device */
3145db2f26eSSascha Wildner static struct dev_ops hpcmi_ops = {
315a639f788SMatthew Dillon 	{ "hpcmi", 0, D_MPSAFE },
3165db2f26eSSascha Wildner 	.d_open = acpi_hp_hpcmi_open,
3175db2f26eSSascha Wildner 	.d_close = acpi_hp_hpcmi_close,
3185db2f26eSSascha Wildner 	.d_read = acpi_hp_hpcmi_read,
3195db2f26eSSascha Wildner };
3205db2f26eSSascha Wildner 
3215db2f26eSSascha Wildner static device_method_t acpi_hp_methods[] = {
3225db2f26eSSascha Wildner 	DEVMETHOD(device_identify, acpi_hp_identify),
3235db2f26eSSascha Wildner 	DEVMETHOD(device_probe, acpi_hp_probe),
3245db2f26eSSascha Wildner 	DEVMETHOD(device_attach, acpi_hp_attach),
3255db2f26eSSascha Wildner 	DEVMETHOD(device_detach, acpi_hp_detach),
326d3c9c58eSSascha Wildner 	DEVMETHOD_END
3275db2f26eSSascha Wildner };
3285db2f26eSSascha Wildner 
3295db2f26eSSascha Wildner static driver_t	acpi_hp_driver = {
3305db2f26eSSascha Wildner 	"acpi_hp",
3315db2f26eSSascha Wildner 	acpi_hp_methods,
3325db2f26eSSascha Wildner 	sizeof(struct acpi_hp_softc),
3335db2f26eSSascha Wildner };
3345db2f26eSSascha Wildner 
3355db2f26eSSascha Wildner static devclass_t acpi_hp_devclass;
3365db2f26eSSascha Wildner 
3375db2f26eSSascha Wildner DRIVER_MODULE(acpi_hp, acpi_wmi, acpi_hp_driver, acpi_hp_devclass,
3385db2f26eSSascha Wildner 		NULL, NULL);
3395db2f26eSSascha Wildner MODULE_VERSION(acpi_hp, 1);
3405db2f26eSSascha Wildner MODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1);
3415db2f26eSSascha Wildner MODULE_DEPEND(acpi_hp, acpi, 1, 1, 1);
3425db2f26eSSascha Wildner 
3435db2f26eSSascha Wildner static void
acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc * sc)3445db2f26eSSascha Wildner acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc)
3455db2f26eSSascha Wildner {
3465db2f26eSSascha Wildner 	int	wireless;
3475db2f26eSSascha Wildner 	int	new_wlan_status;
3485db2f26eSSascha Wildner 	int	new_bluetooth_status;
3495db2f26eSSascha Wildner 	int	new_wwan_status;
3505db2f26eSSascha Wildner 
3515db2f26eSSascha Wildner 	wireless = acpi_hp_exec_wmi_command(sc->wmi_dev,
3525db2f26eSSascha Wildner 		    ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
3535db2f26eSSascha Wildner 	new_wlan_status = -1;
3545db2f26eSSascha Wildner 	new_bluetooth_status = -1;
3555db2f26eSSascha Wildner 	new_wwan_status = -1;
3565db2f26eSSascha Wildner 
3575db2f26eSSascha Wildner 	if (sc->verbose)
3585db2f26eSSascha Wildner 		device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless);
3595db2f26eSSascha Wildner 	if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO)
3605db2f26eSSascha Wildner 	    &&  (wireless & HP_MASK_WLAN_ENABLED)) {
3615db2f26eSSascha Wildner 		acpi_hp_exec_wmi_command(sc->wmi_dev,
3625db2f26eSSascha Wildner 		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100);
3635db2f26eSSascha Wildner 		new_wlan_status = 0;
3645db2f26eSSascha Wildner 	}
3655db2f26eSSascha Wildner 	else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO)
3665db2f26eSSascha Wildner 		&&  !(wireless & HP_MASK_WLAN_ENABLED)) {
3675db2f26eSSascha Wildner 		acpi_hp_exec_wmi_command(sc->wmi_dev,
3685db2f26eSSascha Wildner 		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101);
3695db2f26eSSascha Wildner 		new_wlan_status = 1;
3705db2f26eSSascha Wildner 	}
3715db2f26eSSascha Wildner 	if (sc->bluetooth_disable_if_radio_off &&
3725db2f26eSSascha Wildner 	    !(wireless & HP_MASK_BLUETOOTH_RADIO) &&
3735db2f26eSSascha Wildner 	    (wireless & HP_MASK_BLUETOOTH_ENABLED)) {
3745db2f26eSSascha Wildner 		acpi_hp_exec_wmi_command(sc->wmi_dev,
3755db2f26eSSascha Wildner 		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200);
3765db2f26eSSascha Wildner 		new_bluetooth_status = 0;
3775db2f26eSSascha Wildner 	}
3785db2f26eSSascha Wildner 	else if (sc->bluetooth_enable_if_radio_on &&
3795db2f26eSSascha Wildner 		(wireless & HP_MASK_BLUETOOTH_RADIO) &&
3805db2f26eSSascha Wildner 		!(wireless & HP_MASK_BLUETOOTH_ENABLED)) {
3815db2f26eSSascha Wildner 		acpi_hp_exec_wmi_command(sc->wmi_dev,
3825db2f26eSSascha Wildner 		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202);
3835db2f26eSSascha Wildner 		new_bluetooth_status = 1;
3845db2f26eSSascha Wildner 	}
3855db2f26eSSascha Wildner 	if (sc->wwan_disable_if_radio_off &&
3865db2f26eSSascha Wildner 	    !(wireless & HP_MASK_WWAN_RADIO) &&
3875db2f26eSSascha Wildner 	    (wireless & HP_MASK_WWAN_ENABLED)) {
3885db2f26eSSascha Wildner 		acpi_hp_exec_wmi_command(sc->wmi_dev,
3895db2f26eSSascha Wildner 		ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400);
3905db2f26eSSascha Wildner 		new_wwan_status = 0;
3915db2f26eSSascha Wildner 	}
3925db2f26eSSascha Wildner 	else if (sc->wwan_enable_if_radio_on &&
3935db2f26eSSascha Wildner 		(wireless & HP_MASK_WWAN_RADIO) &&
3945db2f26eSSascha Wildner 		!(wireless & HP_MASK_WWAN_ENABLED)) {
3955db2f26eSSascha Wildner 		acpi_hp_exec_wmi_command(sc->wmi_dev,
3965db2f26eSSascha Wildner 		    ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404);
3975db2f26eSSascha Wildner 		new_wwan_status = 1;
3985db2f26eSSascha Wildner 	}
3995db2f26eSSascha Wildner 
4005db2f26eSSascha Wildner 	if (new_wlan_status == -1) {
4015db2f26eSSascha Wildner 		new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR);
4025db2f26eSSascha Wildner 		if ((new_wlan_status?1:0) != sc->was_wlan_on_air) {
4035db2f26eSSascha Wildner 			sc->was_wlan_on_air = sc->was_wlan_on_air?0:1;
4045db2f26eSSascha Wildner 			if (sc->verbose)
4055db2f26eSSascha Wildner 				device_printf(sc->wmi_dev,
4065db2f26eSSascha Wildner 			    	    "WLAN on air changed to %i "
4075db2f26eSSascha Wildner 			    	    "(new_wlan_status is %i)\n",
4085db2f26eSSascha Wildner 			    	    sc->was_wlan_on_air, new_wlan_status);
4095db2f26eSSascha Wildner 			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
4105db2f26eSSascha Wildner 			    0xc0+sc->was_wlan_on_air);
4115db2f26eSSascha Wildner 		}
4125db2f26eSSascha Wildner 	}
4135db2f26eSSascha Wildner 	if (new_bluetooth_status == -1) {
4145db2f26eSSascha Wildner 		new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR);
4155db2f26eSSascha Wildner 		if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) {
4165db2f26eSSascha Wildner 			sc->was_bluetooth_on_air = sc->was_bluetooth_on_air?
4175db2f26eSSascha Wildner 			    0:1;
4185db2f26eSSascha Wildner 			if (sc->verbose)
4195db2f26eSSascha Wildner 				device_printf(sc->wmi_dev,
4205db2f26eSSascha Wildner 				    "BLUETOOTH on air changed"
4215db2f26eSSascha Wildner 				    " to %i (new_bluetooth_status is %i)\n",
4225db2f26eSSascha Wildner 				    sc->was_bluetooth_on_air,
4235db2f26eSSascha Wildner 				    new_bluetooth_status);
4245db2f26eSSascha Wildner 			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
4255db2f26eSSascha Wildner 			    0xd0+sc->was_bluetooth_on_air);
4265db2f26eSSascha Wildner 		}
4275db2f26eSSascha Wildner 	}
4285db2f26eSSascha Wildner 	if (new_wwan_status == -1) {
4295db2f26eSSascha Wildner 		new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR);
4305db2f26eSSascha Wildner 		if ((new_wwan_status?1:0) != sc->was_wwan_on_air) {
4315db2f26eSSascha Wildner 			sc->was_wwan_on_air = sc->was_wwan_on_air?0:1;
4325db2f26eSSascha Wildner 			if (sc->verbose)
4335db2f26eSSascha Wildner 				device_printf(sc->wmi_dev,
4345db2f26eSSascha Wildner 				    "WWAN on air changed to %i"
4355db2f26eSSascha Wildner 			    	    " (new_wwan_status is %i)\n",
4365db2f26eSSascha Wildner 				    sc->was_wwan_on_air, new_wwan_status);
4375db2f26eSSascha Wildner 			acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
4385db2f26eSSascha Wildner 			    0xe0+sc->was_wwan_on_air);
4395db2f26eSSascha Wildner 		}
4405db2f26eSSascha Wildner 	}
4415db2f26eSSascha Wildner }
4425db2f26eSSascha Wildner 
4435db2f26eSSascha Wildner static void
acpi_hp_identify(driver_t * driver,device_t parent)4445db2f26eSSascha Wildner acpi_hp_identify(driver_t *driver, device_t parent)
4455db2f26eSSascha Wildner {
4465db2f26eSSascha Wildner 
4475db2f26eSSascha Wildner 	/* Don't do anything if driver is disabled. */
4485db2f26eSSascha Wildner 	if (acpi_disabled("hp"))
4495db2f26eSSascha Wildner 		return;
4505db2f26eSSascha Wildner 
4515db2f26eSSascha Wildner 	/* Add only a single device instance. */
4525db2f26eSSascha Wildner 	if (device_find_child(parent, "acpi_hp", -1) != NULL)
4535db2f26eSSascha Wildner 		return;
4545db2f26eSSascha Wildner 
4555db2f26eSSascha Wildner 	if (BUS_ADD_CHILD(parent, parent, 0, "acpi_hp", -1) == NULL)
4565db2f26eSSascha Wildner 		device_printf(parent, "add acpi_hp child failed\n");
4575db2f26eSSascha Wildner }
4585db2f26eSSascha Wildner 
4595db2f26eSSascha Wildner static int
acpi_hp_probe(device_t dev)4605db2f26eSSascha Wildner acpi_hp_probe(device_t dev)
4615db2f26eSSascha Wildner {
4625db2f26eSSascha Wildner 
4635db2f26eSSascha Wildner 	device_set_desc(dev, "HP ACPI-WMI Mapping");
4645db2f26eSSascha Wildner 	return (0);
4655db2f26eSSascha Wildner }
4665db2f26eSSascha Wildner 
4675db2f26eSSascha Wildner static int
acpi_hp_attach(device_t dev)4685db2f26eSSascha Wildner acpi_hp_attach(device_t dev)
4695db2f26eSSascha Wildner {
4705db2f26eSSascha Wildner 	struct acpi_hp_softc	*sc;
4715db2f26eSSascha Wildner 	struct acpi_softc	*acpi_sc;
4725db2f26eSSascha Wildner 	int			arg;
4735db2f26eSSascha Wildner 
4740742f40dSImre Vadasz 	ACPI_SERIAL_INIT(hp);
4755db2f26eSSascha Wildner 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
4765db2f26eSSascha Wildner 
4775db2f26eSSascha Wildner 	sc = device_get_softc(dev);
4785db2f26eSSascha Wildner 	sc->dev = dev;
4795db2f26eSSascha Wildner 	sc->has_notify = 0;
4805db2f26eSSascha Wildner 	sc->has_cmi = 0;
4815db2f26eSSascha Wildner 	sc->bluetooth_enable_if_radio_on = 0;
4825db2f26eSSascha Wildner 	sc->bluetooth_disable_if_radio_off = 0;
4835db2f26eSSascha Wildner 	sc->wlan_enable_if_radio_on = 0;
4845db2f26eSSascha Wildner 	sc->wlan_disable_if_radio_off = 0;
4855db2f26eSSascha Wildner 	sc->wlan_enable_if_radio_on = 0;
4865db2f26eSSascha Wildner 	sc->wlan_disable_if_radio_off = 0;
4875db2f26eSSascha Wildner 	sc->was_wlan_on_air = 0;
4885db2f26eSSascha Wildner 	sc->was_bluetooth_on_air = 0;
4895db2f26eSSascha Wildner 	sc->was_wwan_on_air = 0;
4905db2f26eSSascha Wildner 	sc->cmi_detail = 0;
4915db2f26eSSascha Wildner 	sc->cmi_order_size = -1;
4925db2f26eSSascha Wildner 	sc->verbose = 0;
4935db2f26eSSascha Wildner 	memset(sc->cmi_order, 0, sizeof(sc->cmi_order));
4945db2f26eSSascha Wildner 	sc->wmi_dev = device_get_parent(dev);
4955db2f26eSSascha Wildner 	acpi_sc = acpi_device_get_parent_softc(sc->wmi_dev);
4965db2f26eSSascha Wildner 
4975db2f26eSSascha Wildner 	if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
4985db2f26eSSascha Wildner 	    ACPI_HP_WMI_BIOS_GUID)) {
4995db2f26eSSascha Wildner 		device_printf(dev,
5005db2f26eSSascha Wildner 		    "WMI device does not provide the HP BIOS GUID\n");
5015db2f26eSSascha Wildner 		return (EINVAL);
5025db2f26eSSascha Wildner 	}
5035db2f26eSSascha Wildner 	if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
5045db2f26eSSascha Wildner 	    ACPI_HP_WMI_EVENT_GUID)) {
5055db2f26eSSascha Wildner 		device_printf(dev,
5065db2f26eSSascha Wildner 		    "HP event GUID detected, installing event handler\n");
5075db2f26eSSascha Wildner 		if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
5085db2f26eSSascha Wildner 		    ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) {
5095db2f26eSSascha Wildner 			device_printf(dev,
5105db2f26eSSascha Wildner 			    "Could not install notification handler!\n");
5115db2f26eSSascha Wildner 		}
5125db2f26eSSascha Wildner 		else {
5135db2f26eSSascha Wildner 			sc->has_notify = 1;
5145db2f26eSSascha Wildner 		}
5155db2f26eSSascha Wildner 	}
5165db2f26eSSascha Wildner 	if ((sc->has_cmi =
5175db2f26eSSascha Wildner 	    ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID)
5185db2f26eSSascha Wildner 	    )) {
5195db2f26eSSascha Wildner 		device_printf(dev, "HP CMI GUID detected\n");
5205db2f26eSSascha Wildner 	}
5215db2f26eSSascha Wildner 
5225db2f26eSSascha Wildner 	if (sc->has_cmi) {
5235db2f26eSSascha Wildner 		sc->hpcmi_dev_t = make_dev(&hpcmi_ops, 0, UID_ROOT,
5245db2f26eSSascha Wildner 			    GID_WHEEL, 0644, "hpcmi");
5255db2f26eSSascha Wildner 		sc->hpcmi_dev_t->si_drv1 = sc;
5265db2f26eSSascha Wildner 		sc->hpcmi_open_pid = 0;
5275db2f26eSSascha Wildner 		sc->hpcmi_bufptr = -1;
5285db2f26eSSascha Wildner 	}
5295db2f26eSSascha Wildner 
5305db2f26eSSascha Wildner 	ACPI_SERIAL_BEGIN(hp);
5315db2f26eSSascha Wildner 
5325db2f26eSSascha Wildner         sysctl_ctx_init(&sc->sysctl_ctx);
5335db2f26eSSascha Wildner         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
5345db2f26eSSascha Wildner                 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
5355db2f26eSSascha Wildner                 "hp", CTLFLAG_RD, 0, "");
5365db2f26eSSascha Wildner 
5375db2f26eSSascha Wildner 	for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) {
5385db2f26eSSascha Wildner 		arg = 0;
5395db2f26eSSascha Wildner 		if ((!sc->has_notify &&
5405db2f26eSSascha Wildner 		    (acpi_hp_sysctls[i].method ==
5415db2f26eSSascha Wildner 			ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON ||
5425db2f26eSSascha Wildner 		    acpi_hp_sysctls[i].method ==
5435db2f26eSSascha Wildner 			ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF ||
5445db2f26eSSascha Wildner 		    acpi_hp_sysctls[i].method ==
5455db2f26eSSascha Wildner 			ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON ||
5465db2f26eSSascha Wildner 		    acpi_hp_sysctls[i].method ==
5475db2f26eSSascha Wildner 			ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF ||
5485db2f26eSSascha Wildner 		    acpi_hp_sysctls[i].method ==
5495db2f26eSSascha Wildner 			ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON ||
5505db2f26eSSascha Wildner 		    acpi_hp_sysctls[i].method ==
5515db2f26eSSascha Wildner 			ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) ||
5525db2f26eSSascha Wildner 		    (arg = acpi_hp_sysctl_get(sc,
5535db2f26eSSascha Wildner 		    acpi_hp_sysctls[i].method)) < 0) {
5545db2f26eSSascha Wildner 			continue;
5555db2f26eSSascha Wildner 		}
5565db2f26eSSascha Wildner 		if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) {
5575db2f26eSSascha Wildner 			sc->was_wlan_on_air = arg;
5585db2f26eSSascha Wildner 		}
5595db2f26eSSascha Wildner 		else if (acpi_hp_sysctls[i].method ==
5605db2f26eSSascha Wildner 			    ACPI_HP_METHOD_BLUETOOTH_ON_AIR) {
5615db2f26eSSascha Wildner 			sc->was_bluetooth_on_air = arg;
5625db2f26eSSascha Wildner 		}
5635db2f26eSSascha Wildner 		else if (acpi_hp_sysctls[i].method ==
5645db2f26eSSascha Wildner 			    ACPI_HP_METHOD_WWAN_ON_AIR) {
5655db2f26eSSascha Wildner 			sc->was_wwan_on_air = arg;
5665db2f26eSSascha Wildner 		}
5675db2f26eSSascha Wildner 
5685db2f26eSSascha Wildner 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
5695db2f26eSSascha Wildner 		SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
5705db2f26eSSascha Wildner 			acpi_hp_sysctls[i].name, acpi_hp_sysctls[i].access,
5715db2f26eSSascha Wildner 			sc, i, acpi_hp_sysctl, "I",
5725db2f26eSSascha Wildner 			acpi_hp_sysctls[i].description);
5735db2f26eSSascha Wildner 	}
5745db2f26eSSascha Wildner 	ACPI_SERIAL_END(hp);
5755db2f26eSSascha Wildner 
5765db2f26eSSascha Wildner 	return (0);
5775db2f26eSSascha Wildner }
5785db2f26eSSascha Wildner 
5795db2f26eSSascha Wildner static int
acpi_hp_detach(device_t dev)5805db2f26eSSascha Wildner acpi_hp_detach(device_t dev)
5815db2f26eSSascha Wildner {
5825db2f26eSSascha Wildner 	int	ret;
5835db2f26eSSascha Wildner 
5845db2f26eSSascha Wildner 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
5855db2f26eSSascha Wildner 	struct acpi_hp_softc *sc = device_get_softc(dev);
5865db2f26eSSascha Wildner 	if (sc->has_cmi && sc->hpcmi_open_pid != 0) {
5875db2f26eSSascha Wildner 		ret = EBUSY;
5885db2f26eSSascha Wildner 	}
5895db2f26eSSascha Wildner 	else {
5905db2f26eSSascha Wildner 		if (sc->has_notify) {
5915db2f26eSSascha Wildner 			ACPI_WMI_REMOVE_EVENT_HANDLER(dev,
5925db2f26eSSascha Wildner 			    ACPI_HP_WMI_EVENT_GUID);
5935db2f26eSSascha Wildner 		}
594b1722b2aSImre Vadasz 		sysctl_ctx_free(&sc->sysctl_ctx);
5955db2f26eSSascha Wildner 		if (sc->hpcmi_bufptr != -1) {
5965db2f26eSSascha Wildner 			sbuf_delete(&sc->hpcmi_sbuf);
5975db2f26eSSascha Wildner 			sc->hpcmi_bufptr = -1;
5985db2f26eSSascha Wildner 		}
5995db2f26eSSascha Wildner 		sc->hpcmi_open_pid = 0;
6005db2f26eSSascha Wildner 		destroy_dev(sc->hpcmi_dev_t);
6015db2f26eSSascha Wildner 		ret = 0;
6025db2f26eSSascha Wildner 	}
6035db2f26eSSascha Wildner 
6045db2f26eSSascha Wildner 	return (ret);
6055db2f26eSSascha Wildner }
6065db2f26eSSascha Wildner 
6075db2f26eSSascha Wildner static int
acpi_hp_sysctl(SYSCTL_HANDLER_ARGS)6085db2f26eSSascha Wildner acpi_hp_sysctl(SYSCTL_HANDLER_ARGS)
6095db2f26eSSascha Wildner {
6105db2f26eSSascha Wildner 	struct acpi_hp_softc	*sc;
6115db2f26eSSascha Wildner 	int			arg;
6125db2f26eSSascha Wildner 	int			oldarg;
6135db2f26eSSascha Wildner 	int			error = 0;
6145db2f26eSSascha Wildner 	int			function;
6155db2f26eSSascha Wildner 	int			method;
6165db2f26eSSascha Wildner 
6175db2f26eSSascha Wildner 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
6185db2f26eSSascha Wildner 
6195db2f26eSSascha Wildner 	sc = (struct acpi_hp_softc *)oidp->oid_arg1;
6205db2f26eSSascha Wildner 	function = oidp->oid_arg2;
6215db2f26eSSascha Wildner 	method = acpi_hp_sysctls[function].method;
6225db2f26eSSascha Wildner 
6235db2f26eSSascha Wildner 	ACPI_SERIAL_BEGIN(hp);
6245db2f26eSSascha Wildner 	arg = acpi_hp_sysctl_get(sc, method);
6255db2f26eSSascha Wildner 	oldarg = arg;
6265db2f26eSSascha Wildner 	error = sysctl_handle_int(oidp, &arg, 0, req);
6275db2f26eSSascha Wildner 	if (!error && req->newptr != NULL) {
6285db2f26eSSascha Wildner 		error = acpi_hp_sysctl_set(sc, method, arg, oldarg);
6295db2f26eSSascha Wildner 	}
6305db2f26eSSascha Wildner 	ACPI_SERIAL_END(hp);
6315db2f26eSSascha Wildner 
6325db2f26eSSascha Wildner 	return (error);
6335db2f26eSSascha Wildner }
6345db2f26eSSascha Wildner 
6355db2f26eSSascha Wildner static int
acpi_hp_sysctl_get(struct acpi_hp_softc * sc,int method)6365db2f26eSSascha Wildner acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method)
6375db2f26eSSascha Wildner {
6385db2f26eSSascha Wildner 	int	val = 0;
6395db2f26eSSascha Wildner 
6405db2f26eSSascha Wildner 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
6415db2f26eSSascha Wildner 	ACPI_SERIAL_ASSERT(hp);
6425db2f26eSSascha Wildner 
6435db2f26eSSascha Wildner 	switch (method) {
6445db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WLAN_ENABLED:
6455db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6465db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6475db2f26eSSascha Wildner 		val = ((val & HP_MASK_WLAN_ENABLED) != 0);
6485db2f26eSSascha Wildner 		break;
6495db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WLAN_RADIO:
6505db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6515db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6525db2f26eSSascha Wildner 		val = ((val & HP_MASK_WLAN_RADIO) != 0);
6535db2f26eSSascha Wildner 		break;
6545db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WLAN_ON_AIR:
6555db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6565db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6575db2f26eSSascha Wildner 		val = ((val & HP_MASK_WLAN_ON_AIR) != 0);
6585db2f26eSSascha Wildner 		break;
6595db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
6605db2f26eSSascha Wildner 		val = sc->wlan_enable_if_radio_on;
6615db2f26eSSascha Wildner 		break;
6625db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
6635db2f26eSSascha Wildner 		val = sc->wlan_disable_if_radio_off;
6645db2f26eSSascha Wildner 		break;
6655db2f26eSSascha Wildner 	case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
6665db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6675db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6685db2f26eSSascha Wildner 		val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0);
6695db2f26eSSascha Wildner 		break;
6705db2f26eSSascha Wildner 	case ACPI_HP_METHOD_BLUETOOTH_RADIO:
6715db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6725db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6735db2f26eSSascha Wildner 		val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0);
6745db2f26eSSascha Wildner 		break;
6755db2f26eSSascha Wildner 	case ACPI_HP_METHOD_BLUETOOTH_ON_AIR:
6765db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6775db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6785db2f26eSSascha Wildner 		val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0);
6795db2f26eSSascha Wildner 		break;
6805db2f26eSSascha Wildner 	case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
6815db2f26eSSascha Wildner 		val = sc->bluetooth_enable_if_radio_on;
6825db2f26eSSascha Wildner 		break;
6835db2f26eSSascha Wildner 	case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
6845db2f26eSSascha Wildner 		val = sc->bluetooth_disable_if_radio_off;
6855db2f26eSSascha Wildner 		break;
6865db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WWAN_ENABLED:
6875db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6885db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6895db2f26eSSascha Wildner 		val = ((val & HP_MASK_WWAN_ENABLED) != 0);
6905db2f26eSSascha Wildner 		break;
6915db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WWAN_RADIO:
6925db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6935db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6945db2f26eSSascha Wildner 		val = ((val & HP_MASK_WWAN_RADIO) != 0);
6955db2f26eSSascha Wildner 		break;
6965db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WWAN_ON_AIR:
6975db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
6985db2f26eSSascha Wildner 			ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0);
6995db2f26eSSascha Wildner 		val = ((val & HP_MASK_WWAN_ON_AIR) != 0);
7005db2f26eSSascha Wildner 		break;
7015db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
7025db2f26eSSascha Wildner 		val = sc->wwan_enable_if_radio_on;
7035db2f26eSSascha Wildner 		break;
7045db2f26eSSascha Wildner 	case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
7055db2f26eSSascha Wildner 		val = sc->wwan_disable_if_radio_off;
7065db2f26eSSascha Wildner 		break;
7075db2f26eSSascha Wildner 	case ACPI_HP_METHOD_ALS:
7085db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
7095db2f26eSSascha Wildner 			ACPI_HP_WMI_ALS_COMMAND, 0, 0);
7105db2f26eSSascha Wildner 		break;
7115db2f26eSSascha Wildner 	case ACPI_HP_METHOD_DISPLAY:
7125db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
7135db2f26eSSascha Wildner 			ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0);
7145db2f26eSSascha Wildner 		break;
7155db2f26eSSascha Wildner 	case ACPI_HP_METHOD_HDDTEMP:
7165db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
7175db2f26eSSascha Wildner 			ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0);
7185db2f26eSSascha Wildner 		break;
7195db2f26eSSascha Wildner 	case ACPI_HP_METHOD_DOCK:
7205db2f26eSSascha Wildner 		val = acpi_hp_exec_wmi_command(sc->wmi_dev,
7215db2f26eSSascha Wildner 			ACPI_HP_WMI_DOCK_COMMAND, 0, 0);
7225db2f26eSSascha Wildner 		break;
7235db2f26eSSascha Wildner 	case ACPI_HP_METHOD_CMI_DETAIL:
7245db2f26eSSascha Wildner 		val = sc->cmi_detail;
7255db2f26eSSascha Wildner 		break;
7265db2f26eSSascha Wildner 	case ACPI_HP_METHOD_VERBOSE:
7275db2f26eSSascha Wildner 		val = sc->verbose;
7285db2f26eSSascha Wildner 		break;
7295db2f26eSSascha Wildner 	}
7305db2f26eSSascha Wildner 
7315db2f26eSSascha Wildner 	return (val);
7325db2f26eSSascha Wildner }
7335db2f26eSSascha Wildner 
7345db2f26eSSascha Wildner static int
acpi_hp_sysctl_set(struct acpi_hp_softc * sc,int method,int arg,int oldarg)7355db2f26eSSascha Wildner acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg)
7365db2f26eSSascha Wildner {
7375db2f26eSSascha Wildner 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
7385db2f26eSSascha Wildner 	ACPI_SERIAL_ASSERT(hp);
7395db2f26eSSascha Wildner 
7405db2f26eSSascha Wildner 	if (method != ACPI_HP_METHOD_CMI_DETAIL &&
7415db2f26eSSascha Wildner 	    method != ACPI_HP_METHOD_VERBOSE)
7425db2f26eSSascha Wildner 		arg = arg?1:0;
7435db2f26eSSascha Wildner 
7445db2f26eSSascha Wildner 	if (arg != oldarg) {
7455db2f26eSSascha Wildner 		switch (method) {
7465db2f26eSSascha Wildner 		case ACPI_HP_METHOD_WLAN_ENABLED:
7475db2f26eSSascha Wildner 			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
7485db2f26eSSascha Wildner 				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
7495db2f26eSSascha Wildner 				    arg?0x101:0x100));
7505db2f26eSSascha Wildner 		case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
7515db2f26eSSascha Wildner 			sc->wlan_enable_if_radio_on = arg;
7525db2f26eSSascha Wildner 			acpi_hp_evaluate_auto_on_off(sc);
7535db2f26eSSascha Wildner 			break;
7545db2f26eSSascha Wildner 		case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
7555db2f26eSSascha Wildner 			sc->wlan_disable_if_radio_off = arg;
7565db2f26eSSascha Wildner 			acpi_hp_evaluate_auto_on_off(sc);
7575db2f26eSSascha Wildner 			break;
7585db2f26eSSascha Wildner 		case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
7595db2f26eSSascha Wildner 			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
7605db2f26eSSascha Wildner 				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
7615db2f26eSSascha Wildner 				    arg?0x202:0x200));
7625db2f26eSSascha Wildner 		case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
7635db2f26eSSascha Wildner 			sc->bluetooth_enable_if_radio_on = arg;
7645db2f26eSSascha Wildner 			acpi_hp_evaluate_auto_on_off(sc);
7655db2f26eSSascha Wildner 			break;
7665db2f26eSSascha Wildner 		case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
7675db2f26eSSascha Wildner 			sc->bluetooth_disable_if_radio_off = arg?1:0;
7685db2f26eSSascha Wildner 			acpi_hp_evaluate_auto_on_off(sc);
7695db2f26eSSascha Wildner 			break;
7705db2f26eSSascha Wildner 		case ACPI_HP_METHOD_WWAN_ENABLED:
7715db2f26eSSascha Wildner 			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
7725db2f26eSSascha Wildner 				    ACPI_HP_WMI_WIRELESS_COMMAND, 1,
7735db2f26eSSascha Wildner 				    arg?0x404:0x400));
7745db2f26eSSascha Wildner 		case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
7755db2f26eSSascha Wildner 			sc->wwan_enable_if_radio_on = arg?1:0;
7765db2f26eSSascha Wildner 			acpi_hp_evaluate_auto_on_off(sc);
7775db2f26eSSascha Wildner 			break;
7785db2f26eSSascha Wildner 		case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
7795db2f26eSSascha Wildner 			sc->wwan_disable_if_radio_off = arg?1:0;
7805db2f26eSSascha Wildner 			acpi_hp_evaluate_auto_on_off(sc);
7815db2f26eSSascha Wildner 			break;
7825db2f26eSSascha Wildner 		case ACPI_HP_METHOD_ALS:
7835db2f26eSSascha Wildner 			return (acpi_hp_exec_wmi_command(sc->wmi_dev,
7845db2f26eSSascha Wildner 				    ACPI_HP_WMI_ALS_COMMAND, 1,
7855db2f26eSSascha Wildner 				    arg?1:0));
7865db2f26eSSascha Wildner 		case ACPI_HP_METHOD_CMI_DETAIL:
7875db2f26eSSascha Wildner 			sc->cmi_detail = arg;
7885db2f26eSSascha Wildner 			if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) !=
7895db2f26eSSascha Wildner 			    (oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) {
7905db2f26eSSascha Wildner 			    sc->cmi_order_size = -1;
7915db2f26eSSascha Wildner 			}
7925db2f26eSSascha Wildner 			break;
7935db2f26eSSascha Wildner 		case ACPI_HP_METHOD_VERBOSE:
7945db2f26eSSascha Wildner 			sc->verbose = arg;
7955db2f26eSSascha Wildner 			break;
7965db2f26eSSascha Wildner 		}
7975db2f26eSSascha Wildner 	}
7985db2f26eSSascha Wildner 
7995db2f26eSSascha Wildner 	return (0);
8005db2f26eSSascha Wildner }
8015db2f26eSSascha Wildner 
8025db2f26eSSascha Wildner static __inline void
acpi_hp_free_buffer(ACPI_BUFFER * buf)8035db2f26eSSascha Wildner acpi_hp_free_buffer(ACPI_BUFFER* buf) {
8045db2f26eSSascha Wildner 	if (buf && buf->Pointer) {
8055db2f26eSSascha Wildner 		AcpiOsFree(buf->Pointer);
8065db2f26eSSascha Wildner 	}
8075db2f26eSSascha Wildner }
8085db2f26eSSascha Wildner 
8095db2f26eSSascha Wildner static void
acpi_hp_notify(ACPI_HANDLE h,UINT32 notify,void * context)8105db2f26eSSascha Wildner acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context)
8115db2f26eSSascha Wildner {
8125db2f26eSSascha Wildner 	device_t dev = context;
8135db2f26eSSascha Wildner 	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
8145db2f26eSSascha Wildner 
8155db2f26eSSascha Wildner 	struct acpi_hp_softc *sc = device_get_softc(dev);
8165db2f26eSSascha Wildner 	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
8175db2f26eSSascha Wildner 	ACPI_OBJECT *obj;
8185db2f26eSSascha Wildner 	ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
8195db2f26eSSascha Wildner 	obj = (ACPI_OBJECT*) response.Pointer;
8205db2f26eSSascha Wildner 	if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) {
8215db2f26eSSascha Wildner 		if (*((UINT8 *) obj->Buffer.Pointer) == 0x5) {
8225db2f26eSSascha Wildner 			acpi_hp_evaluate_auto_on_off(sc);
8235db2f26eSSascha Wildner 		}
8245db2f26eSSascha Wildner 	}
8255db2f26eSSascha Wildner 	acpi_hp_free_buffer(&response);
8265db2f26eSSascha Wildner }
8275db2f26eSSascha Wildner 
8285db2f26eSSascha Wildner static int
acpi_hp_exec_wmi_command(device_t wmi_dev,int command,int is_write,int val)8295db2f26eSSascha Wildner acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val)
8305db2f26eSSascha Wildner {
8315db2f26eSSascha Wildner 	UINT32		params[5] = { 0x55434553,
8325db2f26eSSascha Wildner 			    is_write?2:1,
8335db2f26eSSascha Wildner 			    command,
8345db2f26eSSascha Wildner 			    is_write?4:0,
8355db2f26eSSascha Wildner 			    val};
8365db2f26eSSascha Wildner 	UINT32*		result;
8375db2f26eSSascha Wildner 	ACPI_OBJECT	*obj;
8385db2f26eSSascha Wildner 	ACPI_BUFFER	in = { sizeof(params), &params };
8395db2f26eSSascha Wildner 	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
8405db2f26eSSascha Wildner 	int retval;
8415db2f26eSSascha Wildner 
8425db2f26eSSascha Wildner 	if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID,
8435db2f26eSSascha Wildner 		    0, 0x3, &in, &out))) {
8445db2f26eSSascha Wildner 		acpi_hp_free_buffer(&out);
8455db2f26eSSascha Wildner 		return (-EINVAL);
8465db2f26eSSascha Wildner 	}
8475db2f26eSSascha Wildner 	obj = out.Pointer;
8485db2f26eSSascha Wildner 	if (!obj || obj->Type != ACPI_TYPE_BUFFER) {
8495db2f26eSSascha Wildner 		acpi_hp_free_buffer(&out);
8505db2f26eSSascha Wildner 		return (-EINVAL);
8515db2f26eSSascha Wildner 	}
8525db2f26eSSascha Wildner 	result = (UINT32*) obj->Buffer.Pointer;
8535db2f26eSSascha Wildner 	retval = result[2];
8545db2f26eSSascha Wildner 	if (result[1] > 0) {
8555db2f26eSSascha Wildner 		retval = result[1];
8565db2f26eSSascha Wildner 	}
8575db2f26eSSascha Wildner 	acpi_hp_free_buffer(&out);
8585db2f26eSSascha Wildner 
8595db2f26eSSascha Wildner 	return (retval);
8605db2f26eSSascha Wildner }
8615db2f26eSSascha Wildner 
8625db2f26eSSascha Wildner static __inline char*
acpi_hp_get_string_from_object(ACPI_OBJECT * obj,char * dst,size_t size)8635db2f26eSSascha Wildner acpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) {
8645db2f26eSSascha Wildner 	int	length;
8655db2f26eSSascha Wildner 
8665db2f26eSSascha Wildner 	dst[0] = 0;
8675db2f26eSSascha Wildner 	if (obj->Type == ACPI_TYPE_STRING) {
8685db2f26eSSascha Wildner 		length = obj->String.Length+1;
8695db2f26eSSascha Wildner 		if (length > size) {
8705db2f26eSSascha Wildner 			length = size - 1;
8715db2f26eSSascha Wildner 		}
8725db2f26eSSascha Wildner 		strlcpy(dst, obj->String.Pointer, length);
8735db2f26eSSascha Wildner 		acpi_hp_hex_decode(dst);
8745db2f26eSSascha Wildner 	}
8755db2f26eSSascha Wildner 
8765db2f26eSSascha Wildner 	return (dst);
8775db2f26eSSascha Wildner }
8785db2f26eSSascha Wildner 
8795db2f26eSSascha Wildner 
8805db2f26eSSascha Wildner /*
8815db2f26eSSascha Wildner  * Read BIOS Setting block in instance "instance".
8825db2f26eSSascha Wildner  * The block returned is ACPI_TYPE_PACKAGE which should contain the following
8835db2f26eSSascha Wildner  * elements:
8845db2f26eSSascha Wildner  * Index Meaning
8855db2f26eSSascha Wildner  * 0        Setting Name [string]
8865db2f26eSSascha Wildner  * 1        Value (comma separated, asterisk marks the current value) [string]
8875db2f26eSSascha Wildner  * 2        Path within the bios hierarchy [string]
8885db2f26eSSascha Wildner  * 3        IsReadOnly [int]
8895db2f26eSSascha Wildner  * 4        DisplayInUI [int]
8905db2f26eSSascha Wildner  * 5        RequiresPhysicalPresence [int]
8915db2f26eSSascha Wildner  * 6        Sequence for ordering within the bios settings (absolute) [int]
8925db2f26eSSascha Wildner  * 7        Length of prerequisites array [int]
8935db2f26eSSascha Wildner  * 8..8+[7] PrerequisiteN [string]
8945db2f26eSSascha Wildner  * 9+[7]    Current value (in case of enum) [string] / Array length [int]
8955db2f26eSSascha Wildner  * 10+[7]   Enum length [int] / Array values
8965db2f26eSSascha Wildner  * 11+[7]ff Enum value at index x [string]
8975db2f26eSSascha Wildner  */
8985db2f26eSSascha Wildner static int
acpi_hp_get_cmi_block(device_t wmi_dev,const char * guid,UINT8 instance,char * outbuf,size_t outsize,UINT32 * sequence,int detail)8995db2f26eSSascha Wildner acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance,
9005db2f26eSSascha Wildner     char* outbuf, size_t outsize, UINT32* sequence, int detail)
9015db2f26eSSascha Wildner {
9025db2f26eSSascha Wildner 	ACPI_OBJECT	*obj;
9035db2f26eSSascha Wildner 	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
9045db2f26eSSascha Wildner 	int		i;
9055db2f26eSSascha Wildner 	int		outlen;
9065db2f26eSSascha Wildner 	int		size = 255;
9075db2f26eSSascha Wildner 	int		has_enums = 0;
9085db2f26eSSascha Wildner 	int		valuebase = 0;
9095db2f26eSSascha Wildner 	char		string_buffer[size];
9105db2f26eSSascha Wildner 	int		enumbase;
9115db2f26eSSascha Wildner 
91257e09377SMatthew Dillon 	*sequence = 0;	/* whack gcc warning */
9135db2f26eSSascha Wildner 	outlen = 0;
9145db2f26eSSascha Wildner 	outbuf[0] = 0;
9155db2f26eSSascha Wildner 	if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) {
9165db2f26eSSascha Wildner 		acpi_hp_free_buffer(&out);
9175db2f26eSSascha Wildner 		return (-EINVAL);
9185db2f26eSSascha Wildner 	}
9195db2f26eSSascha Wildner 	obj = out.Pointer;
9205db2f26eSSascha Wildner 	if (!obj || obj->Type != ACPI_TYPE_PACKAGE) {
9215db2f26eSSascha Wildner 		acpi_hp_free_buffer(&out);
9225db2f26eSSascha Wildner 		return (-EINVAL);
9235db2f26eSSascha Wildner 	}
9245db2f26eSSascha Wildner 
9255db2f26eSSascha Wildner 	if (obj->Package.Count >= 8 &&
9265db2f26eSSascha Wildner 	    obj->Package.Elements[7].Type == ACPI_TYPE_INTEGER) {
9275db2f26eSSascha Wildner 	    valuebase = 8 + obj->Package.Elements[7].Integer.Value;
9285db2f26eSSascha Wildner 	}
9295db2f26eSSascha Wildner 
9305db2f26eSSascha Wildner 	/* check if this matches our expectations based on limited knowledge */
9315db2f26eSSascha Wildner 	if (valuebase > 7 && obj->Package.Count > valuebase + 1 &&
9325db2f26eSSascha Wildner 	    obj->Package.Elements[0].Type == ACPI_TYPE_STRING &&
9335db2f26eSSascha Wildner 	    obj->Package.Elements[1].Type == ACPI_TYPE_STRING &&
9345db2f26eSSascha Wildner 	    obj->Package.Elements[2].Type == ACPI_TYPE_STRING &&
9355db2f26eSSascha Wildner 	    obj->Package.Elements[3].Type == ACPI_TYPE_INTEGER &&
9365db2f26eSSascha Wildner 	    obj->Package.Elements[4].Type == ACPI_TYPE_INTEGER &&
9375db2f26eSSascha Wildner 	    obj->Package.Elements[5].Type == ACPI_TYPE_INTEGER &&
9385db2f26eSSascha Wildner 	    obj->Package.Elements[6].Type == ACPI_TYPE_INTEGER &&
9395db2f26eSSascha Wildner 	    obj->Package.Elements[valuebase].Type == ACPI_TYPE_STRING &&
9405db2f26eSSascha Wildner 	    obj->Package.Elements[valuebase+1].Type == ACPI_TYPE_INTEGER &&
9415db2f26eSSascha Wildner 	    obj->Package.Count > valuebase +
9425db2f26eSSascha Wildner 	        obj->Package.Elements[valuebase+1].Integer.Value
9435db2f26eSSascha Wildner 	   ) {
9445db2f26eSSascha Wildner 		enumbase = valuebase + 1;
9455db2f26eSSascha Wildner 		if (detail & ACPI_HP_CMI_DETAIL_PATHS) {
9465db2f26eSSascha Wildner 			strlcat(outbuf, acpi_hp_get_string_from_object(
9475db2f26eSSascha Wildner 				&obj->Package.Elements[2], string_buffer, size),
9485db2f26eSSascha Wildner 				outsize);
9495db2f26eSSascha Wildner 			outlen += 48;
9505db2f26eSSascha Wildner 			while (strlen(outbuf) < outlen)
9515db2f26eSSascha Wildner 				strlcat(outbuf, " ", outsize);
9525db2f26eSSascha Wildner 		}
9535db2f26eSSascha Wildner 		strlcat(outbuf, acpi_hp_get_string_from_object(
9545db2f26eSSascha Wildner 				&obj->Package.Elements[0], string_buffer, size),
9555db2f26eSSascha Wildner 				outsize);
9565db2f26eSSascha Wildner 		outlen += 43;
9575db2f26eSSascha Wildner 		while (strlen(outbuf) < outlen)
9585db2f26eSSascha Wildner 			strlcat(outbuf, " ", outsize);
9595db2f26eSSascha Wildner 		strlcat(outbuf, acpi_hp_get_string_from_object(
9605db2f26eSSascha Wildner 				&obj->Package.Elements[valuebase], string_buffer,
9615db2f26eSSascha Wildner 				size),
9625db2f26eSSascha Wildner 				outsize);
9635db2f26eSSascha Wildner 		outlen += 21;
9645db2f26eSSascha Wildner 		while (strlen(outbuf) < outlen)
9655db2f26eSSascha Wildner 			strlcat(outbuf, " ", outsize);
9665db2f26eSSascha Wildner 		for (i = 0; i < strlen(outbuf); ++i)
9675db2f26eSSascha Wildner 			if (outbuf[i] == '\\')
9685db2f26eSSascha Wildner 				outbuf[i] = '/';
9695db2f26eSSascha Wildner 		if (detail & ACPI_HP_CMI_DETAIL_ENUMS) {
9705db2f26eSSascha Wildner 			for (i = enumbase + 1; i < enumbase + 1 +
9715db2f26eSSascha Wildner 			    obj->Package.Elements[enumbase].Integer.Value;
9725db2f26eSSascha Wildner 			    ++i) {
9735db2f26eSSascha Wildner 				acpi_hp_get_string_from_object(
9745db2f26eSSascha Wildner 				    &obj->Package.Elements[i], string_buffer,
9755db2f26eSSascha Wildner 				    size);
9765db2f26eSSascha Wildner 				if (strlen(string_buffer) > 1 ||
9775db2f26eSSascha Wildner 				    (strlen(string_buffer) == 1 &&
9785db2f26eSSascha Wildner 				    string_buffer[0] != ' ')) {
9795db2f26eSSascha Wildner 					if (has_enums)
9805db2f26eSSascha Wildner 						strlcat(outbuf, "/", outsize);
9815db2f26eSSascha Wildner 					else
9825db2f26eSSascha Wildner 						strlcat(outbuf, " (", outsize);
9835db2f26eSSascha Wildner 					strlcat(outbuf, string_buffer, outsize);
9845db2f26eSSascha Wildner 					has_enums = 1;
9855db2f26eSSascha Wildner 				}
9865db2f26eSSascha Wildner 			}
9875db2f26eSSascha Wildner 		}
9885db2f26eSSascha Wildner 		if (has_enums)
9895db2f26eSSascha Wildner 			strlcat(outbuf, ")", outsize);
9905db2f26eSSascha Wildner 		if (detail & ACPI_HP_CMI_DETAIL_FLAGS) {
9915db2f26eSSascha Wildner 			strlcat(outbuf, obj->Package.Elements[3].Integer.Value?
9925db2f26eSSascha Wildner 			    " [ReadOnly]":"", outsize);
9935db2f26eSSascha Wildner 			strlcat(outbuf, obj->Package.Elements[4].Integer.Value?
9945db2f26eSSascha Wildner 			    "":" [NOUI]", outsize);
9955db2f26eSSascha Wildner 			strlcat(outbuf, obj->Package.Elements[5].Integer.Value?
9965db2f26eSSascha Wildner 			    " [RPP]":"", outsize);
9975db2f26eSSascha Wildner 		}
9985db2f26eSSascha Wildner 		*sequence = (UINT32) obj->Package.Elements[6].Integer.Value;
9995db2f26eSSascha Wildner 	}
10005db2f26eSSascha Wildner 	acpi_hp_free_buffer(&out);
10015db2f26eSSascha Wildner 
10025db2f26eSSascha Wildner 	return (0);
10035db2f26eSSascha Wildner }
10045db2f26eSSascha Wildner 
10055db2f26eSSascha Wildner 
10065db2f26eSSascha Wildner 
10075db2f26eSSascha Wildner /*
10085db2f26eSSascha Wildner  * Convert given two digit hex string (hexin) to an UINT8 referenced
10095db2f26eSSascha Wildner  * by byteout.
10105db2f26eSSascha Wildner  * Return != 0 if the was a problem (invalid input)
10115db2f26eSSascha Wildner  */
acpi_hp_hex_to_int(const UINT8 * hexin,UINT8 * byteout)10125db2f26eSSascha Wildner static __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout)
10135db2f26eSSascha Wildner {
10145db2f26eSSascha Wildner 	unsigned int	hi;
10155db2f26eSSascha Wildner 	unsigned int	lo;
10165db2f26eSSascha Wildner 
10175db2f26eSSascha Wildner 	hi = hexin[0];
10185db2f26eSSascha Wildner 	lo = hexin[1];
10195db2f26eSSascha Wildner 	if ('0' <= hi && hi <= '9')
10205db2f26eSSascha Wildner 		hi -= '0';
10215db2f26eSSascha Wildner 	else if ('A' <= hi && hi <= 'F')
10225db2f26eSSascha Wildner 		hi -= ('A' - 10);
10235db2f26eSSascha Wildner 	else if ('a' <= hi && hi <= 'f')
10245db2f26eSSascha Wildner 		hi -= ('a' - 10);
10255db2f26eSSascha Wildner 	else
10265db2f26eSSascha Wildner 		return (1);
10275db2f26eSSascha Wildner 	if ('0' <= lo && lo <= '9')
10285db2f26eSSascha Wildner 		lo -= '0';
10295db2f26eSSascha Wildner 	else if ('A' <= lo && lo <= 'F')
10305db2f26eSSascha Wildner 		lo -= ('A' - 10);
10315db2f26eSSascha Wildner 	else if ('a' <= lo && lo <= 'f')
10325db2f26eSSascha Wildner 		lo -= ('a' - 10);
10335db2f26eSSascha Wildner 	else
10345db2f26eSSascha Wildner 		return (1);
10355db2f26eSSascha Wildner 	*byteout = (hi << 4) + lo;
10365db2f26eSSascha Wildner 
10375db2f26eSSascha Wildner 	return (0);
10385db2f26eSSascha Wildner }
10395db2f26eSSascha Wildner 
10405db2f26eSSascha Wildner 
10415db2f26eSSascha Wildner static void
acpi_hp_hex_decode(char * buffer)10425db2f26eSSascha Wildner acpi_hp_hex_decode(char* buffer)
10435db2f26eSSascha Wildner {
10445db2f26eSSascha Wildner 	int	i;
10455db2f26eSSascha Wildner 	int	length = strlen(buffer);
10465db2f26eSSascha Wildner 	UINT8	*uin;
10475db2f26eSSascha Wildner 	UINT8	uout;
10485db2f26eSSascha Wildner 
1049*ed183f8cSSascha Wildner 	if (rounddown((int)length, 2) == length || length < 10) return;
10505db2f26eSSascha Wildner 
10515db2f26eSSascha Wildner 	for (i = 0; i<length; ++i) {
10525db2f26eSSascha Wildner 		if (!((i+1)%3)) {
10535db2f26eSSascha Wildner 			if (buffer[i] != ' ')
10545db2f26eSSascha Wildner 				return;
10555db2f26eSSascha Wildner 		}
10565db2f26eSSascha Wildner 		else
10575db2f26eSSascha Wildner 			if (!((buffer[i] >= '0' && buffer[i] <= '9') ||
10585db2f26eSSascha Wildner 		    	    (buffer[i] >= 'A' && buffer[i] <= 'F')))
10595db2f26eSSascha Wildner 				return;
10605db2f26eSSascha Wildner 	}
10615db2f26eSSascha Wildner 
10625db2f26eSSascha Wildner 	for (i = 0; i<length; i += 3) {
10635db2f26eSSascha Wildner 		uin = &buffer[i];
10645db2f26eSSascha Wildner 		uout = 0;
10655db2f26eSSascha Wildner 		acpi_hp_hex_to_int(uin, &uout);
10665db2f26eSSascha Wildner 		buffer[i/3] = (char) uout;
10675db2f26eSSascha Wildner 	}
10685db2f26eSSascha Wildner 	buffer[(length+1)/3] = 0;
10695db2f26eSSascha Wildner }
10705db2f26eSSascha Wildner 
10715db2f26eSSascha Wildner 
10725db2f26eSSascha Wildner /*
10735db2f26eSSascha Wildner  * open hpcmi device
10745db2f26eSSascha Wildner  */
10755db2f26eSSascha Wildner static int
acpi_hp_hpcmi_open(struct dev_open_args * ap)10765db2f26eSSascha Wildner acpi_hp_hpcmi_open(struct dev_open_args *ap)
10775db2f26eSSascha Wildner {
10785db2f26eSSascha Wildner 	struct acpi_hp_softc	*sc;
10795db2f26eSSascha Wildner 	int			ret;
10805db2f26eSSascha Wildner 	struct cdev *dev = ap->a_head.a_dev;
10815db2f26eSSascha Wildner 	struct thread *td = curthread;
10825db2f26eSSascha Wildner 
10835db2f26eSSascha Wildner 	if (dev == NULL || dev->si_drv1 == NULL)
10845db2f26eSSascha Wildner 		return (EBADF);
10855db2f26eSSascha Wildner 	sc = dev->si_drv1;
10865db2f26eSSascha Wildner 
10875db2f26eSSascha Wildner 	ACPI_SERIAL_BEGIN(hp);
10885db2f26eSSascha Wildner 	if (sc->hpcmi_open_pid != 0) {
10895db2f26eSSascha Wildner 		ret = EBUSY;
10905db2f26eSSascha Wildner 	}
10915db2f26eSSascha Wildner 	else {
10925db2f26eSSascha Wildner 		if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND)
10935db2f26eSSascha Wildner 		    == NULL) {
10945db2f26eSSascha Wildner 			ret = ENXIO;
10955db2f26eSSascha Wildner 		} else {
10965db2f26eSSascha Wildner 			sc->hpcmi_open_pid = td->td_proc->p_pid;
10975db2f26eSSascha Wildner 			sc->hpcmi_bufptr = 0;
10985db2f26eSSascha Wildner 			ret = 0;
10995db2f26eSSascha Wildner 		}
11005db2f26eSSascha Wildner 	}
11015db2f26eSSascha Wildner 	ACPI_SERIAL_END(hp);
11025db2f26eSSascha Wildner 
11035db2f26eSSascha Wildner 	return (ret);
11045db2f26eSSascha Wildner }
11055db2f26eSSascha Wildner 
11065db2f26eSSascha Wildner /*
11075db2f26eSSascha Wildner  * close hpcmi device
11085db2f26eSSascha Wildner  */
11095db2f26eSSascha Wildner static int
acpi_hp_hpcmi_close(struct dev_close_args * ap)11105db2f26eSSascha Wildner acpi_hp_hpcmi_close(struct dev_close_args *ap)
11115db2f26eSSascha Wildner {
11125db2f26eSSascha Wildner 	struct acpi_hp_softc	*sc;
11135db2f26eSSascha Wildner 	int			ret;
11145db2f26eSSascha Wildner 	struct cdev *dev = ap->a_head.a_dev;
11155db2f26eSSascha Wildner 
11165db2f26eSSascha Wildner 	if (dev == NULL || dev->si_drv1 == NULL)
11175db2f26eSSascha Wildner 		return (EBADF);
11185db2f26eSSascha Wildner 	sc = dev->si_drv1;
11195db2f26eSSascha Wildner 
11205db2f26eSSascha Wildner 	ACPI_SERIAL_BEGIN(hp);
11215db2f26eSSascha Wildner 	if (sc->hpcmi_open_pid == 0) {
11225db2f26eSSascha Wildner 		ret = EBADF;
11235db2f26eSSascha Wildner 	}
11245db2f26eSSascha Wildner 	else {
11255db2f26eSSascha Wildner 		if (sc->hpcmi_bufptr != -1) {
11265db2f26eSSascha Wildner 			sbuf_delete(&sc->hpcmi_sbuf);
11275db2f26eSSascha Wildner 			sc->hpcmi_bufptr = -1;
11285db2f26eSSascha Wildner 		}
11295db2f26eSSascha Wildner 		sc->hpcmi_open_pid = 0;
11305db2f26eSSascha Wildner 		ret = 0;
11315db2f26eSSascha Wildner 	}
11325db2f26eSSascha Wildner 	ACPI_SERIAL_END(hp);
11335db2f26eSSascha Wildner 
11345db2f26eSSascha Wildner 	return (ret);
11355db2f26eSSascha Wildner }
11365db2f26eSSascha Wildner 
11375db2f26eSSascha Wildner /*
11385db2f26eSSascha Wildner  * Read from hpcmi bios information
11395db2f26eSSascha Wildner  */
11405db2f26eSSascha Wildner static int
acpi_hp_hpcmi_read(struct dev_read_args * ap)11415db2f26eSSascha Wildner acpi_hp_hpcmi_read(struct dev_read_args *ap)
11425db2f26eSSascha Wildner {
11435db2f26eSSascha Wildner 	struct acpi_hp_softc	*sc;
11445db2f26eSSascha Wildner 	int			pos, i, l, ret;
11455db2f26eSSascha Wildner 	UINT8			instance;
11465db2f26eSSascha Wildner 	UINT8			maxInstance;
11475db2f26eSSascha Wildner 	UINT32			sequence;
11485db2f26eSSascha Wildner 	int			linesize = 1025;
11495db2f26eSSascha Wildner 	char			line[linesize];
11505db2f26eSSascha Wildner 	struct cdev *dev = ap->a_head.a_dev;
11515db2f26eSSascha Wildner 	struct uio *buf = ap->a_uio;
11525db2f26eSSascha Wildner 
11535db2f26eSSascha Wildner 	if (dev == NULL || dev->si_drv1 == NULL)
11545db2f26eSSascha Wildner 		return (EBADF);
11555db2f26eSSascha Wildner 	sc = dev->si_drv1;
11565db2f26eSSascha Wildner 
11575db2f26eSSascha Wildner 	ACPI_SERIAL_BEGIN(hp);
11585db2f26eSSascha Wildner 	if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid
11595db2f26eSSascha Wildner 	    || sc->hpcmi_bufptr == -1) {
11605db2f26eSSascha Wildner 		ret = EBADF;
11615db2f26eSSascha Wildner 	}
11625db2f26eSSascha Wildner 	else {
11635db2f26eSSascha Wildner 		if (!sbuf_done(&sc->hpcmi_sbuf)) {
11645db2f26eSSascha Wildner 			if (sc->cmi_order_size < 0) {
11655db2f26eSSascha Wildner 				maxInstance = sc->has_cmi;
11665db2f26eSSascha Wildner 				if (!(sc->cmi_detail &
11675db2f26eSSascha Wildner 				    ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) &&
11685db2f26eSSascha Wildner 				    maxInstance > 0) {
11695db2f26eSSascha Wildner 					maxInstance--;
11705db2f26eSSascha Wildner 				}
11715db2f26eSSascha Wildner 				sc->cmi_order_size = 0;
11725db2f26eSSascha Wildner 				for (instance = 0; instance < maxInstance;
11735db2f26eSSascha Wildner 				    ++instance) {
11745db2f26eSSascha Wildner 					if (acpi_hp_get_cmi_block(sc->wmi_dev,
11755db2f26eSSascha Wildner 						ACPI_HP_WMI_CMI_GUID, instance,
11765db2f26eSSascha Wildner 						line, linesize, &sequence,
11775db2f26eSSascha Wildner 						sc->cmi_detail)) {
11785db2f26eSSascha Wildner 						instance = maxInstance;
11795db2f26eSSascha Wildner 					}
11805db2f26eSSascha Wildner 					else {
11815db2f26eSSascha Wildner 						pos = sc->cmi_order_size;
11825db2f26eSSascha Wildner 						for (i=0;
11835db2f26eSSascha Wildner 						  i<sc->cmi_order_size && i<127;
11845db2f26eSSascha Wildner 						     ++i) {
11855db2f26eSSascha Wildner 				if (sc->cmi_order[i].sequence > sequence) {
11865db2f26eSSascha Wildner 								pos = i;
11875db2f26eSSascha Wildner 								break;
11885db2f26eSSascha Wildner 							}
11895db2f26eSSascha Wildner 						}
11905db2f26eSSascha Wildner 						for (i=sc->cmi_order_size;
11915db2f26eSSascha Wildner 						    i>pos;
11925db2f26eSSascha Wildner 						    --i) {
11935db2f26eSSascha Wildner 						sc->cmi_order[i].sequence =
11945db2f26eSSascha Wildner 						    sc->cmi_order[i-1].sequence;
11955db2f26eSSascha Wildner 						sc->cmi_order[i].instance =
11965db2f26eSSascha Wildner 						    sc->cmi_order[i-1].instance;
11975db2f26eSSascha Wildner 						}
11985db2f26eSSascha Wildner 						sc->cmi_order[pos].sequence =
11995db2f26eSSascha Wildner 						    sequence;
12005db2f26eSSascha Wildner 						sc->cmi_order[pos].instance =
12015db2f26eSSascha Wildner 						    instance;
12025db2f26eSSascha Wildner 						sc->cmi_order_size++;
12035db2f26eSSascha Wildner 					}
12045db2f26eSSascha Wildner 				}
12055db2f26eSSascha Wildner 			}
12065db2f26eSSascha Wildner 			for (i=0; i<sc->cmi_order_size; ++i) {
12075db2f26eSSascha Wildner 				if (!acpi_hp_get_cmi_block(sc->wmi_dev,
12085db2f26eSSascha Wildner 				    ACPI_HP_WMI_CMI_GUID,
12095db2f26eSSascha Wildner 				    sc->cmi_order[i].instance, line, linesize,
12105db2f26eSSascha Wildner 				    &sequence, sc->cmi_detail)) {
12115db2f26eSSascha Wildner 					sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line);
12125db2f26eSSascha Wildner 				}
12135db2f26eSSascha Wildner 			}
12145db2f26eSSascha Wildner 			sbuf_finish(&sc->hpcmi_sbuf);
12155db2f26eSSascha Wildner 		}
12165db2f26eSSascha Wildner 		if (sbuf_len(&sc->hpcmi_sbuf) <= 0) {
12175db2f26eSSascha Wildner 			sbuf_delete(&sc->hpcmi_sbuf);
12185db2f26eSSascha Wildner 			sc->hpcmi_bufptr = -1;
12195db2f26eSSascha Wildner 			sc->hpcmi_open_pid = 0;
12205db2f26eSSascha Wildner 			ret = ENOMEM;
12215db2f26eSSascha Wildner 		} else {
12225db2f26eSSascha Wildner 			l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) -
12235db2f26eSSascha Wildner 			    sc->hpcmi_bufptr);
12245db2f26eSSascha Wildner 			ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) +
12255db2f26eSSascha Wildner 			    sc->hpcmi_bufptr, l, buf) : 0;
12265db2f26eSSascha Wildner 			sc->hpcmi_bufptr += l;
12275db2f26eSSascha Wildner 		}
12285db2f26eSSascha Wildner 	}
12295db2f26eSSascha Wildner 	ACPI_SERIAL_END(hp);
12305db2f26eSSascha Wildner 
12315db2f26eSSascha Wildner 	return (ret);
12325db2f26eSSascha Wildner }
1233