Merge branches 'release' and 'stats' into release
[sfrench/cifs-2.6.git] / drivers / acpi / dock.c
index 54a697f9aa1873400cd2ad1237b786e4f3f9550e..b3dec2101e2e7ac7e28b43f5e4280fcb20831fcf 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/notifier.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
+#include <linux/stddef.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -39,10 +40,23 @@ MODULE_AUTHOR("Kristen Carlson Accardi");
 MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
 MODULE_LICENSE("GPL");
 
+static int immediate_undock = 1;
+module_param(immediate_undock, bool, 0644);
+MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
+       "undock immediately when the undock button is pressed, 0 will cause"
+       " the driver to wait for userspace to write the undock sysfs file "
+       " before undocking");
+
 static struct atomic_notifier_head dock_notifier_list;
-static struct platform_device dock_device;
+static struct platform_device *dock_device;
 static char dock_device_name[] = "dock";
 
+static const struct acpi_device_id dock_device_ids[] = {
+       {"LNXDOCK", 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, dock_device_ids);
+
 struct dock_station {
        acpi_handle handle;
        unsigned long last_dock_time;
@@ -62,6 +76,7 @@ struct dock_dependent_device {
 };
 
 #define DOCK_DOCKING   0x00000001
+#define DOCK_UNDOCKING  0x00000002
 #define DOCK_EVENT     3
 #define UNDOCK_EVENT   2
 
@@ -326,12 +341,20 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
 
 static void dock_event(struct dock_station *ds, u32 event, int num)
 {
-       struct device *dev = &dock_device.dev;
+       struct device *dev = &dock_device->dev;
+       char event_string[13];
+       char *envp[] = { event_string, NULL };
+
+       if (num == UNDOCK_EVENT)
+               sprintf(event_string, "EVENT=undock");
+       else
+               sprintf(event_string, "EVENT=dock");
+
        /*
         * Indicate that the status of the dock station has
         * changed.
         */
-       kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 }
 
 /**
@@ -379,12 +402,11 @@ static void handle_dock(struct dock_station *ds, int dock)
        union acpi_object arg;
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
        struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
 
        acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
-       obj = name_buffer.pointer;
 
-       printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
+       printk(KERN_INFO PREFIX "%s - %s\n",
+               (char *)name_buffer.pointer, dock ? "docking" : "undocking");
 
        /* _DCK method has one argument */
        arg_list.count = 1;
@@ -393,7 +415,8 @@ static void handle_dock(struct dock_station *ds, int dock)
        arg.integer.value = dock;
        status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
        if (ACPI_FAILURE(status))
-               pr_debug("%s: failed to execute _DCK\n", obj->string.pointer);
+               printk(KERN_ERR PREFIX "%s - failed to execute _DCK\n",
+                        (char *)name_buffer.pointer);
        kfree(buffer.pointer);
        kfree(name_buffer.pointer);
 }
@@ -419,6 +442,16 @@ static inline void complete_dock(struct dock_station *ds)
        ds->last_dock_time = jiffies;
 }
 
+static inline void begin_undock(struct dock_station *ds)
+{
+       ds->flags |= DOCK_UNDOCKING;
+}
+
+static inline void complete_undock(struct dock_station *ds)
+{
+       ds->flags &= ~(DOCK_UNDOCKING);
+}
+
 /**
  * dock_in_progress - see if we are in the middle of handling a dock event
  * @ds: the dock station
@@ -549,7 +582,7 @@ static int handle_eject_request(struct dock_station *ds, u32 event)
                printk(KERN_ERR PREFIX "Unable to undock!\n");
                return -EBUSY;
        }
-
+       complete_undock(ds);
        return 0;
 }
 
@@ -593,7 +626,11 @@ static void dock_notify(acpi_handle handle, u32 event, void *data)
         * to the driver who wish to hotplug.
          */
        case ACPI_NOTIFY_EJECT_REQUEST:
-               handle_eject_request(ds, event);
+               begin_undock(ds);
+               if (immediate_undock)
+                       handle_eject_request(ds, event);
+               else
+                       dock_event(ds, event, UNDOCK_EVENT);
                break;
        default:
                printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
@@ -651,6 +688,17 @@ static ssize_t show_docked(struct device *dev,
 }
 DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
 
+/*
+ * show_flags - read method for flags file in sysfs
+ */
+static ssize_t show_flags(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
+
+}
+DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
+
 /*
  * write_undock - write method for "undock" file in sysfs
  */
@@ -667,6 +715,22 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
 }
 DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
 
