Add a sysfs interface to allow userspace to modify the mapping between ThinkPad hotkeys and the keycode input events they generate. Signed-off-by: Henrique de Moraes Holschuh <hmh@xxxxxxxxxx> --- drivers/misc/thinkpad_acpi.c | 109 +++++++++++++++++++++++++++++++++++++++-- 1 files changed, 103 insertions(+), 6 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index fad3bd0..5690247 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -809,6 +809,84 @@ static ssize_t hotkey_bios_mask_show(struct device *dev, static struct device_attribute dev_attr_hotkey_bios_mask = __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); +/* sysfs hotkey event_map ---------------------------------------------- */ +static ssize_t hotkey_event_map_read(struct kobject *kobj, char *buffer, + loff_t off, size_t count) +{ + int res; + + BUG_ON(off < 0); + + /* sanitize */ + if (off > sizeof(hotkey_event_map)) + return 0; + if (off + count > sizeof(hotkey_event_map)) + count = sizeof(hotkey_event_map) - off; + + if (count > 0) { + res = mutex_lock_interruptible(&hotkey_mutex); + if (res < 0) + return res; + + /* Attribute is little-endian. Since this driver is for + * ia32 and amd-64 only, conversion is not required */ + memcpy(buffer, (char *)hotkey_event_map + off, count); + + mutex_unlock(&hotkey_mutex); + } + + return count; +} + +static ssize_t hotkey_event_map_write(struct kobject *kobj, char *buffer, + loff_t off, size_t count) +{ + unsigned int i; + u16 *p; + int res; + + /* + * Due to weirdness in the sysfs bin attribute support, + * for fixed-size attributes, we accept only full writes + * of the entire buffer. + * + * Too bad we can't force a fixed size for the inode, + * that would allow proper support. On 2.6.20, a short + * write can truncate the file(!) and break subsequent + * reads of the binary attribute. + */ + if (off != 0 || count != sizeof(hotkey_event_map)) + return -EINVAL; + + /* validate buffer */ + for (i = 0, p = (u16 *)buffer; i < count; i += 2, p++) + if (*p > KEY_MAX) + return -EINVAL; + + res = mutex_lock_interruptible(&hotkey_mutex); + if (res < 0) + return res; + + /* Attribute is little-endian. Since this driver is for + * ia32 and amd-64 only, conversion is not required */ + memcpy((char *)hotkey_event_map + off, buffer, count); + + mutex_unlock(&hotkey_mutex); + + return count; +} + +static struct bin_attribute bin_attr_hotkey_event_map = { + .attr = { + .name = "hotkey_event_map", + .mode = S_IWUSR | S_IRUGO, + .owner = THIS_MODULE, + }, + .size = sizeof(hotkey_event_map), + .read = hotkey_event_map_read, + .write = hotkey_event_map_write, +}; + /* --------------------------------------------------------------------- */ static struct attribute *hotkey_mask_attributes[] = { @@ -862,6 +940,12 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); + if (!res) { + res = sysfs_create_bin_file( + &tpacpi_pdev->dev.kobj, + &bin_attr_hotkey_event_map); + } + if (res) return res; @@ -885,6 +969,9 @@ static void hotkey_exit(void) } if (hotkey_dev_attributes) { + sysfs_remove_bin_file(&tpacpi_pdev->dev.kobj, + &bin_attr_hotkey_event_map); + delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); hotkey_dev_attributes = NULL; } @@ -900,18 +987,28 @@ static void tpacpi_input_send_key(unsigned int keycode) } } +static void hotkey_send_input_event(int hkey) +{ + unsigned int keycode; + + if (hkey > 0x1000 && hkey < 0x1011) { + mutex_lock(&hotkey_mutex); + keycode = hotkey_event_map[(hkey - 1) & 0x0f]; + mutex_unlock(&hotkey_mutex); + + tpacpi_input_send_key(keycode); + } else { + printk(IBM_ERR "unknown hotkey 0x%04x\n", hkey); + } +} + static void hotkey_notify(struct ibm_struct *ibm, u32 event) { int hkey; if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { acpi_bus_generate_event(ibm->acpi->device, event, hkey); - if (hkey > 0x1000 && hkey < 0x1011) { - tpacpi_input_send_key( - hotkey_event_map[(hkey - 1) & 0x0f]); - } else { - printk(IBM_ERR "unknown hotkey 0x%04x\n", hkey); - } + hotkey_send_input_event(hkey); } else { printk(IBM_ERR "unknown hotkey event %d\n", event); acpi_bus_generate_event(ibm->acpi->device, event, 0); -- 1.5.1.6 - To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html