Map volume and brightness events on thinkpads
[pandora-kernel.git] / drivers / misc / thinkpad_acpi.c
index f15a58f..81e068f 100644 (file)
@@ -21,7 +21,7 @@
  *  02110-1301, USA.
  */
 
-#define IBM_VERSION "0.15"
+#define IBM_VERSION "0.16"
 #define TPACPI_SYSFS_VERSION 0x010000
 
 /*
@@ -411,12 +411,13 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
 
        sprintf(ibm->acpi->driver->name, "%s_%s", IBM_NAME, ibm->name);
        ibm->acpi->driver->ids = ibm->acpi->hid;
+
        ibm->acpi->driver->ops.add = &tpacpi_device_add;
 
        rc = acpi_bus_register_driver(ibm->acpi->driver);
        if (rc < 0) {
                printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
-                      ibm->acpi->hid, rc);
+                      ibm->name, rc);
                kfree(ibm->acpi->driver);
                ibm->acpi->driver = NULL;
        } else if (!rc)
@@ -516,7 +517,7 @@ static char *next_cmd(char **cmds)
  ****************************************************************************/
 
 static struct platform_device *tpacpi_pdev;
-static struct class_device *tpacpi_hwmon;
+static struct device *tpacpi_hwmon;
 static struct input_dev *tpacpi_inputdev;
 
 
@@ -905,9 +906,26 @@ static ssize_t hotkey_radio_sw_show(struct device *dev,
 static struct device_attribute dev_attr_hotkey_radio_sw =
        __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL);
 
+/* sysfs hotkey report_mode -------------------------------------------- */
+static ssize_t hotkey_report_mode_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+               (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
+}
+
+static struct device_attribute dev_attr_hotkey_report_mode =
+       __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
+
 /* --------------------------------------------------------------------- */
 
-static struct attribute *hotkey_mask_attributes[] = {
+static struct attribute *hotkey_attributes[] __initdata = {
+       &dev_attr_hotkey_enable.attr,
+       &dev_attr_hotkey_report_mode.attr,
+};
+
+static struct attribute *hotkey_mask_attributes[] __initdata = {
        &dev_attr_hotkey_mask.attr,
        &dev_attr_hotkey_bios_enabled.attr,
        &dev_attr_hotkey_bios_mask.attr,
@@ -927,15 +945,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
                KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
                KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
-               KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
+               KEY_BRIGHTNESSUP,       /* 0x0F: FN+HOME (brightness up) */
                /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
-               KEY_RESERVED,   /* 0x10: FN+END (brightness down) */
+               KEY_BRIGHTNESSDOWN,     /* 0x10: FN+END (brightness down) */
                KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
                KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
                KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
-               KEY_RESERVED,   /* 0x14: VOLUME UP */
-               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
-               KEY_RESERVED,   /* 0x16: MUTE */
+               KEY_VOLUMEUP,   /* 0x14: VOLUME UP */
+               KEY_VOLUMEDOWN, /* 0x15: VOLUME DOWN */
+               KEY_MUTE,       /* 0x16: MUTE */
                KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
                /* (assignments unknown, please report if found) */
                KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
@@ -956,9 +974,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
                KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
                KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
-               KEY_RESERVED,   /* 0x14: VOLUME UP */
-               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
-               KEY_RESERVED,   /* 0x16: MUTE */
+               KEY_VOLUMEUP,   /* 0x14: VOLUME UP */
+               KEY_VOLUMEDOWN, /* 0x15: VOLUME DOWN */
+               KEY_MUTE,       /* 0x16: MUTE */
                KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
                /* (assignments unknown, please report if found) */
                KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
@@ -986,11 +1004,12 @@ 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(7, NULL);
+               hotkey_dev_attributes = create_attr_set(8, NULL);
                if (!hotkey_dev_attributes)
                        return -ENOMEM;
-               res = add_to_attr_set(hotkey_dev_attributes,
-                               &dev_attr_hotkey_enable.attr);
+               res = add_many_to_attr_set(hotkey_dev_attributes,
+                               hotkey_attributes,
+                               ARRAY_SIZE(hotkey_attributes));
                if (res)
                        return res;
 
@@ -1054,11 +1073,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                                TPACPI_HOTKEY_MAP_SIZE);
                }
 