+/*
+ * show_dock_uid - read method for "uid" file in sysfs
+ */
+static ssize_t show_dock_uid(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       unsigned long lbuf;
+       acpi_status status = acpi_evaluate_integer(dock_station->handle,
+                                       "_UID", NULL, &lbuf);
+       if (ACPI_FAILURE(status))
+           return 0;
+
+       return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);
+}
+DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
+
 /**
  * dock_add - add a new dock station
  * @handle: the dock station handle
@@ -693,26 +757,53 @@ static int dock_add(acpi_handle handle)
        ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
 
        /* initialize platform device stuff */
-       dock_device.name = dock_device_name;
-       ret = platform_device_register(&dock_device);
+       dock_device =
+               platform_device_register_simple(dock_device_name, 0, NULL, 0);
+       if (IS_ERR(dock_device)) {
+               kfree(dock_station);
+               dock_station = NULL;
+               return PTR_ERR(dock_device);
+       }
+
+       /* we want the dock device to send uevents */
+       dock_device->dev.uevent_suppress = 0;
+
+       ret = device_create_file(&dock_device->dev, &dev_attr_docked);
        if (ret) {
-               printk(KERN_ERR PREFIX "Error %d registering dock device\n", ret);
+               printk("Error %d adding sysfs file\n", ret);
+               platform_device_unregister(dock_device);
                kfree(dock_station);
+               dock_station = NULL;
                return ret;
        }
-       ret = device_create_file(&dock_device.dev, &dev_attr_docked);
+       ret = device_create_file(&dock_device->dev, &dev_attr_undock);
        if (ret) {
                printk("Error %d adding sysfs file\n", ret);
-               platform_device_unregister(&dock_device);
+               device_remove_file(&dock_device->dev, &dev_attr_docked);
+               platform_device_unregister(dock_device);
                kfree(dock_station);
+               dock_station = NULL;
                return ret;
        }
-       ret = device_create_file(&dock_device.dev, &dev_attr_undock);
+       ret = device_create_file(&dock_device->dev, &dev_attr_uid);
        if (ret) {
                printk("Error %d adding sysfs file\n", ret);
-               device_remove_file(&dock_device.dev, &dev_attr_docked);
-               platform_device_unregister(&dock_device);
+               device_remove_file(&dock_device->dev, &dev_attr_docked);
+               device_remove_file(&dock_device->dev, &dev_attr_undock);
+               platform_device_unregister(dock_device);
                kfree(dock_station);
+               dock_station = NULL;
+               return ret;
+       }
+       ret = device_create_file(&dock_device->dev, &dev_attr_flags);
+       if (ret) {
+               printk("Error %d adding sysfs file\n", ret);
+               device_remove_file(&dock_device->dev, &dev_attr_docked);
+               device_remove_file(&dock_device->dev, &dev_attr_undock);
+               device_remove_file(&dock_device->dev, &dev_attr_uid);
+               platform_device_unregister(dock_device);
+               kfree(dock_station);
+               dock_station = NULL;
                return ret;
        }
 
@@ -725,6 +816,7 @@ static int dock_add(acpi_handle handle)
        dd = alloc_dock_dependent_device(handle);
        if (!dd) {
                kfree(dock_station);
+               dock_station = NULL;
                ret = -ENOMEM;
                goto dock_add_err_unregister;
        }
@@ -748,10 +840,13 @@ static int dock_add(acpi_handle handle)
 dock_add_err:
        kfree(dd);
 dock_add_err_unregister:
-       device_remove_file(&dock_device.dev, &dev_attr_docked);
-       device_remove_file(&dock_device.dev, &dev_attr_undock);
-       platform_device_unregister(&dock_device);
+       device_remove_file(&dock_device->dev, &dev_attr_docked);
+       device_remove_file(&dock_device->dev, &dev_attr_undock);
+       device_remove_file(&dock_device->dev, &dev_attr_uid);
+       device_remove_file(&dock_device->dev, &dev_attr_flags);
+       platform_device_unregister(dock_device);
        kfree(dock_station);
+       dock_station = NULL;
        return ret;
 }
 
@@ -779,12 +874,15 @@ static int dock_remove(void)
                printk(KERN_ERR "Error removing notify handler\n");
 
        /* cleanup sysfs */
-       device_remove_file(&dock_device.dev, &dev_attr_docked);
-       device_remove_file(&dock_device.dev, &dev_attr_undock);
-       platform_device_unregister(&dock_device);
+       device_remove_file(&dock_device->dev, &dev_attr_docked);
+       device_remove_file(&dock_device->dev, &dev_attr_undock);
+       device_remove_file(&dock_device->dev, &dev_attr_uid);
+       device_remove_file(&dock_device->dev, &dev_attr_flags);
+       platform_device_unregister(dock_device);
 
        /* free dock station memory */
        kfree(dock_station);
+       dock_station = NULL;
        return 0;
 }