Merge branch 'acpi-hotplug'
[pandora-kernel.git] / drivers / base / core.c
index 2499cef..2166f34 100644 (file)
@@ -403,6 +403,36 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
 static struct device_attribute uevent_attr =
        __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
 
+static ssize_t show_online(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       bool val;
+
+       lock_device_hotplug();
+       val = !dev->offline;
+       unlock_device_hotplug();
+       return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t store_online(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       bool val;
+       int ret;
+
+       ret = strtobool(buf, &val);
+       if (ret < 0)
+               return ret;
+
+       lock_device_hotplug();
+       ret = val ? device_online(dev) : device_offline(dev);
+       unlock_device_hotplug();
+       return ret < 0 ? ret : count;
+}
+
+static struct device_attribute online_attr =
+       __ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online);
+
 static int device_add_attributes(struct device *dev,
                                 struct device_attribute *attrs)
 {
@@ -516,6 +546,12 @@ static int device_add_attrs(struct device *dev)
        if (error)
                goto err_remove_type_groups;
 
+       if (device_supports_offline(dev) && !dev->offline_disabled) {
+               error = device_create_file(dev, &online_attr);
+               if (error)
+                       goto err_remove_type_groups;
+       }
+
        return 0;
 
  err_remove_type_groups:
@@ -536,6 +572,7 @@ static void device_remove_attrs(struct device *dev)
        struct class *class = dev->class;
        const struct device_type *type = dev->type;
 
+       device_remove_file(dev, &online_attr);
        device_remove_groups(dev, dev->groups);
 
        if (type)
@@ -1433,6 +1470,99 @@ EXPORT_SYMBOL_GPL(put_device);
 EXPORT_SYMBOL_GPL(device_create_file);
 EXPORT_SYMBOL_GPL(device_remove_file);
 
+static DEFINE_MUTEX(device_hotplug_lock);
+
+void lock_device_hotplug(void)
+{
+       mutex_lock(&device_hotplug_lock);
+}
+
+void unlock_device_hotplug(void)
+{
+       mutex_unlock(&device_hotplug_lock);
+}
+
+static int device_check_offline(struct device *dev, void *not_used)
+{
+       int ret;
+
+       ret = device_for_each_child(dev, NULL, device_check_offline);
+       if (ret)
+               return ret;
+
+       return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0;
+}
+
+/**
+ * device_offline - Prepare the device for hot-removal.
+ * @dev: Device to be put offline.
+ *
+ * Execute the device bus type's .offline() callback, if present, to prepare
+ * the device for a subsequent hot-removal.  If that succeeds, the device must
+ * not be used until either it is removed or its bus type's .online() callback
+ * is executed.
+ *
+ * Call under device_hotplug_lock.
+ */
+int device_offline(struct device *dev)
+{
+       int ret;
+
+       if (dev->offline_disabled)
+               return -EPERM;
+
+       ret = device_for_each_child(dev, NULL, device_check_offline);
+       if (ret)
+               return ret;
+
+       device_lock(dev);
+       if (device_supports_offline(dev)) {
+               if (dev->offline) {
+                       ret = 1;
+               } else {
+                       ret = dev->bus->offline(dev);
+                       if (!ret) {
+                               kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
+                               dev->offline = true;
+                       }
+               }
+       }
+       device_unlock(dev);
+
+       return ret;
+}
+
+/**
+ * device_online - Put the device back online after successful device_offline().
+ * @dev: Device to be put back online.
+ *
+ * If device_offline() has been successfully executed for @dev, but the device
+ * has not been removed subsequently, execute its bus type's .online() callback
+ * to indicate that the device can be used again.
+ *
+ * Call under device_hotplug_lock.
+ */
+int device_online(struct device *dev)
+{
+       int ret = 0;
+
+       device_lock(dev);
+       if (device_supports_offline(dev)) {
+               if (dev->offline) {
+                       ret = dev->bus->online(dev);
+                       if (!ret) {
+                               kobject_uevent(&dev->kobj, KOBJ_ONLINE);
+                               dev->offline = false;
+                       }
+               } else {
+                       ret = 1;
+               }
+       }
+       device_unlock(dev);
+
+       return ret;
+}
+
 struct root_device {
        struct device dev;
        struct module *owner;