From e395bba882cb014f8d86352a1b8e64d7c117c2f9 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 Sep 2007 07:55:40 -0300 Subject: [PATCH] ACPI: thinkpad-acpi: add CMOS NVRAM polling for hot keys Older ThinkPad models do not export some of the hot keys over the event-based ACPI hot key interface. For these models, one has to poll the CMOS NVRAM to check the key state at a rate faster than the expected rate at which the user might repeatedly press the same hot key. This patch implements this functionality for many of the hotkeys in a transparent way: hot keys will now Just Work, and the driver knows the best approach (events or NVRAM polling) to employ, based on the HKEY.MHKA ACPI method. Also, the driver can turn off the polling when there are no users for the hot keys that need such polling. The NVRAM-based hot keys of the A3x series that have never been implemented by later models are not supported, to avoid changes in the keymap of the input devices that could cause headaches in the future. This patch is based on a previous effort by Richard Hughes. Signed-off-by: Henrique de Moraes Holschuh Cc: Richard Hughes --- Documentation/thinkpad-acpi.txt | 24 +++ drivers/misc/thinkpad_acpi.c | 361 +++++++++++++++++++++++++++++++++++++++- drivers/misc/thinkpad_acpi.h | 60 ++++++- 3 files changed, 435 insertions(+), 10 deletions(-) diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 60953d6c919..6f18d19928d 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -252,6 +252,30 @@ sysfs notes: handled by the firmware anyway. Echo it to hotkey_mask above, to use. + hotkey_source_mask: + bit mask that selects which hot keys will the driver + poll the CMOS NVRAM for. This is auto-detected by the + driver based on the capabilities reported by the ACPI + firmware, but it can be overridden at runtime for + testing purposes. + + Hot keys whose bits are set in hotkey_source_mask are + polled for in NVRAM. Only a few hot keys are available + through CMOS NVRAM polling. + + hotkey_poll_freq: + frequency in Hz for hot key polling. It must be between + 0 and 25 Hz. Polling is only carried out when strictly + needed. + + Setting hotkey_poll_freq to zero disables polling, and + will cause hot key presses that require NVRAM polling + to never be reported. + + Setting hotkey_poll_freq too low will cause repeated + pressings of the same hot key to be misreported as a + single key press, or to not even be detected at all. + hotkey_radio_sw: if the ThinkPad has a hardware radio switch, this attribute will read 0 if the switch is in the "radios diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 514dda73abf..3cd30bb6eb0 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -764,11 +764,64 @@ static struct ibm_struct thinkpad_acpi_driver_data = { * Hotkey subdriver */ +enum { /* Keys available through NVRAM polling */ + TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb8000U, +}; + +enum { /* Positions of some of the keys in hotkey masks */ + TP_ACPI_HKEY_DISPSWTCH_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF7, + TP_ACPI_HKEY_DISPXPAND_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF8, + TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12, + TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME, + TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND, + TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, + TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE, + TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP, + TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, + TP_ACPI_HKEY_MUTE_MASK = 1 << TP_ACPI_HOTKEYSCAN_MUTE, + TP_ACPI_HKEY_THINKPAD_MASK = 1 << TP_ACPI_HOTKEYSCAN_THINKPAD, +}; + +enum { /* NVRAM to ACPI HKEY group map */ + TP_NVRAM_HKEY_GROUP_HK2 = TP_ACPI_HKEY_THINKPAD_MASK | + TP_ACPI_HKEY_ZOOM_MASK | + TP_ACPI_HKEY_DISPSWTCH_MASK | + TP_ACPI_HKEY_HIBERNATE_MASK, + TP_NVRAM_HKEY_GROUP_BRIGHTNESS = TP_ACPI_HKEY_BRGHTUP_MASK | + TP_ACPI_HKEY_BRGHTDWN_MASK, + TP_NVRAM_HKEY_GROUP_VOLUME = TP_ACPI_HKEY_VOLUP_MASK | + TP_ACPI_HKEY_VOLDWN_MASK | + TP_ACPI_HKEY_MUTE_MASK, +}; + +struct tp_nvram_state { + u16 thinkpad_toggle:1; + u16 zoom_toggle:1; + u16 display_toggle:1; + u16 thinklight_toggle:1; + u16 hibernate_toggle:1; + u16 displayexp_toggle:1; + u16 display_state:1; + u16 brightness_toggle:1; + u16 volume_toggle:1; + u16 mute:1; + + u8 brightness_level; + u8 volume_level; +}; + static int hotkey_orig_status; static u32 hotkey_orig_mask; static u32 hotkey_all_mask; static u32 hotkey_reserved_mask; +static struct task_struct *tpacpi_hotkey_task; +static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ +static int hotkey_poll_freq = 4; /* Hz */ +static struct mutex hotkey_poll_mutex; +static struct mutex thinkpad_nvram_mutex; +static struct mutex hotkey_thread_mutex; + static u16 *hotkey_keycode_map; static struct attribute_set *hotkey_dev_attributes; @@ -820,6 +873,201 @@ static void tpacpi_input_send_radiosw(void) mutex_unlock(&tpacpi_inputdev_send_mutex); } +static void hotkey_read_nvram(struct tp_nvram_state *n) +{ + u8 d; + + if (hotkey_source_mask & TP_NVRAM_HKEY_GROUP_HK2) { + d = nvram_read_byte(TP_NVRAM_ADDR_HK2); + n->thinkpad_toggle = !!(d & TP_NVRAM_MASK_HKT_THINKPAD); + n->zoom_toggle = !!(d & TP_NVRAM_MASK_HKT_ZOOM); + n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY); + n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE); + } + if (hotkey_source_mask & TP_ACPI_HKEY_THNKLGHT_MASK) { + d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT); + n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT); + } + if (hotkey_source_mask & TP_ACPI_HKEY_DISPXPAND_MASK) { + d = nvram_read_byte(TP_NVRAM_ADDR_VIDEO); + n->displayexp_toggle = + !!(d & TP_NVRAM_MASK_HKT_DISPEXPND); + } + if (hotkey_source_mask & TP_NVRAM_HKEY_GROUP_BRIGHTNESS) { + d = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS); + n->brightness_level = (d & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) + >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; + n->brightness_toggle = + !!(d & TP_NVRAM_MASK_HKT_BRIGHTNESS); + } + if (hotkey_source_mask & TP_NVRAM_HKEY_GROUP_VOLUME) { + d = nvram_read_byte(TP_NVRAM_ADDR_MIXER); + n->volume_level = (d & TP_NVRAM_MASK_LEVEL_VOLUME) + >> TP_NVRAM_POS_LEVEL_VOLUME; + n->mute = !!(d & TP_NVRAM_MASK_MUTE); + n->volume_toggle = !!(d & TP_NVRAM_MASK_HKT_VOLUME); + } +} + +#define TPACPI_COMPARE_KEY(__scancode, __member) \ + if (hotkey_source_mask & (1 << __scancode) && \ + oldn->__member != newn->__member) \ + tpacpi_input_send_key(__scancode); + +static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, + struct tp_nvram_state *newn) +{ + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle) + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle) + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle) + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF12, hibernate_toggle) + + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNPAGEUP, thinklight_toggle) + + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle) + + /* handle volume */ + if (oldn->volume_toggle != newn->volume_toggle) { + if (oldn->mute != newn->mute) { + tpacpi_input_send_key(TP_ACPI_HOTKEYSCAN_MUTE); + } + if (oldn->volume_level > newn->volume_level) { + tpacpi_input_send_key(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); + } else if (oldn->volume_level < newn->volume_level) { + tpacpi_input_send_key(TP_ACPI_HOTKEYSCAN_VOLUMEUP); + } + + /* we could "else" here and issue mute regardless of a change + * in the mute bit (and therefore notify of multiple presses of + * the mute button), but that is *incorrect* semanthics for + * a thinkpad mixer, where "mute" always mutes, and a volume + * key unmutes. + */ + } + + /* handle brightness */ + if (oldn->brightness_toggle != newn->brightness_toggle) { + if (oldn->brightness_level < newn->brightness_level) { + tpacpi_input_send_key(TP_ACPI_HOTKEYSCAN_FNHOME); + } else if (oldn->brightness_level > newn->brightness_level) { + tpacpi_input_send_key(TP_ACPI_HOTKEYSCAN_FNEND); + } + } +} + +#undef TPACPI_COMPARE_KEY + +static int hotkey_kthread(void *data) +{ + struct tp_nvram_state s[2]; + unsigned int si, so; + unsigned long t; + + if (tpacpi_lifecycle == TPACPI_LIFE_EXITING) + return 0; + + mutex_lock(&hotkey_thread_mutex); + + si = 1; + so = 0; + t = 0; + + /* Initial state for compares */ + hotkey_read_nvram(&s[so]); + + while (!kthread_should_stop() && hotkey_poll_freq) { + t = msleep_interruptible(1000/hotkey_poll_freq + t); + if (unlikely(kthread_should_stop())) + break; + if (unlikely(try_to_freeze())) { + si = so; /* forget old state on thaw */ + t = 0; + } + if (t > 0) + continue; + + hotkey_read_nvram(&s[si]); + hotkey_compare_and_issue_event(&s[so], &s[si]); + so = si; + si ^= 1; + } + + mutex_unlock(&hotkey_thread_mutex); + return 0; +} + +static void hotkey_poll_stop_sync(void) +{ + if (tpacpi_hotkey_task) { + kthread_stop(tpacpi_hotkey_task); + tpacpi_hotkey_task = NULL; + mutex_lock(&hotkey_thread_mutex); + /* at this point, the thread did exit */ + mutex_unlock(&hotkey_thread_mutex); + } +} + +/* call with hotkey_poll_mutex held */ +static void hotkey_poll_setup(int force) +{ + if (force) + hotkey_poll_stop_sync(); + + if (hotkey_source_mask != 0 && hotkey_poll_freq > 0 && + (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { + if (!tpacpi_hotkey_task) { + tpacpi_hotkey_task = kthread_run(hotkey_kthread, + NULL, IBM_FILE "d"); + if (IS_ERR(tpacpi_hotkey_task)) { + tpacpi_hotkey_task = NULL; + printk(IBM_ERR "could not create kernel thread " + "for hotkey polling\n"); + } + } + } else { + hotkey_poll_stop_sync(); + } +} + +static int hotkey_poll_setup_safe(int force) +{ + int rc; + + rc = mutex_lock_interruptible(&hotkey_poll_mutex); + if (rc < 0) + return rc; + + hotkey_poll_setup(force); + mutex_unlock(&hotkey_poll_mutex); + + return 0; +} + +static void hotkey_warn_no_poll(void) +{ + if (hotkey_source_mask != 0 && hotkey_poll_freq == 0) { + printk(IBM_NOTICE "hot keys 0x%08x require polling, " + "which is currently disabled\n", + hotkey_source_mask); + } + +} + +static int hotkey_inputdev_open(struct input_dev *dev) +{ + if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) + return -EBUSY; + /* enable hotkey polling when required */ + return hotkey_poll_setup_safe(0); +} + +static void hotkey_inputdev_close(struct input_dev *dev) +{ + /* disable hotkey polling when possible */ + if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) + hotkey_poll_setup_safe(0); +} + /* sysfs hotkey enable ------------------------------------------------- */ static ssize_t hotkey_enable_show(struct device *dev, struct device_attribute *attr, @@ -921,7 +1169,8 @@ static ssize_t hotkey_all_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_all_mask); + return snprintf(buf, PAGE_SIZE, "0x%08x\n", + hotkey_all_mask | hotkey_source_mask); } static struct device_attribute dev_attr_hotkey_all_mask = @@ -933,13 +1182,85 @@ static ssize_t hotkey_recommended_mask_show(struct device *dev, char *buf) { return snprintf(buf, PAGE_SIZE, "0x%08x\n", - hotkey_all_mask & ~hotkey_reserved_mask); + (hotkey_all_mask | hotkey_source_mask) + & ~hotkey_reserved_mask); } static struct device_attribute dev_attr_hotkey_recommended_mask = __ATTR(hotkey_recommended_mask, S_IRUGO, hotkey_recommended_mask_show, NULL); +/* sysfs hotkey hotkey_source_mask ------------------------------------- */ +static ssize_t hotkey_source_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask); +} + +static ssize_t hotkey_source_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int rc; + + if (parse_strtoul(buf, 0xffffffffUL, &t) || + ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) + return -EINVAL; + + rc = mutex_lock_interruptible(&hotkey_poll_mutex); + if (rc < 0) + return rc; + + hotkey_source_mask = t; + + hotkey_poll_setup(1); + hotkey_warn_no_poll(); + mutex_unlock(&hotkey_poll_mutex); + + return count; +} + +static struct device_attribute dev_attr_hotkey_source_mask = + __ATTR(hotkey_source_mask, S_IWUSR | S_IRUGO, + hotkey_source_mask_show, hotkey_source_mask_store); + +/* sysfs hotkey hotkey_poll_freq --------------------------------------- */ +static ssize_t hotkey_poll_freq_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq); +} + +static ssize_t hotkey_poll_freq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int rc; + + if (parse_strtoul(buf, 25, &t)) + return -EINVAL; + + rc = mutex_lock_interruptible(&hotkey_poll_mutex); + if (rc < 0) + return rc; + + hotkey_poll_freq = t; + + hotkey_poll_setup(0); + hotkey_warn_no_poll(); + mutex_unlock(&hotkey_poll_mutex); + + return count; +} + +static struct device_attribute dev_attr_hotkey_poll_freq = + __ATTR(hotkey_poll_freq, S_IWUSR | S_IRUGO, + hotkey_poll_freq_show, hotkey_poll_freq_store); + /* sysfs hotkey radio_sw ----------------------------------------------- */ static ssize_t hotkey_radio_sw_show(struct device *dev, struct device_attribute *attr, @@ -981,6 +1302,8 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { &dev_attr_hotkey_bios_mask.attr, &dev_attr_hotkey_all_mask.attr, &dev_attr_hotkey_recommended_mask.attr, + &dev_attr_hotkey_source_mask.attr, + &dev_attr_hotkey_poll_freq.attr, }; static int __init hotkey_init(struct ibm_init_struct *iibm) @@ -1044,9 +1367,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); BUG_ON(!tpacpi_inputdev); + BUG_ON(tpacpi_inputdev->open != NULL || + tpacpi_inputdev->close != NULL); IBM_ACPIHANDLE_INIT(hkey); mutex_init(&hotkey_mutex); + mutex_init(&hotkey_poll_mutex); + mutex_init(&hotkey_thread_mutex); /* hotkey not supported on 570 */ tp_features.hotkey = hkey_handle != NULL; @@ -1055,7 +1382,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) str_supported(tp_features.hotkey)); if (tp_features.hotkey) { - hotkey_dev_attributes = create_attr_set(8, NULL); + hotkey_dev_attributes = create_attr_set(10, NULL); if (!hotkey_dev_attributes) return -ENOMEM; res = add_many_to_attr_set(hotkey_dev_attributes, @@ -1096,6 +1423,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) } } + hotkey_source_mask = TPACPI_HKEY_NVRAM_KNOWN_MASK + & ~hotkey_all_mask; + + vdbg_printk(TPACPI_DBG_INIT, + "hotkey source mask 0x%08x, polling freq %d\n", + hotkey_source_mask, hotkey_poll_freq); + res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); if (!res && tp_features.hotkey_mask) { res = add_many_to_attr_set(hotkey_dev_attributes, @@ -1156,6 +1490,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_reserved_mask |= 1 << i; } } + tpacpi_inputdev->open = &hotkey_inputdev_open; + tpacpi_inputdev->close = &hotkey_inputdev_close; if (tp_features.hotkey_wlsw) { set_bit(EV_SW, tpacpi_inputdev->evbit); @@ -1164,8 +1500,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n"); - res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask) - | hotkey_orig_mask); + res = hotkey_set(1, ((hotkey_all_mask | hotkey_source_mask) + & ~hotkey_reserved_mask) + | hotkey_orig_mask); if (res) return res; @@ -1173,6 +1510,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) "legacy hot key reporting over procfs %s\n", (hotkey_report_mode < 2) ? "enabled" : "disabled"); + + mutex_lock(&hotkey_poll_mutex); + hotkey_poll_setup(0); + hotkey_warn_no_poll(); + mutex_unlock(&hotkey_poll_mutex); } return (tp_features.hotkey)? 0 : 1; @@ -1182,6 +1524,8 @@ static void hotkey_exit(void) { int res; + hotkey_poll_stop_sync(); + if (tp_features.hotkey) { dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n"); res = hotkey_set(hotkey_orig_status, hotkey_orig_mask); @@ -1232,7 +1576,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) scancode = hkey & 0xfff; if (scancode > 0 && scancode < 0x21) { scancode--; - tpacpi_input_send_key(scancode); + if (!(hotkey_source_mask & (1 << scancode))) { + tpacpi_input_send_key(scancode); + } } else { printk(IBM_ERR "hotkey 0x%04x out of range for keyboard map\n", @@ -1286,6 +1632,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) static void hotkey_resume(void) { tpacpi_input_send_radiosw(); + hotkey_poll_setup_safe(1); } /* @@ -4761,6 +5108,8 @@ static int __init thinkpad_acpi_module_init(void) IBM_ACPIHANDLE_INIT(ecrd); IBM_ACPIHANDLE_INIT(ecwr); + mutex_init(&thinkpad_nvram_mutex); + proc_dir = proc_mkdir(IBM_PROC_DIR, acpi_root_dir); if (!proc_dir) { printk(IBM_ERR "unable to create proc dir " IBM_PROC_DIR); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index fa64dedd26d..0369a889cb6 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include #include @@ -81,10 +84,31 @@ #define TP_CMOS_BRIGHTNESS_UP 4 #define TP_CMOS_BRIGHTNESS_DOWN 5 -/* ThinkPad CMOS NVRAM constants */ -#define TP_NVRAM_ADDR_BRIGHTNESS 0x5e -#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07 -#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0 +/* NVRAM Addresses */ +enum tp_nvram_addr { + TP_NVRAM_ADDR_HK2 = 0x57, + TP_NVRAM_ADDR_THINKLIGHT = 0x58, + TP_NVRAM_ADDR_VIDEO = 0x59, + TP_NVRAM_ADDR_BRIGHTNESS = 0x5e, + TP_NVRAM_ADDR_MIXER = 0x60, +}; + +/* NVRAM bit masks */ +enum { + TP_NVRAM_MASK_HKT_THINKPAD = 0x08, + TP_NVRAM_MASK_HKT_ZOOM = 0x20, + TP_NVRAM_MASK_HKT_DISPLAY = 0x40, + TP_NVRAM_MASK_HKT_HIBERNATE = 0x80, + TP_NVRAM_MASK_THINKLIGHT = 0x10, + TP_NVRAM_MASK_HKT_DISPEXPND = 0x30, + TP_NVRAM_MASK_HKT_BRIGHTNESS = 0x20, + TP_NVRAM_MASK_LEVEL_BRIGHTNESS = 0x07, + TP_NVRAM_POS_LEVEL_BRIGHTNESS = 0, + TP_NVRAM_MASK_MUTE = 0x40, + TP_NVRAM_MASK_HKT_VOLUME = 0x80, + TP_NVRAM_MASK_LEVEL_VOLUME = 0x0f, + TP_NVRAM_POS_LEVEL_VOLUME = 0, +}; #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -249,6 +273,7 @@ static struct { u32 input_device_registered:1; u32 platform_drv_registered:1; u32 platform_drv_attrs_registered:1; + u32 hotkey_poll_active:1; } tp_features; struct thinkpad_id_data { @@ -447,6 +472,33 @@ static int fan_write_cmd_watchdog(const char *cmd, int *rc); * Hotkey subdriver */ +enum { /* hot key scan codes (derived from ACPI DSDT) */ + TP_ACPI_HOTKEYSCAN_FNF1 = 0, + TP_ACPI_HOTKEYSCAN_FNF2, + TP_ACPI_HOTKEYSCAN_FNF3, + TP_ACPI_HOTKEYSCAN_FNF4, + TP_ACPI_HOTKEYSCAN_FNF5, + TP_ACPI_HOTKEYSCAN_FNF6, + TP_ACPI_HOTKEYSCAN_FNF7, + TP_ACPI_HOTKEYSCAN_FNF8, + TP_ACPI_HOTKEYSCAN_FNF9, + TP_ACPI_HOTKEYSCAN_FNF10, + TP_ACPI_HOTKEYSCAN_FNF11, + TP_ACPI_HOTKEYSCAN_FNF12, + TP_ACPI_HOTKEYSCAN_FNBACKSPACE, + TP_ACPI_HOTKEYSCAN_FNINSERT, + TP_ACPI_HOTKEYSCAN_FNDELETE, + TP_ACPI_HOTKEYSCAN_FNHOME, + TP_ACPI_HOTKEYSCAN_FNEND, + TP_ACPI_HOTKEYSCAN_FNPAGEUP, + TP_ACPI_HOTKEYSCAN_FNPAGEDOWN, + TP_ACPI_HOTKEYSCAN_FNSPACE, + TP_ACPI_HOTKEYSCAN_VOLUMEUP, + TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, + TP_ACPI_HOTKEYSCAN_MUTE, + TP_ACPI_HOTKEYSCAN_THINKPAD, +}; + static int hotkey_orig_status; static u32 hotkey_orig_mask; -- 2.11.4.GIT