Patchwork [2/2] oem: the kernel module creates an ioctl sys nodes for testing hp's WMI interface. This interface currently supports WMI command 0x1b, including get and set functions.

login
register
mail settings
Submitter Alex Hung
Date Dec. 10, 2012, 2:29 a.m.
Message ID <1355106555-21026-3-git-send-email-alex.hung@canonical.com>
Download mbox | patch
Permalink /patch/204813/
State Rejected
Headers show

Comments

Alex Hung - Dec. 10, 2012, 2:29 a.m.
Signed-off-by: Alex Hung <alex.hung@canonical.com>
---
 oem/Makefile |    6 +
 oem/hp-wmi.c |  533 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 539 insertions(+)
 create mode 100644 oem/Makefile
 create mode 100644 oem/hp-wmi.c
Alex Hung - Dec. 10, 2012, 6:41 a.m.
Hi,

I found out that I didn't include a .h file. I will re-send the patch.

On 12/10/2012 10:29 AM, Alex Hung wrote:
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>   oem/Makefile |    6 +
>   oem/hp-wmi.c |  533 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 539 insertions(+)
>   create mode 100644 oem/Makefile
>   create mode 100644 oem/hp-wmi.c
>
> diff --git a/oem/Makefile b/oem/Makefile
> new file mode 100644
> index 0000000..ab73846
> --- /dev/null
> +++ b/oem/Makefile
> @@ -0,0 +1,6 @@
> +obj-m += hp-wmi.o
> +all:
> +	make -C /lib/modules/`uname -r`/build M=`pwd` modules
> +
> +clean:
> +	make -C /lib/modules/`uname -r`/build M=`pwd` clean
> diff --git a/oem/hp-wmi.c b/oem/hp-wmi.c
> new file mode 100644
> index 0000000..eff6faf
> --- /dev/null
> +++ b/oem/hp-wmi.c
> @@ -0,0 +1,533 @@
> +/*
> + * Copyright (C) 2011-2012 Canonical
> + *
> + * Portions based on hp-wmi.c:
> + * Copyright (C) 2008 Red Hat <mjg@redhat.com>
> + * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME "-fwts: " fmt
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/acpi.h>
> +#include <linux/string.h>
> +
> +#include <linux/proc_fs.h>
> +#include <linux/miscdevice.h>
> +
> +#include "fwts-oem.h"
> +
> +MODULE_AUTHOR("Alex Hung <alex.hung@canonical.com>");
> +MODULE_LICENSE("GPL");
> +
> +MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
> +MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
> +
> +#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
> +#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
> +
> +#define HPWMI_DISPLAY_QUERY	0x1
> +#define HPWMI_HDDTEMP_QUERY	0x2
> +#define HPWMI_ALS_QUERY		0x3
> +#define HPWMI_HARDWARE_QUERY	0x4
> +#define HPWMI_WIRELESS_QUERY	0x5
> +#define HPWMI_HOTKEY_QUERY	0xc
> +#define HPWMI_WIRELESS2_QUERY	0x1b
> +
> +enum hp_wmi_radio {
> +	HPWMI_WIFI = 0,
> +	HPWMI_BLUETOOTH = 1,
> +	HPWMI_WWAN = 2,
> +};
> +
> +enum hp_wmi_event_ids {
> +	HPWMI_DOCK_EVENT = 1,
> +	HPWMI_PARK_HDD = 2,
> +	HPWMI_SMART_ADAPTER = 3,
> +	HPWMI_BEZEL_BUTTON = 4,
> +	HPWMI_WIRELESS = 5,
> +	HPWMI_CPU_BATTERY_THROTTLE = 6,
> +	HPWMI_LOCK_SWITCH = 7,
> +};
> +
> +static int __devinit hp_wmi_bios_setup(struct platform_device *device);
> +static int __exit hp_wmi_bios_remove(struct platform_device *device);
> +static int hp_wmi_resume_handler(struct device *device);
> +
> +struct bios_args {
> +	u32 signature;
> +	u32 command;
> +	u32 commandtype;
> +	u32 datasize;
> +	u32 data;
> +};
> +
> +struct bios_return {
> +	u32 sigpass;
> +	u32 return_code;
> +};
> +
> +enum hp_return_value {
> +	HPWMI_RET_WRONG_SIGNATURE	= 0x02,
> +	HPWMI_RET_UNKNOWN_COMMAND	= 0x03,
> +	HPWMI_RET_UNKNOWN_CMDTYPE	= 0x04,
> +	HPWMI_RET_INVALID_PARAMETERS	= 0x05,
> +};
> +
> +enum hp_wireless2_bits {
> +	HPWMI_POWER_STATE	= 0x01,
> +	HPWMI_POWER_SOFT	= 0x02,
> +	HPWMI_POWER_BIOS	= 0x04,
> +	HPWMI_POWER_HARD	= 0x08,
> +};
> +
> +#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
> +			 != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
> +#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
> +
> +struct bios_rfkill2_device_state {
> +	u8 radio_type;
> +	u8 bus_type;
> +	u16 vendor_id;
> +	u16 product_id;
> +	u16 subsys_vendor_id;
> +	u16 subsys_product_id;
> +	u8 rfkill_id;
> +	u8 power;
> +	u8 unknown[4];
> +};
> +
> +/* 7 devices fit into the 128 byte buffer */
> +#define HPWMI_MAX_RFKILL2_DEVICES	7
> +
> +struct bios_rfkill2_state {
> +	u8 unknown[7];
> +	u8 count;
> +	u8 pad[8];
> +	struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
> +};
> +
> +static struct platform_device *hp_wmi_platform_dev;
> +
> +static const struct dev_pm_ops hp_wmi_pm_ops = {
> +	.resume  = hp_wmi_resume_handler,
> +	.restore  = hp_wmi_resume_handler,
> +};
> +
> +static struct platform_driver hp_wmi_driver = {
> +	.driver = {
> +		.name = "hp-wmi",
> +		.owner = THIS_MODULE,
> +		.pm = &hp_wmi_pm_ops,
> +	},
> +	.probe = hp_wmi_bios_setup,
> +	.remove = hp_wmi_bios_remove,
> +};
> +
> +static int hp_wmi_perform_query(int query, int write, void *buffer,
> +				int insize, int outsize)
> +{
> +	struct bios_return *bios_return;
> +	int actual_outsize;
> +	union acpi_object *obj;
> +	struct bios_args args = {
> +		.signature = 0x55434553,
> +		.command = write ? 0x2 : 0x1,
> +		.commandtype = query,
> +		.datasize = insize,
> +		.data = 0,
> +	};
> +	struct acpi_buffer input = { sizeof(struct bios_args), &args };
> +	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> +	u32 rc;
> +
> +	if (WARN_ON(insize > sizeof(args.data)))
> +		return -EINVAL;
> +	memcpy(&args.data, buffer, insize);
> +
> +	wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
> +
> +	obj = output.pointer;
> +
> +	if (!obj)
> +		return -EINVAL;
> +	else if (obj->type != ACPI_TYPE_BUFFER) {
> +		kfree(obj);
> +		return -EINVAL;
> +	}
> +
> +	bios_return = (struct bios_return *)obj->buffer.pointer;
> +	rc = bios_return->return_code;
> +
> +	if (rc) {
> +		if (rc != HPWMI_RET_UNKNOWN_CMDTYPE)
> +			pr_warn("query 0x%x returned error 0x%x\n", query, rc);
> +		kfree(obj);
> +		return rc;
> +	}
> +
> +	if (!outsize) {
> +		/* ignore output data */
> +		kfree(obj);
> +		return 0;
> +	}
> +
> +	actual_outsize = min(outsize,
> +			(int)(obj->buffer.length - sizeof(*bios_return)));
> +	memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return),
> +							actual_outsize);
> +	memset(buffer + actual_outsize, 0, outsize - actual_outsize);
> +	kfree(obj);
> +	return 0;
> +}
> +
> +static int hp_wmi_type_1b_write(u8 power_ctrl, int state)
> +{
> +	char buffer[4] = { 0x01, 0x00, power_ctrl, state };
> +
> +	if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1,
> +				buffer, sizeof(buffer), 0)) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hp_wmi_type_1b_read(struct bios_rfkill2_state *rf_info)
> +{
> +	int err;
> +
> +	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, rf_info,
> +			0, sizeof(struct bios_rfkill2_state));
> +	if (err)
> +		return err;
> +	
> +	return 0;
> +}
> +
> +static int get_wireless_dev_structure_wmi_1b(
> +	struct bios_rfkill2_device_state *rfkill_dev, enum hp_wmi_radio r)
> +{
> +	struct bios_rfkill2_state rf_info;
> +	struct bios_rfkill2_device_state *dev = NULL;
> +	int err, i;
> +	bool found = false;
> +
> +	err = hp_wmi_type_1b_read(&rf_info);
> +	if (err)
> +		return -EINVAL;
> +	
> +	for (i = 0; i < rf_info.count; i++) {
> +		dev = &rf_info.device[i];
> +		if (dev->radio_type == r) {
> +			found = true;
> +			break;
> +		}
> +	}
> +	if (!found)
> +		return FWTS_NOT_PRESENT;
> +	memcpy(rfkill_dev, dev, sizeof(struct bios_rfkill2_device_state));
> +
> +	return 0;
> +}
> +
> +static int convert_wireless_device_type(int radio)
> +{
> +	int dev_type = HPWMI_WIFI;
> +
> +	switch (radio) {
> +	case FWTS_WIFI:
> +		dev_type = HPWMI_WIFI;
> +		break;
> +	case FWTS_BLUETOOTH:
> +		dev_type = HPWMI_BLUETOOTH;
> +		break;
> +	case FWTS_WWAN:
> +		dev_type = HPWMI_WWAN;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return dev_type;
> +}
> +
> +static int get_wireless_device(fwts_oem_wireless __user *wd)
> +{
> +	struct bios_rfkill2_device_state rfdev;
> +	int err;
> +	int dev_type;
> +
> +	dev_type = convert_wireless_device_type(wd->device.device_type);
> +	err = get_wireless_dev_structure_wmi_1b(&rfdev, dev_type);
> +	if (err == FWTS_NOT_PRESENT) {
> +		wd->oem_parameter.func_status = FWTS_NOT_PRESENT;
> +		return 0;
> +	} else if (err == FWTS_SUCCESS) {
> +		wd->oem_parameter.func_status = FWTS_SUCCESS;
> +		wd->device.device_bus = rfdev.bus_type;
> +		wd->device.vendor_id = rfdev.vendor_id;
> +		wd->device.device_id = rfdev.product_id;
> +		wd->device.soft_kill_status = IS_SWBLOCKED(rfdev.power);
> +		wd->device.hard_kill_status = IS_HWBLOCKED(rfdev.power);
> +		return 0;
> +	}
> +
> +	return err;
> +}
> +
> +static int set_wireless_device(fwts_oem_wireless __user *wd)
> +{
> +	int err;
> +	struct bios_rfkill2_device_state dev;
> +	int dev_type;
> +	int target_power;
> +
> +	dev_type = convert_wireless_device_type(wd->device.device_type);
> +	err = get_wireless_dev_structure_wmi_1b(&dev, dev_type);
> +	if (err)
> +		return err;
> +	target_power = !wd->device.soft_kill_status;
> +	err = hp_wmi_type_1b_write(dev.rfkill_id, target_power);
> +	if (err)
> +		return err;
> +
> +	return 0;
> +}
> +
> +static int handle_oem_wireless_cmd(fwts_oem_wireless __user *wd)
> +{
> +	int ret = 0;
> +	u8 cmd;
> +
> +	cmd = wd->oem_parameter.func;
> +	switch(cmd) {
> +	case GET_DEVICE:
> +		ret = get_wireless_device(wd);
> +		break;
> +	case SET_DEVICE:
> +		ret = set_wireless_device(wd);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static long fwts_oem_runtime_ioctl(struct file *file, unsigned int cmd,
> +							unsigned long arg)
> +{
> +	switch(cmd) {
> +	case OEM_WIRELESS_CMD:
> +		handle_oem_wireless_cmd((fwts_oem_wireless __user *) arg);
> +		break;
> +	default:
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static int fwts_oem_runtime_open(struct inode *inode, struct file *file)
> +{
> +	return 0;
> +}
> +
> +static int fwts_oem_runtime_close(struct inode *inode, struct file *file)
> +{
> +	return 0;
> +}
> +
> +static const struct file_operations fwts_oem_runtime_fops = {
> +	.owner		= THIS_MODULE,
> +	.unlocked_ioctl = fwts_oem_runtime_ioctl,
> +	.open		= fwts_oem_runtime_open,
> +	.release	= fwts_oem_runtime_close,
> +	.llseek		= no_llseek,
> +};
> +
> +static struct miscdevice fwts_oem_runtime_dev = {
> +	MISC_DYNAMIC_MINOR,
> +	"fwts_oem",
> +	&fwts_oem_runtime_fops
> +};
> +
> +static void hp_wmi_notify(u32 value, void *context)
> +{
> +	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> +	union acpi_object *obj;
> +	u32 event_id, event_data;
> +	u32 *location;
> +	acpi_status status;
> +
> +	status = wmi_get_event_data(value, &response);
> +	if (status != AE_OK) {
> +		pr_info("bad event status 0x%x\n", status);
> +		return;
> +	}
> +
> +	obj = (union acpi_object *)response.pointer;
> +
> +	if (!obj)
> +		return;
> +	if (obj->type != ACPI_TYPE_BUFFER) {
> +		pr_info("Unknown response received %d\n", obj->type);
> +		kfree(obj);
> +		return;
> +	}
> +
> +	location = (u32 *)obj->buffer.pointer;
> +	if (obj->buffer.length == 8) {
> +		event_id = *location;
> +		event_data = *(location + 1);
> +	} else if (obj->buffer.length == 16) {
> +		event_id = *location;
> +		event_data = *(location + 2);
> +	} else {
> +		pr_info("Unknown buffer length %d\n", obj->buffer.length);
> +		kfree(obj);
> +		return;
> +	}
> +	kfree(obj);
> +
> +	switch (event_id) {
> +	case HPWMI_WIRELESS:
> +		pr_info("HPWMI_WIRELESS event is received\n");
> +		break;
> +	default:
> +		pr_info("Event_id - %d - 0x%x\n", event_id, event_data);
> +		break;
> +	}
> +}
> +
> +static int __init hp_wmi_input_setup(void)
> +{
> +	acpi_status status;
> +	int err;
> +
> +	status = wmi_install_notify_handler(HPWMI_EVENT_GUID,
> +						hp_wmi_notify, NULL);
> +	if (ACPI_FAILURE(status)) {
> +		pr_info("input_setup failed\n");
> +		err = -EIO;
> +		goto err_uninstall_notifier;
> +	}
> +
> +	return 0;
> +
> + err_uninstall_notifier:
> +	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
> +	return err;
> +}
> +
> +static void hp_wmi_input_destroy(void)
> +{
> +	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
> +}
> +
> +static void cleanup_sysfs(struct platform_device *device)
> +{
> +
> +}
> +
> +
> +static int __devinit hp_wmi_bios_setup(struct platform_device *device)
> +{
> +	return 0;
> +}
> +
> +static int __exit hp_wmi_bios_remove(struct platform_device *device)
> +{
> +	cleanup_sysfs(device);
> +
> +	return 0;
> +}
> +
> +static int hp_wmi_resume_handler(struct device *device)
> +{
> +	return 0;
> +}
> +
> +static int __init hp_wmi_init(void)
> +{
> +	int err;
> +	int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
> +	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
> +
> +	if (event_capable) {
> +		err = hp_wmi_input_setup();
> +		if (err)
> +			return err;
> +	}
> +
> +	if (bios_capable) {
> +		err = platform_driver_register(&hp_wmi_driver);
> +		if (err)
> +			goto err_driver_reg;
> +		hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
> +		if (!hp_wmi_platform_dev) {
> +			err = -ENOMEM;
> +			goto err_device_alloc;
> +		}
> +		err = platform_device_add(hp_wmi_platform_dev);
> +		if (err)
> +			goto err_device_add;
> +	}
> +
> +	if (!bios_capable && !event_capable)
> +		return -ENODEV;
> +
> +	err = misc_register(&fwts_oem_runtime_dev);
> +	if (err) {
> +		printk(KERN_ERR "fwts_oem: can't misc_register on minor=%d\n",
> +					MISC_DYNAMIC_MINOR);
> +		return err;
> +	}
> +
> +	return 0;
> +
> +err_device_add:
> +	platform_device_put(hp_wmi_platform_dev);
> +err_device_alloc:
> +	platform_driver_unregister(&hp_wmi_driver);
> +err_driver_reg:
> +	if (event_capable)
> +		hp_wmi_input_destroy();
> +
> +	return err;
> +}
> +
> +static void __exit hp_wmi_exit(void)
> +{
> +	if (wmi_has_guid(HPWMI_EVENT_GUID))
> +		hp_wmi_input_destroy();
> +
> +	if (hp_wmi_platform_dev) {
> +		platform_device_unregister(hp_wmi_platform_dev);
> +		platform_driver_unregister(&hp_wmi_driver);
> +	}
> +
> +	misc_deregister(&fwts_oem_runtime_dev);
> +}
> +
> +module_init(hp_wmi_init);
> +module_exit(hp_wmi_exit);
>

Patch

diff --git a/oem/Makefile b/oem/Makefile
new file mode 100644
index 0000000..ab73846
--- /dev/null
+++ b/oem/Makefile
@@ -0,0 +1,6 @@ 
+obj-m += hp-wmi.o
+all:
+	make -C /lib/modules/`uname -r`/build M=`pwd` modules
+
+clean:
+	make -C /lib/modules/`uname -r`/build M=`pwd` clean
diff --git a/oem/hp-wmi.c b/oem/hp-wmi.c
new file mode 100644
index 0000000..eff6faf
--- /dev/null
+++ b/oem/hp-wmi.c
@@ -0,0 +1,533 @@ 
+/*
+ * Copyright (C) 2011-2012 Canonical
+ *
+ * Portions based on hp-wmi.c:
+ * Copyright (C) 2008 Red Hat <mjg@redhat.com>
+ * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME "-fwts: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/string.h>
+
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+
+#include "fwts-oem.h"
+
+MODULE_AUTHOR("Alex Hung <alex.hung@canonical.com>");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
+MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
+
+#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
+#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
+
+#define HPWMI_DISPLAY_QUERY	0x1
+#define HPWMI_HDDTEMP_QUERY	0x2
+#define HPWMI_ALS_QUERY		0x3
+#define HPWMI_HARDWARE_QUERY	0x4
+#define HPWMI_WIRELESS_QUERY	0x5
+#define HPWMI_HOTKEY_QUERY	0xc
+#define HPWMI_WIRELESS2_QUERY	0x1b
+
+enum hp_wmi_radio {
+	HPWMI_WIFI = 0,
+	HPWMI_BLUETOOTH = 1,
+	HPWMI_WWAN = 2,
+};
+
+enum hp_wmi_event_ids {
+	HPWMI_DOCK_EVENT = 1,
+	HPWMI_PARK_HDD = 2,
+	HPWMI_SMART_ADAPTER = 3,
+	HPWMI_BEZEL_BUTTON = 4,
+	HPWMI_WIRELESS = 5,
+	HPWMI_CPU_BATTERY_THROTTLE = 6,
+	HPWMI_LOCK_SWITCH = 7,
+};
+
+static int __devinit hp_wmi_bios_setup(struct platform_device *device);
+static int __exit hp_wmi_bios_remove(struct platform_device *device);
+static int hp_wmi_resume_handler(struct device *device);
+
+struct bios_args {
+	u32 signature;
+	u32 command;
+	u32 commandtype;
+	u32 datasize;
+	u32 data;
+};
+
+struct bios_return {
+	u32 sigpass;
+	u32 return_code;
+};
+
+enum hp_return_value {
+	HPWMI_RET_WRONG_SIGNATURE	= 0x02,
+	HPWMI_RET_UNKNOWN_COMMAND	= 0x03,
+	HPWMI_RET_UNKNOWN_CMDTYPE	= 0x04,
+	HPWMI_RET_INVALID_PARAMETERS	= 0x05,
+};
+
+enum hp_wireless2_bits {
+	HPWMI_POWER_STATE	= 0x01,
+	HPWMI_POWER_SOFT	= 0x02,
+	HPWMI_POWER_BIOS	= 0x04,
+	HPWMI_POWER_HARD	= 0x08,
+};
+
+#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
+			 != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
+#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
+
+struct bios_rfkill2_device_state {
+	u8 radio_type;
+	u8 bus_type;
+	u16 vendor_id;
+	u16 product_id;
+	u16 subsys_vendor_id;
+	u16 subsys_product_id;
+	u8 rfkill_id;
+	u8 power;
+	u8 unknown[4];
+};
+
+/* 7 devices fit into the 128 byte buffer */
+#define HPWMI_MAX_RFKILL2_DEVICES	7
+
+struct bios_rfkill2_state {
+	u8 unknown[7];
+	u8 count;
+	u8 pad[8];
+	struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
+};
+
+static struct platform_device *hp_wmi_platform_dev;
+
+static const struct dev_pm_ops hp_wmi_pm_ops = {
+	.resume  = hp_wmi_resume_handler,
+	.restore  = hp_wmi_resume_handler,
+};
+
+static struct platform_driver hp_wmi_driver = {
+	.driver = {
+		.name = "hp-wmi",
+		.owner = THIS_MODULE,
+		.pm = &hp_wmi_pm_ops,
+	},
+	.probe = hp_wmi_bios_setup,
+	.remove = hp_wmi_bios_remove,
+};
+
+static int hp_wmi_perform_query(int query, int write, void *buffer,
+				int insize, int outsize)
+{
+	struct bios_return *bios_return;
+	int actual_outsize;
+	union acpi_object *obj;
+	struct bios_args args = {
+		.signature = 0x55434553,
+		.command = write ? 0x2 : 0x1,
+		.commandtype = query,
+		.datasize = insize,
+		.data = 0,
+	};
+	struct acpi_buffer input = { sizeof(struct bios_args), &args };
+	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+	u32 rc;
+
+	if (WARN_ON(insize > sizeof(args.data)))
+		return -EINVAL;
+	memcpy(&args.data, buffer, insize);
+
+	wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
+
+	obj = output.pointer;
+
+	if (!obj)
+		return -EINVAL;
+	else if (obj->type != ACPI_TYPE_BUFFER) {
+		kfree(obj);
+		return -EINVAL;
+	}
+
+	bios_return = (struct bios_return *)obj->buffer.pointer;
+	rc = bios_return->return_code;
+
+	if (rc) {
+		if (rc != HPWMI_RET_UNKNOWN_CMDTYPE)
+			pr_warn("query 0x%x returned error 0x%x\n", query, rc);
+		kfree(obj);
+		return rc;
+	}
+
+	if (!outsize) {
+		/* ignore output data */
+		kfree(obj);
+		return 0;
+	}
+
+	actual_outsize = min(outsize,
+			(int)(obj->buffer.length - sizeof(*bios_return)));
+	memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return),
+							actual_outsize);
+	memset(buffer + actual_outsize, 0, outsize - actual_outsize);
+	kfree(obj);
+	return 0;
+}
+
+static int hp_wmi_type_1b_write(u8 power_ctrl, int state)
+{
+	char buffer[4] = { 0x01, 0x00, power_ctrl, state };
+
+	if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1,
+				buffer, sizeof(buffer), 0)) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hp_wmi_type_1b_read(struct bios_rfkill2_state *rf_info)
+{
+	int err;
+
+	err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, rf_info,
+			0, sizeof(struct bios_rfkill2_state));
+	if (err)
+		return err;
+	
+	return 0;
+}
+
+static int get_wireless_dev_structure_wmi_1b(
+	struct bios_rfkill2_device_state *rfkill_dev, enum hp_wmi_radio r)
+{
+	struct bios_rfkill2_state rf_info;
+	struct bios_rfkill2_device_state *dev = NULL;
+	int err, i;
+	bool found = false;
+
+	err = hp_wmi_type_1b_read(&rf_info);
+	if (err)
+		return -EINVAL;
+	
+	for (i = 0; i < rf_info.count; i++) {
+		dev = &rf_info.device[i];
+		if (dev->radio_type == r) {
+			found = true;
+			break;
+		}
+	}
+	if (!found)
+		return FWTS_NOT_PRESENT;
+	memcpy(rfkill_dev, dev, sizeof(struct bios_rfkill2_device_state));
+
+	return 0;
+}
+
+static int convert_wireless_device_type(int radio)
+{
+	int dev_type = HPWMI_WIFI;
+
+	switch (radio) {
+	case FWTS_WIFI:
+		dev_type = HPWMI_WIFI;
+		break;
+	case FWTS_BLUETOOTH:
+		dev_type = HPWMI_BLUETOOTH;
+		break;
+	case FWTS_WWAN:
+		dev_type = HPWMI_WWAN;
+		break;
+	default:
+		break;
+	}
+
+	return dev_type;
+}
+
+static int get_wireless_device(fwts_oem_wireless __user *wd)
+{
+	struct bios_rfkill2_device_state rfdev;
+	int err;
+	int dev_type;
+
+	dev_type = convert_wireless_device_type(wd->device.device_type);
+	err = get_wireless_dev_structure_wmi_1b(&rfdev, dev_type);
+	if (err == FWTS_NOT_PRESENT) {
+		wd->oem_parameter.func_status = FWTS_NOT_PRESENT;
+		return 0;
+	} else if (err == FWTS_SUCCESS) {
+		wd->oem_parameter.func_status = FWTS_SUCCESS;
+		wd->device.device_bus = rfdev.bus_type;
+		wd->device.vendor_id = rfdev.vendor_id;
+		wd->device.device_id = rfdev.product_id;
+		wd->device.soft_kill_status = IS_SWBLOCKED(rfdev.power);
+		wd->device.hard_kill_status = IS_HWBLOCKED(rfdev.power);
+		return 0;
+	} 
+
+	return err;
+}
+
+static int set_wireless_device(fwts_oem_wireless __user *wd)
+{
+	int err;
+	struct bios_rfkill2_device_state dev;
+	int dev_type;
+	int target_power;
+
+	dev_type = convert_wireless_device_type(wd->device.device_type);
+	err = get_wireless_dev_structure_wmi_1b(&dev, dev_type);
+	if (err)
+		return err;
+	target_power = !wd->device.soft_kill_status;
+	err = hp_wmi_type_1b_write(dev.rfkill_id, target_power);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int handle_oem_wireless_cmd(fwts_oem_wireless __user *wd)
+{
+	int ret = 0;
+	u8 cmd;
+
+	cmd = wd->oem_parameter.func;
+	switch(cmd) {
+	case GET_DEVICE:
+		ret = get_wireless_device(wd);
+		break;
+	case SET_DEVICE:
+		ret = set_wireless_device(wd);
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static long fwts_oem_runtime_ioctl(struct file *file, unsigned int cmd,
+							unsigned long arg)
+{
+	switch(cmd) {
+	case OEM_WIRELESS_CMD:
+		handle_oem_wireless_cmd((fwts_oem_wireless __user *) arg);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int fwts_oem_runtime_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int fwts_oem_runtime_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations fwts_oem_runtime_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl = fwts_oem_runtime_ioctl,
+	.open		= fwts_oem_runtime_open,
+	.release	= fwts_oem_runtime_close,
+	.llseek		= no_llseek,
+};
+
+static struct miscdevice fwts_oem_runtime_dev = {
+	MISC_DYNAMIC_MINOR,
+	"fwts_oem",
+	&fwts_oem_runtime_fops
+};
+
+static void hp_wmi_notify(u32 value, void *context)
+{
+	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+	u32 event_id, event_data;
+	u32 *location;
+	acpi_status status;
+
+	status = wmi_get_event_data(value, &response);
+	if (status != AE_OK) {
+		pr_info("bad event status 0x%x\n", status);
+		return;
+	}
+
+	obj = (union acpi_object *)response.pointer;
+
+	if (!obj)
+		return;
+	if (obj->type != ACPI_TYPE_BUFFER) {
+		pr_info("Unknown response received %d\n", obj->type);
+		kfree(obj);
+		return;
+	}
+
+	location = (u32 *)obj->buffer.pointer;
+	if (obj->buffer.length == 8) {
+		event_id = *location;
+		event_data = *(location + 1);
+	} else if (obj->buffer.length == 16) {
+		event_id = *location;
+		event_data = *(location + 2);
+	} else {
+		pr_info("Unknown buffer length %d\n", obj->buffer.length);
+		kfree(obj);
+		return;
+	}
+	kfree(obj);
+
+	switch (event_id) {
+	case HPWMI_WIRELESS:
+		pr_info("HPWMI_WIRELESS event is received\n");
+		break;
+	default:
+		pr_info("Event_id - %d - 0x%x\n", event_id, event_data);
+		break;
+	}
+}
+
+static int __init hp_wmi_input_setup(void)
+{
+	acpi_status status;
+	int err;
+
+	status = wmi_install_notify_handler(HPWMI_EVENT_GUID,
+						hp_wmi_notify, NULL);
+	if (ACPI_FAILURE(status)) {
+		pr_info("input_setup failed\n");
+		err = -EIO;
+		goto err_uninstall_notifier;
+	}
+
+	return 0;
+
+ err_uninstall_notifier:
+	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+	return err;
+}
+
+static void hp_wmi_input_destroy(void)
+{
+	wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+}
+
+static void cleanup_sysfs(struct platform_device *device)
+{
+
+}
+
+
+static int __devinit hp_wmi_bios_setup(struct platform_device *device)
+{
+	return 0;
+}
+
+static int __exit hp_wmi_bios_remove(struct platform_device *device)
+{
+	cleanup_sysfs(device);
+
+	return 0;
+}
+
+static int hp_wmi_resume_handler(struct device *device)
+{
+	return 0;
+}
+
+static int __init hp_wmi_init(void)
+{
+	int err;
+	int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
+	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
+
+	if (event_capable) {
+		err = hp_wmi_input_setup();
+		if (err)
+			return err;
+	}
+
+	if (bios_capable) {
+		err = platform_driver_register(&hp_wmi_driver);
+		if (err)
+			goto err_driver_reg;
+		hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
+		if (!hp_wmi_platform_dev) {
+			err = -ENOMEM;
+			goto err_device_alloc;
+		}
+		err = platform_device_add(hp_wmi_platform_dev);
+		if (err)
+			goto err_device_add;
+	}
+
+	if (!bios_capable && !event_capable)
+		return -ENODEV;
+
+	err = misc_register(&fwts_oem_runtime_dev);
+	if (err) {
+		printk(KERN_ERR "fwts_oem: can't misc_register on minor=%d\n",
+					MISC_DYNAMIC_MINOR);
+		return err;
+	}
+
+	return 0;
+
+err_device_add:
+	platform_device_put(hp_wmi_platform_dev);
+err_device_alloc:
+	platform_driver_unregister(&hp_wmi_driver);
+err_driver_reg:
+	if (event_capable)
+		hp_wmi_input_destroy();
+
+	return err;
+}
+
+static void __exit hp_wmi_exit(void)
+{
+	if (wmi_has_guid(HPWMI_EVENT_GUID))
+		hp_wmi_input_destroy();
+
+	if (hp_wmi_platform_dev) {
+		platform_device_unregister(hp_wmi_platform_dev);
+		platform_driver_unregister(&hp_wmi_driver);
+	}
+
+	misc_deregister(&fwts_oem_runtime_dev);
+}
+
+module_init(hp_wmi_init);
+module_exit(hp_wmi_exit);