Skip to content

Commit

Permalink
hp-wmi: Add support for reporting tablet state
Browse files Browse the repository at this point in the history
HP tablets send a WMI event when a tablet state change occurs, but use the
same method as is used for reporting docking and undocking. The same query
is used to obtain the state of the hardware. Bit 0 indicates the docking
state, while bit 2 indicates the tablet state. This patch breaks these out
and sends separate input events for tablet and dock state changes. An
additional sysfs file is added to report the tablet state.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Matthew Garrett authored and Len Brown committed Jun 18, 2009
1 parent db18b04 commit 871043b
Showing 1 changed file with 55 additions and 32 deletions.
87 changes: 55 additions & 32 deletions drivers/platform/x86/hp-wmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
#define HPWMI_DISPLAY_QUERY 0x1
#define HPWMI_HDDTEMP_QUERY 0x2
#define HPWMI_ALS_QUERY 0x3
#define HPWMI_DOCK_QUERY 0x4
#define HPWMI_HARDWARE_QUERY 0x4
#define HPWMI_WIRELESS_QUERY 0x5
#define HPWMI_HOTKEY_QUERY 0xc

Expand Down Expand Up @@ -75,10 +75,9 @@ struct key_entry {
u16 keycode;
};

enum { KE_KEY, KE_SW, KE_END };
enum { KE_KEY, KE_END };

static struct key_entry hp_wmi_keymap[] = {
{KE_SW, 0x01, SW_DOCK},
{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
{KE_KEY, 0x20e6, KEY_PROG1},
Expand Down Expand Up @@ -151,7 +150,22 @@ static int hp_wmi_als_state(void)

static int hp_wmi_dock_state(void)
{
return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);

if (ret < 0)
return ret;

return ret & 0x1;
}

static int hp_wmi_tablet_state(void)
{
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);

if (ret < 0)
return ret;

return (ret & 0x4) ? 1 : 0;
}

static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
Expand Down Expand Up @@ -244,6 +258,15 @@ static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%d\n", value);
}

static ssize_t show_tablet(struct device *dev, struct device_attribute *attr,
char *buf)
{
int value = hp_wmi_tablet_state();
if (value < 0)
return -EINVAL;
return sprintf(buf, "%d\n", value);
}

static ssize_t set_als(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
Expand All @@ -256,6 +279,7 @@ 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 struct key_entry *hp_wmi_get_entry_by_scancode(int code)
{
Expand Down Expand Up @@ -338,13 +362,13 @@ static void hp_wmi_notify(u32 value, void *context)
key->keycode, 0);
input_sync(hp_wmi_input_dev);
break;
case KE_SW:
input_report_switch(hp_wmi_input_dev,
key->keycode,
hp_wmi_dock_state());
input_sync(hp_wmi_input_dev);
break;
}
} else if (eventcode == 0x1) {
input_report_switch(hp_wmi_input_dev, SW_DOCK,
hp_wmi_dock_state());
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
} else if (eventcode == 0x5) {
if (wifi_rfkill)
rfkill_force_state(wifi_rfkill,
Expand Down Expand Up @@ -381,18 +405,19 @@ static int __init hp_wmi_input_setup(void)
set_bit(EV_KEY, hp_wmi_input_dev->evbit);
set_bit(key->keycode, hp_wmi_input_dev->keybit);
break;
case KE_SW:
set_bit(EV_SW, hp_wmi_input_dev->evbit);
set_bit(key->keycode, hp_wmi_input_dev->swbit);

/* Set initial dock state */
input_report_switch(hp_wmi_input_dev, key->keycode,
hp_wmi_dock_state());
input_sync(hp_wmi_input_dev);
break;
}
}

set_bit(EV_SW, hp_wmi_input_dev->evbit);
set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);

/* Set initial hardware state */
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);

err = input_register_device(hp_wmi_input_dev);

if (err) {
Expand All @@ -409,6 +434,7 @@ static void cleanup_sysfs(struct platform_device *device)
device_remove_file(&device->dev, &dev_attr_hddtemp);
device_remove_file(&device->dev, &dev_attr_als);
device_remove_file(&device->dev, &dev_attr_dock);
device_remove_file(&device->dev, &dev_attr_tablet);
}

static int __init hp_wmi_bios_setup(struct platform_device *device)
Expand All @@ -426,6 +452,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
if (err)
goto add_sysfs_error;
err = device_create_file(&device->dev, &dev_attr_dock);
if (err)
goto add_sysfs_error;
err = device_create_file(&device->dev, &dev_attr_tablet);
if (err)
goto add_sysfs_error;

Expand Down Expand Up @@ -491,23 +520,17 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)

static int hp_wmi_resume_handler(struct platform_device *device)
{
struct key_entry *key;

/*
* Docking state may have changed while suspended, so trigger
* an input event for the current state. As this is a switch,
* Hardware state may have changed while suspended, so trigger
* input events for the current state. As this is a switch,
* the input layer will only actually pass it on if the state
* changed.
*/
for (key = hp_wmi_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_SW:
input_report_switch(hp_wmi_input_dev, key->keycode,
hp_wmi_dock_state());
input_sync(hp_wmi_input_dev);
break;
}
}

input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);

return 0;
}
Expand Down

0 comments on commit 871043b

Please sign in to comment.