-#ifndef CONFIG_THINKPAD_ACPI_INPUT_ENABLED
-               for (i = 0; i < 12; i++)
-                       hotkey_keycode_map[i] = KEY_UNKNOWN;
-#endif /* ! CONFIG_THINKPAD_ACPI_INPUT_ENABLED */
-
                set_bit(EV_KEY, tpacpi_inputdev->evbit);
                set_bit(EV_MSC, tpacpi_inputdev->evbit);
                set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
@@ -1080,14 +1094,17 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                        set_bit(SW_RADIO, tpacpi_inputdev->swbit);
                }
 
-#ifdef CONFIG_THINKPAD_ACPI_INPUT_ENABLED
                dbg_printk(TPACPI_DBG_INIT,
                                "enabling hot key handling\n");
                res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask)
                                        | hotkey_orig_mask);
                if (res)
                        return res;
-#endif /* CONFIG_THINKPAD_ACPI_INPUT_ENABLED */
+
+               dbg_printk(TPACPI_DBG_INIT,
+                               "legacy hot key reporting over procfs %s\n",
+                               (hotkey_report_mode < 2) ?
+                                       "enabled" : "disabled");
        }
 
        return (tp_features.hotkey)? 0 : 1;
@@ -1141,58 +1158,65 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
 {
        u32 hkey;
        unsigned int keycode, scancode;
-       int sendacpi = 1;
+       int send_acpi_ev = 0;
 
        if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
-               if (tpacpi_inputdev->users > 0) {
-                       switch (hkey >> 12) {
-                       case 1:
-                               /* 0x1000-0x1FFF: key presses */
-                               scancode = hkey & 0xfff;
-                               if (scancode > 0 && scancode < 0x21) {
-                                       scancode--;
-                                       keycode = hotkey_keycode_map[scancode];
-                                       tpacpi_input_send_key(scancode, keycode);
-                                       sendacpi = (keycode == KEY_RESERVED
-                                               || keycode == KEY_UNKNOWN);
-                               } else {
-                                       printk(IBM_ERR
-                                              "hotkey 0x%04x out of range for keyboard map\n",
-                                              hkey);
-                               }
-                               break;
-                       case 5:
-                               /* 0x5000-0x5FFF: LID */
-                               /* we don't handle it through this path, just
-                                * eat up known LID events */
-                               if (hkey != 0x5001 && hkey != 0x5002) {
-                                       printk(IBM_ERR
-                                               "unknown LID-related hotkey event: 0x%04x\n",
-                                               hkey);
-                               }
+               switch (hkey >> 12) {
+               case 1:
+                       /* 0x1000-0x1FFF: key presses */
+                       scancode = hkey & 0xfff;
+                       if (scancode > 0 && scancode < 0x21) {
+                               scancode--;
+                               keycode = hotkey_keycode_map[scancode];
+                               tpacpi_input_send_key(scancode, keycode);
+                       } else {
+                               printk(IBM_ERR
+                                      "hotkey 0x%04x out of range for keyboard map\n",
+                                      hkey);
+                               send_acpi_ev = 1;
+                       }
+                       break;
+               case 5:
+                       /* 0x5000-0x5FFF: LID */
+                       /* we don't handle it through this path, just
+                        * eat up known LID events */
+                       if (hkey != 0x5001 && hkey != 0x5002) {
+                               printk(IBM_ERR
+                                       "unknown LID-related hotkey event: 0x%04x\n",
+                                       hkey);
+                               send_acpi_ev = 1;
+                       }
+                       break;
+               case 7:
+                       /* 0x7000-0x7FFF: misc */
+                       if (tp_features.hotkey_wlsw && hkey == 0x7000) {
+                               tpacpi_input_send_radiosw();
                                break;
-                       case 7:
-                               /* 0x7000-0x7FFF: misc */
-                               if (tp_features.hotkey_wlsw && hkey == 0x7000) {
-                                               tpacpi_input_send_radiosw();
-                                               sendacpi = 0;
-                                       break;
-                               }
-                               /* fallthrough to default */
-                       default:
-                               /* case 2: dock-related */
-                               /*      0x2305 - T43 waking up due to bay lever eject while aslept */
-                               /* case 3: ultra-bay related. maybe bay in dock? */
-                               /*      0x3003 - T43 after wake up by bay lever eject (0x2305) */
-                               printk(IBM_NOTICE "unhandled hotkey event 0x%04x\n", hkey);
                        }
+                       /* fallthrough to default */
+               default:
+                       /* case 2: dock-related */
+                       /*      0x2305 - T43 waking up due to bay lever eject while aslept */
+                       /* case 3: ultra-bay related. maybe bay in dock? */
+                       /*      0x3003 - T43 after wake up by bay lever eject (0x2305) */
+                       printk(IBM_NOTICE "unhandled HKEY event 0x%04x\n", hkey);
+                       send_acpi_ev = 1;
                }
-
-               if (sendacpi)
-                       acpi_bus_generate_event(ibm->acpi->device, event, hkey);
        } else {
                printk(IBM_ERR "unknown hotkey notification event %d\n", event);
-               acpi_bus_generate_event(ibm->acpi->device, event, 0);
+               hkey = 0;
+               send_acpi_ev = 1;
+       }
+
+       /* Legacy events */
+       if (send_acpi_ev || hotkey_report_mode < 2)
+               acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey);
+
+       /* netlink events */
+       if (send_acpi_ev) {
+               acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
+                                               ibm->acpi->device->dev.bus_id,
+                                               event, hkey);
        }
 }
 
