Merge branch 'linus' into perfcounters/core
[safe/jmp/linux-2.6] / drivers / acpi / scan.c
index ad06798..8ff510b 100644 (file)
@@ -10,7 +10,8 @@
 #include <linux/kthread.h>
 
 #include <acpi/acpi_drivers.h>
-#include <acpi/acinterp.h>     /* for acpi_ex_eisa_id_to_string() */
+
+#include "internal.h"
 
 #define _COMPONENT             ACPI_BUS_COMPONENT
 ACPI_MODULE_NAME("scan");
@@ -23,7 +24,7 @@ extern struct acpi_device *acpi_root;
 
 static LIST_HEAD(acpi_device_list);
 static LIST_HEAD(acpi_bus_id_list);
-DEFINE_SPINLOCK(acpi_device_lock);
+DEFINE_MUTEX(acpi_device_lock);
 LIST_HEAD(acpi_wakeup_device_list);
 
 struct acpi_device_bus_id{
@@ -109,20 +110,19 @@ static int acpi_bus_hot_remove_device(void *context)
                return 0;
 
        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-               "Hot-removing device %s...\n", device->dev.bus_id));
-
+               "Hot-removing device %s...\n", dev_name(&device->dev)));
 
        if (acpi_bus_trim(device, 1)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                               "Removing device failed\n"));
+               printk(KERN_ERR PREFIX
+                               "Removing device failed\n");
                return -1;
        }
 
        /* power off device */
        status = acpi_evaluate_object(handle, "_PS3", NULL, NULL);
        if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
-               ACPI_DEBUG_PRINT((ACPI_DB_WARN,
-                               "Power-off device failed\n"));
+               printk(KERN_WARNING PREFIX
+                               "Power-off device failed\n");
 
        if (device->flags.lockable) {
                arg_list.count = 1;
@@ -276,6 +276,13 @@ int acpi_match_device_ids(struct acpi_device *device,
 {
        const struct acpi_device_id *id;
 
+       /*
+        * If the device is not present, it is unnecessary to load device
+        * driver for it.
+        */
+       if (!device->status.present)
+               return -ENODEV;
+
        if (device->flags.hardware_id) {
                for (id = ids; id->id[0]; id++) {
                        if (!strcmp((char*)id->id, device->pnp.hardware_id))
@@ -352,6 +359,61 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
        return 0;
 }
 
+static void acpi_device_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct acpi_device *device = data;
+
+       device->driver->ops.notify(device, event);
+}
+
+static acpi_status acpi_device_notify_fixed(void *data)
+{
+       struct acpi_device *device = data;
+
+       acpi_device_notify(device->handle, ACPI_FIXED_HARDWARE_EVENT, device);
+       return AE_OK;
+}
+
+static int acpi_device_install_notify_handler(struct acpi_device *device)
+{
+       acpi_status status;
+       char *hid;
+
+       hid = acpi_device_hid(device);
+       if (!strcmp(hid, ACPI_BUTTON_HID_POWERF))
+               status =
+                   acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+                                                    acpi_device_notify_fixed,
+                                                    device);
+       else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEPF))
+               status =
+                   acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
+                                                    acpi_device_notify_fixed,
+                                                    device);
+       else
+               status = acpi_install_notify_handler(device->handle,
+                                                    ACPI_DEVICE_NOTIFY,
+                                                    acpi_device_notify,
+                                                    device);
+
+       if (ACPI_FAILURE(status))
+               return -EINVAL;
+       return 0;
+}
+
+static void acpi_device_remove_notify_handler(struct acpi_device *device)
+{
+       if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF))
+               acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+                                               acpi_device_notify_fixed);
+       else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF))
+               acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
+                                               acpi_device_notify_fixed);
+       else
+               acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+                                          acpi_device_notify);
+}
+
 static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *);
 static int acpi_start_single_object(struct acpi_device *);
 static int acpi_device_probe(struct device * dev)
@@ -364,6 +426,20 @@ static int acpi_device_probe(struct device * dev)
        if (!ret) {
                if (acpi_dev->bus_ops.acpi_op_start)
                        acpi_start_single_object(acpi_dev);
+
+               if (acpi_drv->ops.notify) {
+                       ret = acpi_device_install_notify_handler(acpi_dev);
+                       if (ret) {
+                               if (acpi_drv->ops.stop)
+                                       acpi_drv->ops.stop(acpi_dev,
+                                                  acpi_dev->removal_type);
+                               if (acpi_drv->ops.remove)
+                                       acpi_drv->ops.remove(acpi_dev,
+                                                    acpi_dev->removal_type);
+                               return ret;
+                       }
+               }
+
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                        "Found driver [%s] for device [%s]\n",
                        acpi_drv->name, acpi_dev->pnp.bus_id));
