Add undock attribute to dock sysfs

Add an undock attribute to the dock_station kobject and export it via sysfs.
Anything written to this file will cause an undock event to be triggered.

Warning: Compile tested only

Signed-off-by: Brandon Philips <brandon@ifup.org>
---
 drivers/acpi/dock.c |   78 ++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 61 insertions(+), 17 deletions(-)

Index: linux-2.6-rc/drivers/acpi/dock.c
===================================================================
--- linux-2.6-rc.orig/drivers/acpi/dock.c
+++ linux-2.6-rc/drivers/acpi/dock.c
@@ -511,6 +511,37 @@ void unregister_hotplug_dock_device(acpi
 EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
 
 /**
+ * handle_eject_request - handle an undock request checking for error conditions
+ *
+ * Check to make sure the dock device is still present, then undock and
+ * hotremove all the devices that may need removing.
+ */
+static int handle_eject_request(struct dock_station *ds, u32 event)
+{
+	if (!dock_present(ds))
+		return -ENODEV;
+
+	if (dock_in_progress(ds))
+		return -EBUSY;
+
+	/*
+	 * here we need to generate the undock
+	 * event prior to actually doing the undock
+	 * so that the device struct still exists.
+	 */
+	dock_event(ds, event, UNDOCK_EVENT);
+	hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
+	undock(ds);
+	eject_dock(ds);
+	if (dock_present(ds)) {
+		printk(KERN_ERR PREFIX "Unable to undock!\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/**
  * dock_notify - act upon an acpi dock notification
  * @handle: the dock station handle
  * @event: the acpi event
@@ -518,9 +549,7 @@ EXPORT_SYMBOL_GPL(unregister_hotplug_doc
  *
  * If we are notified to dock, then check to see if the dock is
  * present and then dock.  Notify all drivers of the dock event,
- * and then hotplug and devices that may need hotplugging.  For undock
- * check to make sure the dock device is still present, then undock
- * and hotremove all the devices that may need removing.
+ * and then hotplug and devices that may need hotplugging.
  */
 static void dock_notify(acpi_handle handle, u32 event, void *data)
 {
@@ -552,19 +581,7 @@ static void dock_notify(acpi_handle hand
 	 * to the driver who wish to hotplug.
          */
 	case ACPI_NOTIFY_EJECT_REQUEST:
-		if (!dock_in_progress(ds) && dock_present(ds)) {
-			/*
-			 * here we need to generate the undock
-			 * event prior to actually doing the undock
-			 * so that the device struct still exists.
-			 */
-			dock_event(ds, event, UNDOCK_EVENT);
-			hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
-			undock(ds);
-			eject_dock(ds);
-			if (dock_present(ds))
-				printk(KERN_ERR PREFIX "Unable to undock!\n");
-		}
+		handle_eject_request(ds, event);
 		break;
 	default:
 		printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
@@ -617,9 +634,29 @@ static ssize_t show_docked(struct acpi_d
 }
 ACPI_DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
 
+/*
+ * write_undock - write method for "undock" file in sysfs
+ *
+ * anything written to this file will cause an undock to be written
+ */
+static ssize_t write_undock(struct acpi_device *dev, const char *buf, size_t count)
+{
+	struct dock_station *ds = (struct dock_station *)dev->driver_data;
+	int ret;
+
+	if (!count)
+		return -EINVAL;
+
+	ret = handle_eject_request(ds, ACPI_NOTIFY_EJECT_REQUEST);
+
+	return ret ? ret: count;
+}
+ACPI_DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
+
 static void dock_remove_sysfs_files(struct acpi_device *ad)
 {
 	sysfs_remove_file(&ad->kobj, &acpi_device_attr_docked.attr);
+	sysfs_remove_file(&ad->kobj, &acpi_device_attr_undock.attr);
 }
 
 static int dock_add_sysfs_files(struct acpi_device *ad)
@@ -628,8 +665,15 @@ static int dock_add_sysfs_files(struct a
 
 	ret = sysfs_create_file(&ad->kobj, &acpi_device_attr_docked.attr);
 	if (ret)
-		dock_remove_sysfs_files(ad);;
+		goto error;
+	ret = sysfs_create_file(&ad->kobj, &acpi_device_attr_undock.attr);
+	if (ret)
+		goto error;
+
+	return 0;
 
+error:
+	dock_remove_sysfs_files(ad);;
 	return ret;
 }
 