@@ -1316,8 +1340,13 @@ errexit:
        return res;
 }
 
+static const struct acpi_device_id ibm_htk_device_ids[] = {
+       {IBM_HKEY_HID, 0},
+       {"", 0},
+};
+
 static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
-       .hid = IBM_HKEY_HID,
+       .hid = ibm_htk_device_ids,
        .notify = hotkey_notify,
        .handle = &hkey_handle,
        .type = ACPI_DEVICE_NOTIFY,
@@ -2080,6 +2109,11 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK",     /* X30, X31, X40 */
 /* don't list other alternatives as we install a notify handler on the 570 */
 IBM_HANDLE(pci, root, "\\_SB.PCI");    /* 570 */
 
+static const struct acpi_device_id ibm_pci_device_ids[] = {
+       {PCI_ROOT_HID_STRING, 0},
+       {"", 0},
+};
+
 static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
        {
         .notify = dock_notify,
@@ -2090,7 +2124,7 @@ static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
        /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING.
         * We just use it to get notifications of dock hotplug
         * in very old thinkpads */
-        .hid = PCI_ROOT_HID_STRING,
+        .hid = ibm_pci_device_ids,
         .notify = dock_notify,
         .handle = &pci_handle,
         .type = ACPI_SYSTEM_NOTIFY,
@@ -2149,23 +2183,29 @@ static int __init dock_init2(struct ibm_init_struct *iibm)
 static void dock_notify(struct ibm_struct *ibm, u32 event)
 {
        int docked = dock_docked();
-       int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, PCI_ROOT_HID_STRING);
+       int pci = ibm->acpi->hid && ibm->acpi->device &&
+               acpi_match_device_ids(ibm->acpi->device, ibm_pci_device_ids);
+       int data;
 
        if (event == 1 && !pci) /* 570 */
-               acpi_bus_generate_event(ibm->acpi->device, event, 1);   /* button */
+               data = 1;       /* button */
        else if (event == 1 && pci)     /* 570 */
-               acpi_bus_generate_event(ibm->acpi->device, event, 3);   /* dock */
+               data = 3;       /* dock */
        else if (event == 3 && docked)
-               acpi_bus_generate_event(ibm->acpi->device, event, 1);   /* button */
+               data = 1;       /* button */
        else if (event == 3 && !docked)
-               acpi_bus_generate_event(ibm->acpi->device, event, 2);   /* undock */
+               data = 2;       /* undock */
        else if (event == 0 && docked)
-               acpi_bus_generate_event(ibm->acpi->device, event, 3);   /* dock */
+               data = 3;       /* dock */
        else {
                printk(IBM_ERR "unknown dock event %d, status %d\n",
                       event, _sta(dock_handle));
-               acpi_bus_generate_event(ibm->acpi->device, event, 0);   /* unknown */
+               data = 0;       /* unknown */
        }
+       acpi_bus_generate_proc_event(ibm->acpi->device, event, data);
+       acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
+                                         ibm->acpi->device->dev.bus_id,
+                                         event, data);
 }
 
 static int dock_read(char *p)