@@ -378,34 +454,24 @@ static int acpi_device_remove(struct device * dev)
        struct acpi_driver *acpi_drv = acpi_dev->driver;
 
        if (acpi_drv) {
+               if (acpi_drv->ops.notify)
+                       acpi_device_remove_notify_handler(acpi_dev);
                if (acpi_drv->ops.stop)
                        acpi_drv->ops.stop(acpi_dev, acpi_dev->removal_type);
                if (acpi_drv->ops.remove)
                        acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type);
        }
        acpi_dev->driver = NULL;
-       acpi_driver_data(dev) = NULL;
+       acpi_dev->driver_data = NULL;
 
        put_device(dev);
        return 0;
 }
 
-static void acpi_device_shutdown(struct device *dev)
-{
-       struct acpi_device *acpi_dev = to_acpi_device(dev);
-       struct acpi_driver *acpi_drv = acpi_dev->driver;
-
-       if (acpi_drv && acpi_drv->ops.shutdown)
-               acpi_drv->ops.shutdown(acpi_dev);
-
-       return ;
-}
-
 struct bus_type acpi_bus_type = {
        .name           = "acpi",
        .suspend        = acpi_device_suspend,
        .resume         = acpi_device_resume,
-       .shutdown       = acpi_device_shutdown,
        .match          = acpi_bus_match,
        .probe          = acpi_device_probe,
        .remove         = acpi_device_remove,
@@ -425,7 +491,6 @@ static int acpi_device_register(struct acpi_device *device,
         */
        INIT_LIST_HEAD(&device->children);
        INIT_LIST_HEAD(&device->node);
-       INIT_LIST_HEAD(&device->g_list);
        INIT_LIST_HEAD(&device->wakeup_list);
 
        new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);
@@ -434,7 +499,7 @@ static int acpi_device_register(struct acpi_device *device,
                return -ENOMEM;
        }
 
-       spin_lock(&acpi_device_lock);
+       mutex_lock(&acpi_device_lock);
        /*
         * Find suitable bus_id and instance number in acpi_bus_id_list
         * If failed, create one and link it into acpi_bus_id_list
@@ -453,16 +518,14 @@ static int acpi_device_register(struct acpi_device *device,
                acpi_device_bus_id->instance_no = 0;
                list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
        }
-       sprintf(device->dev.bus_id, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no);
+       dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no);
 
-       if (device->parent) {
+       if (device->parent)
                list_add_tail(&device->node, &device->parent->children);
-               list_add_tail(&device->g_list, &device->parent->g_list);
-       } else
-               list_add_tail(&device->g_list, &acpi_device_list);
+
        if (device->wakeup.flags.valid)
                list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
-       spin_unlock(&acpi_device_lock);
+       mutex_unlock(&acpi_device_lock);
 
        if (device->parent)
                device->dev.parent = &parent->dev;
@@ -477,33 +540,28 @@ static int acpi_device_register(struct acpi_device *device,
 
        result = acpi_device_setup_files(device);
        if(result)
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error creating sysfs interface for device %s\n", device->dev.bus_id));
+               printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n",
+                      dev_name(&device->dev));
 
        device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
        return 0;
   end:
-       spin_lock(&acpi_device_lock);
-       if (device->parent) {
+       mutex_lock(&acpi_device_lock);
+       if (device->parent)
                list_del(&device->node);
-               list_del(&device->g_list);
-       } else
-               list_del(&device->g_list);
        list_del(&device->wakeup_list);
-       spin_unlock(&acpi_device_lock);
+       mutex_unlock(&acpi_device_lock);
        return result;
 }
 
 static void acpi_device_unregister(struct acpi_device *device, int type)
 {
-       spin_lock(&acpi_device_lock);
-       if (device->parent) {
+       mutex_lock(&acpi_device_lock);
+       if (device->parent)
                list_del(&device->node);
-               list_del(&device->g_list);
-       } else
-               list_del(&device->g_list);
 
        list_del(&device->wakeup_list);
-       spin_unlock(&acpi_device_lock);
+       mutex_unlock(&acpi_device_lock);
 
        acpi_detach_data(device->handle, acpi_bus_data_handler);
 
@@ -537,7 +595,7 @@ acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver)
        result = driver->ops.add(device);
        if (result) {
                device->driver = NULL;
-               acpi_driver_data(device) = NULL;
+               device->driver_data = NULL;
                return result;
        }
 
@@ -902,36 +960,6 @@ static void acpi_device_get_busid(struct acpi_device *device,
        }
 }
 
-static int
-acpi_video_bus_match(struct acpi_device *device)
-{
-       acpi_handle h_dummy;
-
-       if (!device)
-               return -EINVAL;
-
-       /* Since there is no HID, CID for ACPI Video drivers, we have
-        * to check well known required nodes for each feature we support.
-        */
-
-       /* Does this device able to support video switching ? */
-       if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) &&
-           ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy)))
-               return 0;
-
-       /* Does this device able to retrieve a video ROM ? */
-       if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy)))
-               return 0;
-
-       /* Does this device able to configure which video head to be POSTed ? */
-       if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) &&
-           ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) &&
-           ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy)))
-               return 0;
-
-       return -ENODEV;
-}
-
 /*
  * acpi_bay_match - see if a device is an ejectable driver bay
  *
@@ -1014,7 +1042,7 @@ static void acpi_device_set_id(struct acpi_device *device,
                   will get autoloaded and the device might still match
                   against another driver.
                */
