aboutsummaryrefslogtreecommitdiff
path: root/drivers/platform/x86/hp-wmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/hp-wmi.c')
-rw-r--r--drivers/platform/x86/hp-wmi.c286
1 files changed, 211 insertions, 75 deletions
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 1bc4a7539ba..484a8673b83 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -24,6 +24,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -51,16 +53,17 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
#define HPWMI_ALS_QUERY 0x3
#define HPWMI_HARDWARE_QUERY 0x4
#define HPWMI_WIRELESS_QUERY 0x5
+#define HPWMI_BIOS_QUERY 0x9
#define HPWMI_HOTKEY_QUERY 0xc
+#define HPWMI_FEATURE_QUERY 0xd
#define HPWMI_WIRELESS2_QUERY 0x1b
-
-#define PREFIX "HP WMI: "
-#define UNIMP "Unimplemented "
+#define HPWMI_POSTCODEERROR_QUERY 0x2a
enum hp_wmi_radio {
HPWMI_WIFI = 0,
HPWMI_BLUETOOTH = 1,
HPWMI_WWAN = 2,
+ HPWMI_GPS = 3,
};
enum hp_wmi_event_ids {
@@ -71,12 +74,16 @@ enum hp_wmi_event_ids {
HPWMI_WIRELESS = 5,
HPWMI_CPU_BATTERY_THROTTLE = 6,
HPWMI_LOCK_SWITCH = 7,
+ HPWMI_LID_SWITCH = 8,
+ HPWMI_SCREEN_ROTATION = 9,
+ HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A,
+ HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B,
+ HPWMI_PROXIMITY_SENSOR = 0x0C,
+ HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D,
+ HPWMI_PEAKSHIFT_PERIOD = 0x0F,
+ HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
};
-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;
@@ -138,6 +145,7 @@ static const struct key_entry hp_wmi_keymap[] = {
{ KE_KEY, 0x2142, { KEY_MEDIA } },
{ KE_KEY, 0x213b, { KEY_INFO } },
{ KE_KEY, 0x2169, { KEY_DIRECTION } },
+ { KE_KEY, 0x216a, { KEY_SETUP } },
{ KE_KEY, 0x231b, { KEY_HELP } },
{ KE_END, 0 }
};
@@ -148,6 +156,7 @@ static struct platform_device *hp_wmi_platform_dev;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
+static struct rfkill *gps_rfkill;
struct rfkill2_device {
u8 id;
@@ -158,21 +167,6 @@ struct rfkill2_device {
static int rfkill2_count;
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
-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,
-};
-
/*
* hp_wmi_perform_query
*
@@ -208,6 +202,7 @@ static int hp_wmi_perform_query(int query, int write, void *buffer,
};
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;
@@ -225,14 +220,13 @@ static int hp_wmi_perform_query(int query, int write, void *buffer,
}
bios_return = (struct bios_return *)obj->buffer.pointer;
+ rc = bios_return->return_code;
- if (bios_return->return_code) {
- if (bios_return->return_code != HPWMI_RET_UNKNOWN_CMDTYPE)
- printk(KERN_WARNING PREFIX "query 0x%x returned "
- "error 0x%x\n",
- query, 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 bios_return->return_code;
+ return rc;
}
if (!outsize) {
@@ -301,6 +295,30 @@ static int hp_wmi_tablet_state(void)
return (state & 0x4) ? 1 : 0;
}
+static int hp_wmi_bios_2009_later(void)
+{
+ int state = 0;
+ int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state,
+ sizeof(state), sizeof(state));
+ if (ret)
+ return ret;
+
+ return (state & 0x10) ? 1 : 0;
+}
+
+static int hp_wmi_enable_hotkeys(void)
+{
+ int ret;
+ int query = 0x6e;
+
+ ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query),
+ 0);
+
+ if (ret)
+ return -EINVAL;
+ return 0;
+}
+
static int hp_wmi_set_block(void *data, bool blocked)
{
enum hp_wmi_radio r = (enum hp_wmi_radio) data;
@@ -384,8 +402,7 @@ static int hp_wmi_rfkill2_refresh(void)
if (num >= state.count ||
devstate->rfkill_id != rfkill2[i].id) {
- printk(KERN_WARNING PREFIX "power configuration of "
- "the wireless devices unexpectedly changed\n");
+ pr_warn("power configuration of the wireless devices unexpectedly changed\n");
continue;
}
@@ -397,6 +414,16 @@ static int hp_wmi_rfkill2_refresh(void)
return 0;
}
+static int hp_wmi_post_code_state(void)
+{
+ int state = 0;
+ int ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 0, &state,
+ sizeof(state), sizeof(state));
+ if (ret)
+ return -EINVAL;
+ return state;
+}
+
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -442,6 +469,16 @@ static ssize_t show_tablet(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%d\n", value);
}
+static ssize_t show_postcode(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ /* Get the POST error code of previous boot failure. */
+ int value = hp_wmi_post_code_state();
+ if (value < 0)
+ return -EINVAL;
+ return sprintf(buf, "0x%x\n", value);
+}
+
static ssize_t set_als(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -454,11 +491,33 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr,
return count;
}
+static ssize_t set_postcode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ u32 tmp;
+ long unsigned int tmp2;
+
+ ret = kstrtoul(buf, 10, &tmp2);
+ if (ret || tmp2 != 1)
+ return -EINVAL;
+
+ /* Clear the POST error code. It is kept until until cleared. */
+ tmp = (u32) tmp2;
+ ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 1, &tmp,
+ sizeof(tmp), sizeof(tmp));
+ if (ret)
+ return -EINVAL;
+
+ return count;
+}
+
static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
+static DEVICE_ATTR(postcode, S_IRUGO | S_IWUSR, show_postcode, set_postcode);
static void hp_wmi_notify(u32 value, void *context)
{
@@ -471,7 +530,7 @@ static void hp_wmi_notify(u32 value, void *context)
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
- printk(KERN_INFO PREFIX "bad event status 0x%x\n", status);
+ pr_info("bad event status 0x%x\n", status);
return;
}
@@ -480,8 +539,7 @@ static void hp_wmi_notify(u32 value, void *context)
if (!obj)
return;
if (obj->type != ACPI_TYPE_BUFFER) {
- printk(KERN_INFO "hp-wmi: Unknown response received %d\n",
- obj->type);
+ pr_info("Unknown response received %d\n", obj->type);
kfree(obj);
return;
}
@@ -498,8 +556,7 @@ static void hp_wmi_notify(u32 value, void *context)
event_id = *location;
event_data = *(location + 2);
} else {
- printk(KERN_INFO "hp-wmi: Unknown buffer length %d\n",
- obj->buffer.length);
+ pr_info("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return;
}
@@ -527,8 +584,7 @@ static void hp_wmi_notify(u32 value, void *context)
if (!sparse_keymap_report_event(hp_wmi_input_dev,
key_code, 1, true))
- printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
- key_code);
+ pr_info("Unknown key code - 0x%x\n", key_code);
break;
case HPWMI_WIRELESS:
if (rfkill2_count) {
@@ -548,16 +604,34 @@ static void hp_wmi_notify(u32 value, void *context)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN));
+ if (gps_rfkill)
+ rfkill_set_states(gps_rfkill,
+ hp_wmi_get_sw_state(HPWMI_GPS),
+ hp_wmi_get_hw_state(HPWMI_GPS));
break;
case HPWMI_CPU_BATTERY_THROTTLE:
- printk(KERN_INFO PREFIX UNIMP "CPU throttle because of 3 Cell"
- " battery event detected\n");
+ pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
break;
case HPWMI_LOCK_SWITCH:
break;
+ case HPWMI_LID_SWITCH:
+ break;
+ case HPWMI_SCREEN_ROTATION:
+ break;
+ case HPWMI_COOLSENSE_SYSTEM_MOBILE:
+ break;
+ case HPWMI_COOLSENSE_SYSTEM_HOT:
+ break;
+ case HPWMI_PROXIMITY_SENSOR:
+ break;
+ case HPWMI_BACKLIT_KB_BRIGHTNESS:
+ break;
+ case HPWMI_PEAKSHIFT_PERIOD:
+ break;
+ case HPWMI_BATTERY_CHARGE_PERIOD:
+ break;
default:
- printk(KERN_INFO PREFIX "Unknown event_id - %d - 0x%x\n",
- event_id, event_data);
+ pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data);
break;
}
}
@@ -589,6 +663,9 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
+ if (hp_wmi_bios_2009_later() == 4)
+ hp_wmi_enable_hotkeys();
+
status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
err = -EIO;
@@ -624,9 +701,10 @@ static void cleanup_sysfs(struct platform_device *device)
device_remove_file(&device->dev, &dev_attr_als);
device_remove_file(&device->dev, &dev_attr_dock);
device_remove_file(&device->dev, &dev_attr_tablet);
+ device_remove_file(&device->dev, &dev_attr_postcode);
}
-static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
+static int hp_wmi_rfkill_setup(struct platform_device *device)
{
int err;
int wireless = 0;
@@ -641,6 +719,8 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
RFKILL_TYPE_WLAN,
&hp_wmi_rfkill_ops,
(void *) HPWMI_WIFI);
+ if (!wifi_rfkill)
+ return -ENOMEM;
rfkill_init_sw_state(wifi_rfkill,
hp_wmi_get_sw_state(HPWMI_WIFI));
rfkill_set_hw_state(wifi_rfkill,
@@ -655,6 +735,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
RFKILL_TYPE_BLUETOOTH,
&hp_wmi_rfkill_ops,
(void *) HPWMI_BLUETOOTH);
+ if (!bluetooth_rfkill) {
+ err = -ENOMEM;
+ goto register_wifi_error;
+ }
rfkill_init_sw_state(bluetooth_rfkill,
hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
rfkill_set_hw_state(bluetooth_rfkill,
@@ -669,21 +753,48 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
RFKILL_TYPE_WWAN,
&hp_wmi_rfkill_ops,
(void *) HPWMI_WWAN);
+ if (!wwan_rfkill) {
+ err = -ENOMEM;
+ goto register_bluetooth_error;
+ }
rfkill_init_sw_state(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN));
rfkill_set_hw_state(wwan_rfkill,
hp_wmi_get_hw_state(HPWMI_WWAN));
err = rfkill_register(wwan_rfkill);
if (err)
- goto register_wwan_err;
+ goto register_wwan_error;
+ }
+
+ if (wireless & 0x8) {
+ gps_rfkill = rfkill_alloc("hp-gps", &device->dev,
+ RFKILL_TYPE_GPS,
+ &hp_wmi_rfkill_ops,
+ (void *) HPWMI_GPS);
+ if (!gps_rfkill) {
+ err = -ENOMEM;
+ goto register_wwan_error;
+ }
+ rfkill_init_sw_state(gps_rfkill,
+ hp_wmi_get_sw_state(HPWMI_GPS));
+ rfkill_set_hw_state(gps_rfkill,
+ hp_wmi_get_hw_state(HPWMI_GPS));
+ err = rfkill_register(gps_rfkill);
+ if (err)
+ goto register_gps_error;
}
return 0;
-register_wwan_err:
- rfkill_destroy(wwan_rfkill);
- wwan_rfkill = NULL;
+register_gps_error:
+ rfkill_destroy(gps_rfkill);
+ gps_rfkill = NULL;
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
+register_wwan_error:
+ rfkill_destroy(wwan_rfkill);
+ wwan_rfkill = NULL;
+ if (gps_rfkill)
+ rfkill_unregister(gps_rfkill);
register_bluetooth_error:
rfkill_destroy(bluetooth_rfkill);
bluetooth_rfkill = NULL;
@@ -695,7 +806,7 @@ register_wifi_error:
return err;
}
-static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
+static int hp_wmi_rfkill2_setup(struct platform_device *device)
{
int err, i;
struct bios_rfkill2_state state;
@@ -705,7 +816,7 @@ static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
return err;
if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
- printk(KERN_WARNING PREFIX "unable to parse 0x1b query output\n");
+ pr_warn("unable to parse 0x1b query output\n");
return -EINVAL;
}
@@ -726,15 +837,19 @@ static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
type = RFKILL_TYPE_WWAN;
name = "hp-wwan";
break;
+ case HPWMI_GPS:
+ type = RFKILL_TYPE_GPS;
+ name = "hp-gps";
+ break;
default:
- printk(KERN_WARNING PREFIX "unknown device type 0x%x\n",
- state.device[i].radio_type);
+ pr_warn("unknown device type 0x%x\n",
+ state.device[i].radio_type);
continue;
}
if (!state.device[i].vendor_id) {
- printk(KERN_WARNING PREFIX "zero device %d while %d "
- "reported\n", i, state.count);
+ pr_warn("zero device %d while %d reported\n",
+ i, state.count);
continue;
}
@@ -755,8 +870,7 @@ static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
IS_HWBLOCKED(state.device[i].power));
if (!(state.device[i].power & HPWMI_POWER_BIOS))
- printk(KERN_INFO PREFIX "device %s blocked by BIOS\n",
- name);
+ pr_info("device %s blocked by BIOS\n", name);
err = rfkill_register(rfkill);
if (err) {
@@ -776,7 +890,7 @@ fail:
return err;
}
-static int __devinit hp_wmi_bios_setup(struct platform_device *device)
+static int __init hp_wmi_bios_setup(struct platform_device *device)
{
int err;
@@ -784,9 +898,10 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)
wifi_rfkill = NULL;
bluetooth_rfkill = NULL;
wwan_rfkill = NULL;
+ gps_rfkill = NULL;
rfkill2_count = 0;
- if (hp_wmi_rfkill_setup(device))
+ if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
hp_wmi_rfkill2_setup(device);
err = device_create_file(&device->dev, &dev_attr_display);
@@ -804,6 +919,9 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)
err = device_create_file(&device->dev, &dev_attr_tablet);
if (err)
goto add_sysfs_error;
+ err = device_create_file(&device->dev, &dev_attr_postcode);
+ if (err)
+ goto add_sysfs_error;
return 0;
add_sysfs_error:
@@ -833,6 +951,10 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
+ if (gps_rfkill) {
+ rfkill_unregister(gps_rfkill);
+ rfkill_destroy(gps_rfkill);
+ }
return 0;
}
@@ -868,16 +990,37 @@ static int hp_wmi_resume_handler(struct device *device)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN));
+ if (gps_rfkill)
+ rfkill_set_states(gps_rfkill,
+ hp_wmi_get_sw_state(HPWMI_GPS),
+ hp_wmi_get_hw_state(HPWMI_GPS));
return 0;
}
+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,
+ },
+ .remove = __exit_p(hp_wmi_bios_remove),
+};
+
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 (!bios_capable && !event_capable)
+ return -ENODEV;
+
if (event_capable) {
err = hp_wmi_input_setup();
if (err)
@@ -885,34 +1028,29 @@ static int __init hp_wmi_init(void)
}
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;
+ hp_wmi_platform_dev =
+ platform_device_register_simple("hp-wmi", -1, NULL, 0);
+ if (IS_ERR(hp_wmi_platform_dev)) {
+ err = PTR_ERR(hp_wmi_platform_dev);
+ goto err_destroy_input;
}
- err = platform_device_add(hp_wmi_platform_dev);
+
+ err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
if (err)
- goto err_device_add;
+ goto err_unregister_device;
}
- if (!bios_capable && !event_capable)
- return -ENODEV;
-
return 0;
-err_device_add:
- platform_device_put(hp_wmi_platform_dev);
-err_device_alloc:
- platform_driver_unregister(&hp_wmi_driver);
-err_driver_reg:
+err_unregister_device:
+ platform_device_unregister(hp_wmi_platform_dev);
+err_destroy_input:
if (event_capable)
hp_wmi_input_destroy();
return err;
}
+module_init(hp_wmi_init);
static void __exit hp_wmi_exit(void)
{
@@ -924,6 +1062,4 @@ static void __exit hp_wmi_exit(void)
platform_driver_unregister(&hp_wmi_driver);
}
}
-
-module_init(hp_wmi_init);
module_exit(hp_wmi_exit);