@@ -2263,7 +2303,10 @@ static int __init bay_init(struct ibm_init_struct *iibm)
 
 static void bay_notify(struct ibm_struct *ibm, u32 event)
 {
-       acpi_bus_generate_event(ibm->acpi->device, event, 0);
+       acpi_bus_generate_proc_event(ibm->acpi->device, event, 0);
+       acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
+                                         ibm->acpi->device->dev.bus_id,
+                                         event, 0);
 }
 
 #define bay_occupied(b) (_sta(b##_handle) & 1)
@@ -4405,7 +4448,7 @@ static void ibm_exit(struct ibm_struct *ibm)
 
 static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
 {
-       struct dmi_device *dev = NULL;
+       const struct dmi_device *dev = NULL;
        char ec_fw_string[18];
 
        if (!tp)
@@ -4603,6 +4646,9 @@ module_param_named(fan_control, fan_control_allowed, bool, 0);
 static int brightness_mode;
 module_param_named(brightness_mode, brightness_mode, int, 0);
 
+static unsigned int hotkey_report_mode;
+module_param(hotkey_report_mode, uint, 0);
+
 #define IBM_PARAM(feature) \
        module_param_call(feature, set_ibm_param, NULL, NULL, 0)
 
@@ -4628,6 +4674,10 @@ static int __init thinkpad_acpi_module_init(void)
 {
        int ret, i;
 
+       /* Parameter checking */
+       if (hotkey_report_mode > 2)
+               return -EINVAL;
+
        /* Driver-level probe */
 
        get_thinkpad_model_data(&thinkpad_id);
@@ -4656,12 +4706,15 @@ static int __init thinkpad_acpi_module_init(void)
                thinkpad_acpi_module_exit();
                return ret;
        }
+       tp_features.platform_drv_registered = 1;
+
        ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
        if (ret) {
                printk(IBM_ERR "unable to create sysfs driver attributes\n");
                thinkpad_acpi_module_exit();
                return ret;
        }
+       tp_features.platform_drv_attrs_registered = 1;
 
 
        /* Device initialization */
@@ -4744,8 +4797,11 @@ static void thinkpad_acpi_module_exit(void)
        if (tpacpi_pdev)
                platform_device_unregister(tpacpi_pdev);
 
-       tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
-       platform_driver_unregister(&tpacpi_pdriver);
+       if (tp_features.platform_drv_attrs_registered)
+               tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
+
+       if (tp_features.platform_drv_registered)
+               platform_driver_unregister(&tpacpi_pdriver);
 
        if (proc_dir)
                remove_proc_entry(IBM_PROC_DIR, acpi_root_dir);