-               if (ACPI_SUCCESS(acpi_video_bus_match(device)))
+               if (acpi_is_video_device(device))
                        cid_add = ACPI_VIDEO_HID;
                else if (ACPI_SUCCESS(acpi_bay_match(device)))
                        cid_add = ACPI_BAY_HID;
@@ -1026,7 +1054,7 @@ static void acpi_device_set_id(struct acpi_device *device,
                hid = ACPI_POWER_HID;
                break;
        case ACPI_BUS_TYPE_PROCESSOR:
-               hid = ACPI_PROCESSOR_HID;
+               hid = ACPI_PROCESSOR_OBJECT_HID;
                break;
        case ACPI_BUS_TYPE_SYSTEM:
                hid = ACPI_SYSTEM_HID;
@@ -1154,20 +1182,6 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice)
 }
 
 static int
-acpi_is_child_device(struct acpi_device *device,
-                       int (*matcher)(struct acpi_device *))
-{
-       int result = -ENODEV;
-
-       do {
-               if (ACPI_SUCCESS(matcher(device)))
-                       return AE_OK;
-       } while ((device = device->parent));
-
-       return result;
-}
-
-static int
 acpi_add_single_object(struct acpi_device **child,
                       struct acpi_device *parent, acpi_handle handle, int type,
                        struct acpi_bus_ops *ops)
@@ -1222,15 +1236,18 @@ acpi_add_single_object(struct acpi_device **child,
                        result = -ENODEV;
                        goto end;
                }
-               if (!device->status.present) {
-                       /* Bay and dock should be handled even if absent */
-                       if (!ACPI_SUCCESS(
-                            acpi_is_child_device(device, acpi_bay_match)) &&
-                           !ACPI_SUCCESS(
-                            acpi_is_child_device(device, acpi_dock_match))) {
-                                       result = -ENODEV;
-                                       goto end;
-                       }
+               /*
+                * When the device is neither present nor functional, the
+                * device should not be added to Linux ACPI device tree.
+                * When the status of the device is not present but functinal,
+                * it should be added to Linux ACPI tree. For example : bay
+                * device , dock device.
+                * In such conditions it is unncessary to check whether it is
+                * bay device or dock device.
+                */
+               if (!device->status.present && !device->status.functional) {
+                       result = -ENODEV;
+                       goto end;
                }
                break;
        default:
@@ -1411,7 +1428,12 @@ static int acpi_bus_scan(struct acpi_device *start, struct acpi_bus_ops *ops)
                 * TBD: Need notifications and other detection mechanisms
                 *      in place before we can fully implement this.
                 */
-               if (child->status.present) {
+                /*
+                * When the device is not present but functional, it is also
+                * necessary to scan the children of this device.
+                */
+               if (child->status.present || (!child->status.present &&
+                                       child->status.functional)) {
                        status = acpi_get_next_object(ACPI_TYPE_ANY, chandle,
                                                      NULL, NULL);
                        if (ACPI_SUCCESS(status)) {
@@ -1554,17 +1576,11 @@ static int acpi_bus_scan_fixed(struct acpi_device *root)
        return result;
 }
 
-int __init acpi_boot_ec_enable(void);
-
-static int __init acpi_scan_init(void)
+int __init acpi_scan_init(void)
 {
        int result;
        struct acpi_bus_ops ops;
 
-
-       if (acpi_disabled)
-               return 0;
-
        memset(&ops, 0, sizeof(ops));
        ops.acpi_op_add = 1;
        ops.acpi_op_start = 1;
@@ -1588,9 +1604,6 @@ static int __init acpi_scan_init(void)
         */
        result = acpi_bus_scan_fixed(acpi_root);
 
-       /* EC region might be needed at bus_scan, so enable it now */
-       acpi_boot_ec_enable();
-
        if (!result)
                result = acpi_bus_scan(acpi_root, &ops);
 
@@ -1600,5 +1613,3 @@ static int __init acpi_scan_init(void)
       Done:
        return result;
 }
-
-subsys_initcall(acpi_scan_init);