linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 1/3] acpi: dock driver
       [not found] <20060412221027.472109000@intel.com>
@ 2006-04-12 22:18 ` Kristen Accardi
  2006-04-12 22:35   ` [patch 1/3] acpi: dock driver (refreshed) Kristen Accardi
  2006-04-13  5:27   ` [patch 1/3] acpi: dock driver Andrew Morton
  2006-04-12 22:18 ` [patch 2/3] acpiphp: use new " Kristen Accardi
  2006-04-12 22:18 ` [patch 3/3] acpiphp: prevent duplicate slot numbers when no _SUN Kristen Accardi
  2 siblings, 2 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-12 22:18 UTC (permalink / raw)
  To: len.brown, greg
  Cc: linux-acpi, pcihpd-discuss, linux-kernel, mochel, arjan,
	muneda.takahiro, pavel, temnota, Kristen Carlson Accardi

Create a driver which lives in the acpi subsystem to handle dock events.  This 
driver is not an acpi driver, because acpi drivers require that the object
be present when the driver is loaded.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---
 drivers/acpi/Kconfig        |    6 
 drivers/acpi/Makefile       |    1 
 drivers/acpi/dock.c         |  684 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c         |   23 +
 include/acpi/acpi_bus.h     |    2 
 include/acpi/acpi_drivers.h |    9 
 6 files changed, 724 insertions(+), 1 deletion(-)

--- /dev/null
+++ 2.6-git-kca2/drivers/acpi/dock.c
@@ -0,0 +1,684 @@
+/*
+ *  dock.c - ACPI dock station driver
+ *
+ *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_DOCK_COMPONENT 0x10000000
+#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
+#define _COMPONENT		ACPI_DOCK_COMPONENT
+
+ACPI_MODULE_NAME("dock")
+MODULE_AUTHOR("Kristen Carlson Accardi");
+MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static struct atomic_notifier_head dock_notifier_list;
+
+struct dock_station {
+	acpi_handle handle;
+	u32 last_dock_time;
+	u32 flags;
+	struct list_head dependent_devices;
+	struct list_head hotplug_devices;
+};
+
+
+struct dock_dependent_device {
+	struct list_head list;
+	struct list_head hotplug_list;
+	acpi_handle handle;
+	acpi_notify_handler handler;
+	void *context;
+};
+
+#define DOCK_DOCKING	0x00000001
+
+static struct dock_station *ds;
+
+/*****************************************************************************
+ *                         Dock Dependent device functions                   *
+ *****************************************************************************/
+/**
+ *  alloc_dock_dependent_device - allocate and init a dependent device
+ *  @handle: the acpi_handle of the dependent device
+ *
+ *  Allocate memory for a dependent device structure for a device referenced
+ *  by the acpi handle
+ */
+static struct dock_dependent_device * alloc_dock_dependent_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd) {
+		dd->handle = handle;
+		INIT_LIST_HEAD(&dd->list);
+		INIT_LIST_HEAD(&dd->hotplug_list);
+	}
+	return dd;
+}
+
+
+
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static void
+add_dock_dependent_device(struct dock_station *ds,
+			  struct dock_dependent_device *dd)
+{
+	list_add_tail(&dd->list, &ds->dependent_devices);
+}
+
+
+
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @handle: the acpi_handle of the device we want
+ *
+ * iterate over the dependent device list for this dock.  If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (handle == dd->handle)
+			return dd;
+	}
+	return NULL;
+}
+
+
+
+/*****************************************************************************
+ *                         Dock functions                                    *
+ *****************************************************************************/
+/**
+ * is_dock - see if a device is a dock station
+ * @handle: acpi handle of the device
+ *
+ * If an acpi object has a _DCK method, then it is by definition a dock
+ * station, so return true.
+ */
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	return 1;
+}
+
+
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @handle: acpi handle of the device
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(acpi_handle handle)
+{
+	if (is_dock(handle) || find_dock_dependent_device(ds, handle))
+		return 1;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+
+
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method.  note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+	unsigned long sta;
+	acpi_status status;
+
+	if (ds) {
+		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+		if (ACPI_SUCCESS(status) && sta)
+			return 1;
+	}
+	return 0;
+}
+
+
+
+
+/**
+ * hotplug_devices - insert or remove devices on the dock station
+ * @ds: the dock station
+ * @event: either bus check or eject request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_devices(struct dock_station *ds, u32 event)
+{
+	struct dock_dependent_device *dd;
+
+	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+		if (dd->handler)
+			dd->handler(dd->handle, event, dd->context);
+	}
+}
+
+
+
+
+/**
+ * eject_dock - respond to a dock eject request
+ * @ds: the dock station
+ *
+ * This is called after _DCK is called, to execute the dock station's
+ * _EJ0 method.
+ */
+static void eject_dock(struct dock_station *ds)
+{
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &buffer);
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+
+	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
+					&arg_list, NULL)) || dock_present(ds))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to eject dock!\n",
+			buffer.pointer));
+
+	acpi_os_free(buffer.pointer);
+}
+
+
+
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static acpi_status handle_dock(struct dock_station *ds, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
+
+	ACPI_FUNCTION_TRACE(__FUNCTION__);
+
+	printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = dock;
+	status = acpi_evaluate_object(ds->handle, "_DCK",
+					&arg_list, &buffer);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to execute _DCK\n",
+			name_buffer.pointer));
+	acpi_os_free(buffer.pointer);
+	acpi_os_free(name_buffer.pointer);
+
+	return_VALUE(status);
+}
+
+
+
+static inline void dock(struct dock_station *ds)
+{
+	handle_dock(ds, 1);
+}
+
+
+static inline void undock(struct dock_station *ds)
+{
+	handle_dock(ds, 0);
+}
+
+
+static inline void begin_dock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_DOCKING;
+}
+
+
+static inline void complete_dock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_DOCKING);
+	ds->last_dock_time = jiffies;
+}
+
+
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason.  Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+	if (ds->flags & DOCK_DOCKING ||
+		(jiffies < (ds->last_dock_time + 10))) {
+		return 1;
+	}
+	return 0;
+}
+
+
+
+/**
+ * register_dock_notifier - add yourself to the dock notifier list
+ * @nb: the callers notifier block
+ *
+ * If a driver wishes to be notified about dock events, they can
+ * use this function to put a notifier block on the dock notifier list.
+ * this notifier call chain will be called after a dock event, but
+ * before hotplugging any new devices.
+ */
+int register_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&dock_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_dock_notifier);
+
+
+
+
+/**
+ * unregister_dock_notifier - remove yourself from the dock notifier list
+ * @nb: the callers notifier block
+ */
+int unregister_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&dock_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_dock_notifier);
+
+
+
+/**
+ * register_hotplug_dock_device - register a hotplug function
+ * @handle: the handle of the device
+ * @handler: the acpi_notifier_handler to call after docking
+ * @context: device specific data
+ *
+ * If a driver would like to perform a hotplug operation after a dock
+ * event, they can register an acpi_notifiy_handler to be called by
+ * the dock driver after _DCK is executed.
+ */
+acpi_status
+register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
+		void *context)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_dependent_device *dd;
+
+	if (!ds)
+		return AE_ERROR;
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * or the dock station itself.
+	 */
+	if (is_dock(handle)) {
+		dd = alloc_dock_dependent_device(handle);
+		dd->handler = handler;
+		dd->context = context;
+		list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+		add_dock_dependent_device(ds, dd);
+		return AE_OK;
+	}
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+	if (is_dock(tmp)) {
+		dd = find_dock_dependent_device(ds, handle);
+		if (dd) {
+			dd->handler = handler;
+			dd->context = context;
+			list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+		}
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
+
+
+
+/**
+ * unregister_hotplug_dock_device - remove yourself from the hotplug list
+ * @handle: the acpi handle of the device
+ */
+acpi_status unregister_hotplug_dock_device(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_dependent_device *dd;
+
+	if (!ds)
+		return AE_ERROR;
+
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * or the dock station itself.
+	 */
+	if (is_dock(handle)) {
+		tmp = handle;
+		status = AE_OK;
+	} else {
+		status = acpi_bus_get_ejd(handle, &tmp);
+		if (ACPI_FAILURE(status))
+			return status;
+	}
+
+	if (is_dock(tmp)) {
+		dd = find_dock_dependent_device(ds, handle);
+		if (dd) {
+			list_del(&dd->hotplug_list);
+			kfree(dd);
+		}
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
+
+
+
+
+/**
+ * acpi_dock_notify - act upon an acpi dock notification
+ * @handle: the dock station handle
+ * @event: the acpi event
+ * @data: our driver data struct
+ *
+ * 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.
+ */
+static void acpi_dock_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct dock_station *ds = (struct dock_station *)data;
+	struct acpi_device *device;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_notify");
+
+	switch (event) {
+		case ACPI_NOTIFY_BUS_CHECK:
+			if (!dock_in_progress(ds) && dock_present(ds)) {
+				begin_dock(ds);
+				dock(ds);
+				if (!dock_present(ds)) {
+					printk(KERN_ERR PREFIX "Unable to dock!\n");
+					break;
+				}
+				atomic_notifier_call_chain(&dock_notifier_list,
+					 event, NULL);
+				hotplug_devices(ds, event);
+				complete_dock(ds);
+				if (acpi_bus_get_device(ds->handle, &device))
+					acpi_bus_generate_event(device,
+						event, 0);
+			}
+			break;
+		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.
+				 */
+				if (acpi_bus_get_device(ds->handle, &device))
+					acpi_bus_generate_event(device,
+						event, 0);
+
+				hotplug_devices(ds, event);
+				undock(ds);
+				eject_dock(ds);
+				if (dock_present(ds))
+					printk(KERN_ERR PREFIX "Unable to undock!\n");
+			}
+			break;
+	}
+	return_VOID;
+}
+
+
+
+
+/**
+ * find_dock_devices - find devices on the dock station
+ * @handle: the handle of the device we are examining
+ * @lvl: unused
+ * @context: the dock station private data
+ * @rv: unused
+ *
+ * This function is called by acpi_walk_namespace.  It will
+ * check to see if an object has an _EJD method.  If it does, then it
+ * will see if it is dependent on the dock station.
+ */
+static acpi_status
+find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_station *ds = (struct dock_station *) context;
+	struct dock_dependent_device *dd;
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return AE_OK;
+
+	if (tmp == ds->handle) {
+		dd = alloc_dock_dependent_device(handle);
+		add_dock_dependent_device(ds, dd);
+	}
+
+	return AE_OK;
+}
+
+
+
+/**
+ * acpi_dock_add - add a new dock station
+ * @handle: the dock station handle
+ *
+ * allocated and initialize a new dock station device.  Find all devices
+ * that are on the dock station, and register for dock event notifications.
+ */
+static int acpi_dock_add(acpi_handle handle)
+{
+	int ret;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_add");
+
+	/* allocate & initialize the dock_station private data */
+	ds = kzalloc(sizeof(*ds), GFP_KERNEL);
+	if (!ds)
+		return_VALUE(-ENOMEM);
+	ds->handle = handle;
+	INIT_LIST_HEAD(&ds->dependent_devices);
+	INIT_LIST_HEAD(&ds->hotplug_devices);
+
+	/* Find dependent devices */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock_devices, ds, NULL);
+
+	/* register for dock events */
+	status = acpi_install_notify_handler(ds->handle, ACPI_SYSTEM_NOTIFY,
+				acpi_dock_notify, ds);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Error installing notify handler\n"));
+		ret = -ENODEV;
+		goto dock_add_err;
+	}
+
+	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
+
+	return_VALUE(0);
+dock_add_err:
+	kfree(ds);
+	return_VALUE(ret);
+}
+
+
+
+/**
+ * acpi_dock_remove - free up resources related to the dock station
+ */
+static int acpi_dock_remove(void)
+{
+	struct dock_dependent_device *dd, *tmp;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_remove");
+
+	if (!ds)
+		return_VALUE(0);
+
+	/* remove dependent devices */
+	list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
+			list)
+		kfree(dd);
+
+	/* remove hotplug devices */
+	list_for_each_entry_safe(dd, tmp, &ds->hotplug_devices,
+			list)
+		kfree(dd);
+
+	/* remove dock notify handler */
+	status = acpi_remove_notify_handler(ds->handle,
+			ACPI_SYSTEM_NOTIFY, acpi_dock_notify);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Error removing notify handler\n"));
+
+	/* free dock station memory */
+	kfree(ds);
+	return_VALUE(0);
+}
+
+
+
+
+/**
+ * find_dock - look for a dock station
+ * @handle: acpi handle of a device
+ * @lvl: unused
+ * @context: counter of dock stations found
+ * @rv: unused
+ *
+ * This is called by acpi_walk_namespace to look for dock stations.
+ */
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+	acpi_status status = AE_OK;
+
+	if (is_dock(handle)) {
+		if (acpi_dock_add(handle) >= 0) {
+			(*count)++;
+			status = AE_CTRL_TERMINATE;
+		}
+	}
+	return status;
+}
+
+
+
+static int __init acpi_dock_init(void)
+{
+	int num = 0;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_init");
+
+	ds = NULL;
+
+	/* look for a dock station */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	if (!num)
+		return_VALUE(-ENODEV);
+
+	return_VALUE(0);
+}
+
+static void __exit acpi_dock_exit(void)
+{
+	ACPI_FUNCTION_TRACE("acpi_dock_exit");
+	acpi_dock_remove();
+	return_VOID;
+}
+
+module_init(acpi_dock_init);
+module_exit(acpi_dock_exit);
--- 2.6-git-kca2.orig/drivers/acpi/Kconfig
+++ 2.6-git-kca2/drivers/acpi/Kconfig
@@ -134,6 +134,12 @@ config ACPI_FAN
 	  This driver adds support for ACPI fan devices, allowing user-mode 
 	  applications to perform basic fan control (on, off, status).
 
+config ACPI_DOCK
+	tristate "Dock"
+	default y
+	help
+	  This driver adds support for ACPI controlled docking stations
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	default y
--- 2.6-git-kca2.orig/drivers/acpi/Makefile
+++ 2.6-git-kca2/drivers/acpi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
+obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o 
 obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
--- 2.6-git-kca2.orig/drivers/acpi/scan.c
+++ 2.6-git-kca2/drivers/acpi/scan.c
@@ -703,6 +703,29 @@ static int acpi_bus_find_driver(struct a
                                  Device Enumeration
    -------------------------------------------------------------------------- */
 
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		obj = buffer.pointer;
+		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+		acpi_os_free(buffer.pointer);
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+
 static int acpi_bus_get_flags(struct acpi_device *device)
 {
 	acpi_status status = AE_OK;
--- 2.6-git-kca2.orig/include/acpi/acpi_bus.h
+++ 2.6-git-kca2/include/acpi/acpi_bus.h
@@ -332,7 +332,7 @@ int acpi_bus_add(struct acpi_device **ch
 		 acpi_handle handle, int type);
 int acpi_bus_trim(struct acpi_device *start, int rmdevice);
 int acpi_bus_start(struct acpi_device *device);
-
+acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd);
 int acpi_match_ids(struct acpi_device *device, char *ids);
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
--- 2.6-git-kca2.orig/include/acpi/acpi_drivers.h
+++ 2.6-git-kca2/include/acpi/acpi_drivers.h
@@ -110,4 +110,13 @@ int acpi_processor_set_thermal_limit(acp
 
 extern int acpi_specific_hotkey_enabled;
 
+/*--------------------------------------------------------------------------
+                                  Dock Station
+  -------------------------------------------------------------------------- */
+extern int is_dock_device(acpi_handle handle);
+extern int register_dock_notifier(struct notifier_block *nb);
+extern int unregister_dock_notifier(struct notifier_block *nb);
+extern acpi_status register_hotplug_dock_device(acpi_handle handle,
+	acpi_notify_handler handler, void *context);
+extern acpi_status unregister_hotplug_dock_device(acpi_handle handle);
 #endif /*__ACPI_DRIVERS_H__*/

--

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [patch 2/3] acpiphp: use new dock driver
       [not found] <20060412221027.472109000@intel.com>
  2006-04-12 22:18 ` [patch 1/3] acpi: dock driver Kristen Accardi
@ 2006-04-12 22:18 ` Kristen Accardi
  2006-04-12 23:16   ` Christian Trefzer
  2006-04-28 23:55   ` [patch 2/3] acpiphp: use new dock driver v2 Kristen Accardi
  2006-04-12 22:18 ` [patch 3/3] acpiphp: prevent duplicate slot numbers when no _SUN Kristen Accardi
  2 siblings, 2 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-12 22:18 UTC (permalink / raw)
  To: len.brown, greg
  Cc: linux-acpi, pcihpd-discuss, linux-kernel, mochel, arjan,
	muneda.takahiro, pavel, temnota, Kristen Carlson Accardi

Modify the acpiphp driver to use the acpi dock driver for dock notifications.
Only load the acpiphp driver if we find we have pci dock devices

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---
 drivers/pci/hotplug/acpiphp_dock.c |  438 -------------------------------------
 drivers/pci/hotplug/Makefile       |    3 
 drivers/pci/hotplug/acpiphp.h      |   35 --
 drivers/pci/hotplug/acpiphp_core.c |   19 -
 drivers/pci/hotplug/acpiphp_glue.c |  110 +++++++--
 5 files changed, 86 insertions(+), 519 deletions(-)

--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp_glue.c
@@ -116,6 +116,59 @@ is_ejectable_slot(acpi_handle handle, u3
 	}
 }
 
+/* callback routine to check for the existance of a pci dock device */
+static acpi_status
+is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+
+	if (is_dock_device(handle)) {
+		(*count)++;
+		return AE_CTRL_TERMINATE;
+	} else {
+		return AE_OK;
+	}
+}
+
+
+
+
+/*
+ * the _DCK method can do funny things... and sometimes not
+ * hah-hah funny.
+ *
+ * TBD - figure out a way to only call fixups for
+ * systems that require them.
+ */
+static int post_dock_fixups(struct notifier_block *nb, unsigned long val,
+	void *v)
+{
+	struct acpiphp_func *func = container_of(nb, struct acpiphp_func, nb);
+	struct pci_bus *bus = func->slot->bridge->pci_bus;
+	u32 buses;
+
+	if (!bus->self)
+		return  NOTIFY_OK;
+
+	/* fixup bad _DCK function that rewrites
+	 * secondary bridge on slot
+	 */
+	pci_read_config_dword(bus->self,
+			PCI_PRIMARY_BUS,
+			&buses);
+
+	if (((buses >> 8) & 0xff) != bus->secondary) {
+		buses = (buses & 0xff000000)
+	     		| ((unsigned int)(bus->primary)     <<  0)
+	     		| ((unsigned int)(bus->secondary)   <<  8)
+	     		| ((unsigned int)(bus->subordinate) << 16);
+		pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
+	}
+	return NOTIFY_OK;
+}
+
+
+
 
 /* callback routine to register each ACPI PCI slot object */
 static acpi_status
@@ -124,7 +177,6 @@ register_slot(acpi_handle handle, u32 lv
 	struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context;
 	struct acpiphp_slot *slot;
 	struct acpiphp_func *newfunc;
-	struct dependent_device *dd;
 	acpi_handle tmp;
 	acpi_status status = AE_OK;
 	unsigned long adr, sun;
@@ -137,7 +189,7 @@ register_slot(acpi_handle handle, u32 lv
 
 	status = acpi_get_handle(handle, "_EJ0", &tmp);
 
-	if (ACPI_FAILURE(status) && !(is_dependent_device(handle)))
+	if (ACPI_FAILURE(status) && !(is_dock_device(handle)))
 		return AE_OK;
 
 	device = (adr >> 16) & 0xffff;
@@ -162,18 +214,8 @@ register_slot(acpi_handle handle, u32 lv
 	if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp)))
 		newfunc->flags |= FUNC_HAS_PS3;
 
-	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) {
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp)))
 		newfunc->flags |= FUNC_HAS_DCK;
-		/* add to devices dependent on dock station,
-		 * because this may actually be the dock bridge
-		 */
-		dd = alloc_dependent_device(handle);
-                if (!dd)
-                        err("Can't allocate memory for "
-				"new dependent device!\n");
-		else
-			add_dependent_device(dd);
-	}
 
 	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
 	if (ACPI_FAILURE(status))
@@ -225,20 +267,22 @@ register_slot(acpi_handle handle, u32 lv
 		slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
 	}
 
-	/* if this is a device dependent on a dock station,
-	 * associate the acpiphp_func to the dependent_device
- 	 * struct.
-	 */
-	if ((dd = get_dependent_device(handle))) {
-		newfunc->flags |= FUNC_IS_DD;
-		/*
-		 * we don't want any devices which is dependent
-		 * on the dock to have it's _EJ0 method executed.
-		 * because we need to run _DCK first.
+	if (is_dock_device(handle)) {
+		/* we don't want to call this device's _EJ0
+		 * because we want the dock notify handler
+		 * to call it after it calls _DCK
 		 */
 		newfunc->flags &= ~FUNC_HAS_EJ0;
-		dd->func = newfunc;
-		add_pci_dependent_device(dd);
+		status = register_hotplug_dock_device(handle, handle_hotplug_event_func,
+			newfunc);
+		if (ACPI_FAILURE(status))
+			dbg("failed to register dock device\n");
+		/* we need to be notified when dock events happen
+		 * outside of the hotplug operation, since we may
+		 * need to do fixups before we can hotplug.
+		 */
+		newfunc->nb.notifier_call = post_dock_fixups;
+		register_dock_notifier(&newfunc->nb);
 	}
 
 	/* install notify handler */
@@ -277,6 +321,15 @@ static int detect_ejectable_slots(acpi_h
 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1,
 				     is_ejectable_slot, (void *)&count, NULL);
 
+	/*
+	 * we also need to add this bridge if there is a dock bridge or
+	 * other pci device on a dock station (removable)
+	 */
+	if (!count)
+		status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
+				(u32)1, is_pci_dock_device, (void *)&count,
+				NULL);
+
 	return count;
 }
 
@@ -413,8 +466,7 @@ find_p2p_bridge(acpi_handle handle, u32 
 		goto out;
 
 	/* check if this bridge has ejectable slots */
-	if ((detect_ejectable_slots(handle) > 0) ||
-		(detect_dependent_devices(handle) > 0)) {
+	if ((detect_ejectable_slots(handle) > 0)) {
 		dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
 		add_p2p_bridge(handle, dev);
 	}
@@ -522,6 +574,10 @@ static void cleanup_bridge(struct acpiph
 		list_for_each_safe (list, tmp, &slot->funcs) {
 			struct acpiphp_func *func;
 			func = list_entry(list, struct acpiphp_func, sibling);
+			if (is_dock_device(func->handle)) {
+				unregister_hotplug_dock_device(func->handle);
+				unregister_dock_notifier(&func->nb);
+			}
 			if (!(func->flags & FUNC_HAS_DCK)) {
 				status = acpi_remove_notify_handler(func->handle,
 						ACPI_SYSTEM_NOTIFY,
--- 2.6-git-kca2.orig/drivers/pci/hotplug/Makefile
+++ 2.6-git-kca2/drivers/pci/hotplug/Makefile
@@ -40,8 +40,7 @@ ibmphp-objs		:=	ibmphp_core.o	\
 				ibmphp_hpc.o
 
 acpiphp-objs		:=	acpiphp_core.o	\
-				acpiphp_glue.o  \
-				acpiphp_dock.o
+				acpiphp_glue.o
 
 rpaphp-objs		:=	rpaphp_core.o	\
 				rpaphp_pci.o	\
--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp.h
+++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp.h
@@ -125,7 +125,7 @@ struct acpiphp_func {
 
 	struct list_head sibling;
 	struct pci_dev *pci_dev;
-
+	struct notifier_block nb;
 	acpi_handle	handle;
 
 	u8		function;	/* pci function# */
@@ -146,24 +146,6 @@ struct acpiphp_attention_info
 };
 

-struct dependent_device {
-	struct list_head device_list;
-	struct list_head pci_list;
-	acpi_handle handle;
-	struct acpiphp_func *func;
-};
-
-
-struct acpiphp_dock_station {
-	acpi_handle handle;
-	u32 last_dock_time;
-	u32 flags;
-	struct acpiphp_func *dock_bridge;
-	struct list_head dependent_devices;
-	struct list_head pci_dependent_devices;
-};
-
-
 /* PCI bus bridge HID */
 #define ACPI_PCI_HOST_HID		"PNP0A03"
 
@@ -202,11 +184,6 @@ struct acpiphp_dock_station {
 #define FUNC_HAS_PS2		(0x00000040)
 #define FUNC_HAS_PS3		(0x00000080)
 #define FUNC_HAS_DCK            (0x00000100)
-#define FUNC_IS_DD              (0x00000200)
-
-/* dock station flags */
-#define DOCK_DOCKING            (0x00000001)
-#define DOCK_HAS_BRIDGE         (0x00000002)
 
 /* function prototypes */
 
@@ -231,16 +208,6 @@ extern u8 acpiphp_get_latch_status (stru
 extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
 extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
 
-/* acpiphp_dock.c */
-extern int find_dock_station(void);
-extern void remove_dock_station(void);
-extern void add_dependent_device(struct dependent_device *new_dd);
-extern void add_pci_dependent_device(struct dependent_device *new_dd);
-extern struct dependent_device *get_dependent_device(acpi_handle handle);
-extern int is_dependent_device(acpi_handle handle);
-extern int detect_dependent_devices(acpi_handle *bridge_handle);
-extern struct dependent_device *alloc_dependent_device(acpi_handle handle);
-
 /* variables */
 extern int acpiphp_debug;
 
--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_core.c
+++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp_core.c
@@ -416,27 +416,12 @@ void acpiphp_unregister_hotplug_slot(str
 
 static int __init acpiphp_init(void)
 {
-	int retval;
-	int docking_station;
-
 	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
 
 	acpiphp_debug = debug;
 
-	docking_station = find_dock_station();
-
 	/* read all the ACPI info from the system */
-	retval = init_acpi();
-
-	/* if we have found a docking station, we should
-	 * go ahead and load even if init_acpi has found
-	 * no slots.  This handles the case when the _DCK
-	 * method not defined under the actual dock bridge
-	 */
-	if (docking_station)
-		return 0;
-	else
-		return retval;
+	return init_acpi();
 }
 

@@ -444,8 +429,6 @@ static void __exit acpiphp_exit(void)
 {
 	/* deallocate internal data structures etc. */
 	acpiphp_glue_exit();
-
-	remove_dock_station();
 }
 
 module_init(acpiphp_init);
--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_dock.c
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * ACPI PCI HotPlug dock functions to ACPI CA subsystem
- *
- * Copyright (C) 2006 Kristen Carlson Accardi (kristen.c.accardi@intel.com)
- * Copyright (C) 2006 Intel Corporation
- *
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Send feedback to <kristen.c.accardi@intel.com>
- *
- */
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/smp_lock.h>
-#include <linux/mutex.h>
-
-#include "../pci.h"
-#include "pci_hotplug.h"
-#include "acpiphp.h"
-
-static struct acpiphp_dock_station *ds;
-#define MY_NAME "acpiphp_dock"
-
-
-int is_dependent_device(acpi_handle handle)
-{
-	return (get_dependent_device(handle) ? 1 : 0);
-}
-
-
-static acpi_status
-find_dependent_device(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	int *count = (int *)context;
-
-	if (is_dependent_device(handle)) {
-		(*count)++;
-		return AE_CTRL_TERMINATE;
-	} else {
-		return AE_OK;
-	}
-}
-
-
-
-
-void add_dependent_device(struct dependent_device *new_dd)
-{
-	list_add_tail(&new_dd->device_list, &ds->dependent_devices);
-}
-
-
-void add_pci_dependent_device(struct dependent_device *new_dd)
-{
-	list_add_tail(&new_dd->pci_list, &ds->pci_dependent_devices);
-}
-
-
-
-struct dependent_device * get_dependent_device(acpi_handle handle)
-{
-	struct dependent_device *dd;
-
-	if (!ds)
-		return NULL;
-
-	list_for_each_entry(dd, &ds->dependent_devices, device_list) {
-		if (handle == dd->handle)
-			return dd;
-	}
-	return NULL;
-}
-
-
-
-struct dependent_device *alloc_dependent_device(acpi_handle handle)
-{
-	struct dependent_device *dd;
-
-	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
-	if (dd) {
-		INIT_LIST_HEAD(&dd->pci_list);
-		INIT_LIST_HEAD(&dd->device_list);
-		dd->handle = handle;
-	}
-	return dd;
-}
-
-
-
-static int is_dock(acpi_handle handle)
-{
-	acpi_status status;
-	acpi_handle tmp;
-
-	status = acpi_get_handle(handle, "_DCK", &tmp);
-	if (ACPI_FAILURE(status)) {
-		return 0;
-	}
-	return 1;
-}
-
-
-
-static int dock_present(void)
-{
-	unsigned long sta;
-	acpi_status status;
-
-	if (ds) {
-		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
-		if (ACPI_SUCCESS(status) && sta)
-			return 1;
-	}
-	return 0;
-}
-
-
-
-static void eject_dock(void)
-{
-	struct acpi_object_list arg_list;
-	union acpi_object arg;
-
-	arg_list.count = 1;
-	arg_list.pointer = &arg;
-	arg.type = ACPI_TYPE_INTEGER;
-	arg.integer.value = 1;
-
-	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
-					&arg_list, NULL)) || dock_present())
-		warn("%s: failed to eject dock!\n", __FUNCTION__);
-
-	return;
-}
-
-
-
-
-static acpi_status handle_dock(int dock)
-{
-	acpi_status status;
-	struct acpi_object_list arg_list;
-	union acpi_object arg;
-	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-
-	dbg("%s: %s\n", __FUNCTION__, dock ? "docking" : "undocking");
-
-	/* _DCK method has one argument */
-	arg_list.count = 1;
-	arg_list.pointer = &arg;
-	arg.type = ACPI_TYPE_INTEGER;
-	arg.integer.value = dock;
-	status = acpi_evaluate_object(ds->handle, "_DCK",
-					&arg_list, &buffer);
-	if (ACPI_FAILURE(status))
-		err("%s: failed to execute _DCK\n", __FUNCTION__);
-	acpi_os_free(buffer.pointer);
-
-	return status;
-}
-
-
-
-static inline void dock(void)
-{
-	handle_dock(1);
-}
-
-
-
-static inline void undock(void)
-{
-	handle_dock(0);
-}
-
-
-
-/*
- * the _DCK method can do funny things... and sometimes not
- * hah-hah funny.
- *
- * TBD - figure out a way to only call fixups for
- * systems that require them.
- */
-static void post_dock_fixups(void)
-{
-	struct pci_bus *bus;
-	u32 buses;
-	struct dependent_device *dd;
-
-	list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list) {
-		bus = dd->func->slot->bridge->pci_bus;
-
-		/* fixup bad _DCK function that rewrites
-	 	 * secondary bridge on slot
-	 	 */
-		pci_read_config_dword(bus->self,
-				PCI_PRIMARY_BUS,
-				&buses);
-
-		if (((buses >> 8) & 0xff) != bus->secondary) {
-			buses = (buses & 0xff000000)
-	     			| ((unsigned int)(bus->primary)     <<  0)
-	     			| ((unsigned int)(bus->secondary)   <<  8)
-	     			| ((unsigned int)(bus->subordinate) << 16);
-			pci_write_config_dword(bus->self,
-					PCI_PRIMARY_BUS,
-					buses);
-		}
-	}
-}
-
-
-
-static void hotplug_pci(u32 type)
-{
-	struct dependent_device *dd;
-
-	list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list)
-		handle_hotplug_event_func(dd->handle, type, dd->func);
-}
-
-
-
-static inline void begin_dock(void)
-{
-	ds->flags |= DOCK_DOCKING;
-}
-
-
-static inline void complete_dock(void)
-{
-	ds->flags &= ~(DOCK_DOCKING);
-	ds->last_dock_time = jiffies;
-}
-
-
-static int dock_in_progress(void)
-{
-	if (ds->flags & DOCK_DOCKING ||
-		ds->last_dock_time == jiffies) {
-		dbg("dock in progress\n");
-		return 1;
-	}
-	return 0;
-}
-
-
-
-static void
-handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
-{
-	dbg("%s: enter\n", __FUNCTION__);
-
-	switch (type) {
-		case ACPI_NOTIFY_BUS_CHECK:
-			dbg("BUS Check\n");
-			if (!dock_in_progress() && dock_present()) {
-				begin_dock();
-				dock();
-				if (!dock_present()) {
-					err("Unable to dock!\n");
-					break;
-				}
-				post_dock_fixups();
-				hotplug_pci(type);
-				complete_dock();
-			}
-			break;
-		case ACPI_NOTIFY_EJECT_REQUEST:
-			dbg("EJECT request\n");
-			if (!dock_in_progress() && dock_present()) {
-				hotplug_pci(type);
-				undock();
-				eject_dock();
-				if (dock_present())
-					err("Unable to undock!\n");
-			}
-			break;
-	}
-}
-
-
-
-
-static acpi_status
-find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	acpi_status status;
-	acpi_handle tmp;
-	acpi_handle dck_handle = (acpi_handle) context;
-	char objname[64];
-	struct acpi_buffer buffer = { .length = sizeof(objname),
-				      .pointer = objname };
-	struct acpi_buffer ejd_buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-	union acpi_object *ejd_obj;
-
-	status = acpi_get_handle(handle, "_EJD", &tmp);
-	if (ACPI_FAILURE(status))
-		return AE_OK;
-
-	/* make sure we are dependent on the dock device,
-	 * by executing the _EJD method, then getting a handle
-	 * to the device referenced by that name.  If that
-	 * device handle is the same handle as the dock station
-	 * handle, then we are a device dependent on the dock station
-	 */
-	acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);
-	status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);
-	if (ACPI_FAILURE(status)) {
-		err("Unable to execute _EJD!\n");
-		goto find_ejd_out;
-	}
-	ejd_obj = ejd_buffer.pointer;
-	status = acpi_get_handle(NULL, ejd_obj->string.pointer, &tmp);
-	if (ACPI_FAILURE(status))
-		goto find_ejd_out;
-
-	if (tmp == dck_handle) {
-		struct dependent_device *dd;
-		dbg("%s: found device dependent on dock\n", __FUNCTION__);
-		dd = alloc_dependent_device(handle);
-		if (!dd) {
-			err("Can't allocate memory for dependent device!\n");
-			goto find_ejd_out;
-		}
-		add_dependent_device(dd);
-	}
-
-find_ejd_out:
-	acpi_os_free(ejd_buffer.pointer);
-	return AE_OK;
-}
-
-
-
-int detect_dependent_devices(acpi_handle *bridge_handle)
-{
-	acpi_status status;
-	int count;
-
-	count = 0;
-
-	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
-					(u32)1, find_dependent_device,
-					(void *)&count, NULL);
-
-	return count;
-}
-
-
-
-
-
-static acpi_status
-find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	int *count = (int *)context;
-
-	if (is_dock(handle)) {
-		dbg("%s: found dock\n", __FUNCTION__);
-		ds = kzalloc(sizeof(*ds), GFP_KERNEL);
-		ds->handle = handle;
-		INIT_LIST_HEAD(&ds->dependent_devices);
-		INIT_LIST_HEAD(&ds->pci_dependent_devices);
-
-		/* look for devices dependent on dock station */
-		acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-			ACPI_UINT32_MAX, find_dock_ejd, handle, NULL);
-
-		acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
-			handle_hotplug_event_dock, ds);
-		(*count)++;
-	}
-
-	return AE_OK;
-}
-
-
-
-
-int find_dock_station(void)
-{
-	int num = 0;
-
-	ds = NULL;
-
-	/* start from the root object, because some laptops define
-	 * _DCK methods outside the scope of PCI (IBM x-series laptop)
-	 */
-	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-			ACPI_UINT32_MAX, find_dock, &num, NULL);
-
-	return num;
-}
-
-
-
-void remove_dock_station(void)
-{
-	struct dependent_device *dd, *tmp;
-	if (ds) {
-		if (ACPI_FAILURE(acpi_remove_notify_handler(ds->handle,
-			ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))
-			err("failed to remove dock notify handler\n");
-
-		/* free all dependent devices */
-		list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
-				device_list)
-			kfree(dd);
-
-		/* no need to touch the pci_dependent_device list,
-		 * cause all memory was freed above
-		 */
-		kfree(ds);
-	}
-}
-
-

--

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [patch 3/3] acpiphp: prevent duplicate slot numbers when no _SUN
       [not found] <20060412221027.472109000@intel.com>
  2006-04-12 22:18 ` [patch 1/3] acpi: dock driver Kristen Accardi
  2006-04-12 22:18 ` [patch 2/3] acpiphp: use new " Kristen Accardi
@ 2006-04-12 22:18 ` Kristen Accardi
  2006-04-13 12:36   ` MUNEDA Takahiro
  2 siblings, 1 reply; 24+ messages in thread
From: Kristen Accardi @ 2006-04-12 22:18 UTC (permalink / raw)
  To: len.brown, greg
  Cc: linux-acpi, pcihpd-discuss, linux-kernel, mochel, arjan,
	muneda.takahiro, pavel, temnota, Kristen Carlson Accardi

Dock bridges generally do not implement _SUN, yet show up as ejectable slots.
If you have more than one ejectable slot that does not implement SUN, with the
current code you will get duplicate slot numbers.  So, if there is no _SUN,
use the current count of the number of slots found instead.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---
 drivers/pci/hotplug/acpiphp_glue.c |    9 +++++++--
 1 files changed, 7 insertions(+), 2 deletions(-)

--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp_glue.c
@@ -218,8 +218,13 @@ register_slot(acpi_handle handle, u32 lv
 		newfunc->flags |= FUNC_HAS_DCK;
 
 	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
-	if (ACPI_FAILURE(status))
-		sun = -1;
+	if (ACPI_FAILURE(status)) {
+		/*
+		 * use the count of the number of slots we've found
+		 * for the number of the slot
+		 */
+		sun = bridge->nr_slots+1;
+	}
 
 	/* search for objects that share the same slot */
 	for (slot = bridge->slots; slot; slot = slot->next)

--

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver (refreshed)
  2006-04-12 22:18 ` [patch 1/3] acpi: dock driver Kristen Accardi
@ 2006-04-12 22:35   ` Kristen Accardi
  2006-04-13  5:27   ` [patch 1/3] acpi: dock driver Andrew Morton
  1 sibling, 0 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-12 22:35 UTC (permalink / raw)
  To: len.brown
  Cc: greg, linux-acpi, pcihpd-discuss, linux-kernel, mochel, arjan,
	muneda.takahiro, pavel, temnota

Create a driver which lives in the acpi subsystem to handle dock events.  This 
driver is not an acpi driver, because acpi drivers require that the object
be present when the driver is loaded.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---
 drivers/acpi/Kconfig        |    6 
 drivers/acpi/Makefile       |    1 
 drivers/acpi/dock.c         |  688 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c         |   23 +
 include/acpi/acpi_bus.h     |    2 
 include/acpi/acpi_drivers.h |    9 
 6 files changed, 728 insertions(+), 1 deletion(-)

--- /dev/null
+++ 2.6-git-kca2/drivers/acpi/dock.c
@@ -0,0 +1,688 @@
+/*
+ *  dock.c - ACPI dock station driver
+ *
+ *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_DOCK_COMPONENT 0x10000000
+#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
+#define _COMPONENT		ACPI_DOCK_COMPONENT
+
+ACPI_MODULE_NAME("dock")
+MODULE_AUTHOR("Kristen Carlson Accardi");
+MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static struct atomic_notifier_head dock_notifier_list;
+
+struct dock_station {
+	acpi_handle handle;
+	u32 last_dock_time;
+	u32 flags;
+	struct list_head dependent_devices;
+	struct list_head hotplug_devices;
+};
+
+
+struct dock_dependent_device {
+	struct list_head list;
+	struct list_head hotplug_list;
+	acpi_handle handle;
+	acpi_notify_handler handler;
+	void *context;
+};
+
+#define DOCK_DOCKING	0x00000001
+
+static struct dock_station *ds;
+
+/*****************************************************************************
+ *                         Dock Dependent device functions                   *
+ *****************************************************************************/
+/**
+ *  alloc_dock_dependent_device - allocate and init a dependent device
+ *  @handle: the acpi_handle of the dependent device
+ *
+ *  Allocate memory for a dependent device structure for a device referenced
+ *  by the acpi handle
+ */
+static struct dock_dependent_device * alloc_dock_dependent_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd) {
+		dd->handle = handle;
+		INIT_LIST_HEAD(&dd->list);
+		INIT_LIST_HEAD(&dd->hotplug_list);
+	}
+	return dd;
+}
+
+
+
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static void
+add_dock_dependent_device(struct dock_station *ds,
+			  struct dock_dependent_device *dd)
+{
+	list_add_tail(&dd->list, &ds->dependent_devices);
+}
+
+
+
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @handle: the acpi_handle of the device we want
+ *
+ * iterate over the dependent device list for this dock.  If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (handle == dd->handle)
+			return dd;
+	}
+	return NULL;
+}
+
+
+
+/*****************************************************************************
+ *                         Dock functions                                    *
+ *****************************************************************************/
+/**
+ * is_dock - see if a device is a dock station
+ * @handle: acpi handle of the device
+ *
+ * If an acpi object has a _DCK method, then it is by definition a dock
+ * station, so return true.
+ */
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	return 1;
+}
+
+
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @handle: acpi handle of the device
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(acpi_handle handle)
+{
+	if (is_dock(handle) || find_dock_dependent_device(ds, handle))
+		return 1;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+
+
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method.  note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+	unsigned long sta;
+	acpi_status status;
+
+	if (ds) {
+		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+		if (ACPI_SUCCESS(status) && sta)
+			return 1;
+	}
+	return 0;
+}
+
+
+
+
+/**
+ * hotplug_devices - insert or remove devices on the dock station
+ * @ds: the dock station
+ * @event: either bus check or eject request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_devices(struct dock_station *ds, u32 event)
+{
+	struct dock_dependent_device *dd;
+
+	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+		if (dd->handler)
+			dd->handler(dd->handle, event, dd->context);
+	}
+}
+
+
+
+
+/**
+ * eject_dock - respond to a dock eject request
+ * @ds: the dock station
+ *
+ * This is called after _DCK is called, to execute the dock station's
+ * _EJ0 method.
+ */
+static void eject_dock(struct dock_station *ds)
+{
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+
+	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &buffer);
+	obj = buffer.pointer;
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+
+	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
+					&arg_list, NULL)) || dock_present(ds))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to eject dock!\n",
+			obj->string.pointer));
+
+	acpi_os_free(buffer.pointer);
+}
+
+
+
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static acpi_status handle_dock(struct dock_station *ds, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	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;
+
+	ACPI_FUNCTION_TRACE(__FUNCTION__);
+
+	printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = dock;
+	status = acpi_evaluate_object(ds->handle, "_DCK",
+					&arg_list, &buffer);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to execute _DCK\n",
+			obj->string.pointer));
+	acpi_os_free(buffer.pointer);
+	acpi_os_free(name_buffer.pointer);
+
+	return_VALUE(status);
+}
+
+
+
+static inline void dock(struct dock_station *ds)
+{
+	handle_dock(ds, 1);
+}
+
+
+static inline void undock(struct dock_station *ds)
+{
+	handle_dock(ds, 0);
+}
+
+
+static inline void begin_dock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_DOCKING;
+}
+
+
+static inline void complete_dock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_DOCKING);
+	ds->last_dock_time = jiffies;
+}
+
+
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason.  Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+	if (ds->flags & DOCK_DOCKING ||
+		(jiffies < (ds->last_dock_time + 10))) {
+		return 1;
+	}
+	return 0;
+}
+
+
+
+/**
+ * register_dock_notifier - add yourself to the dock notifier list
+ * @nb: the callers notifier block
+ *
+ * If a driver wishes to be notified about dock events, they can
+ * use this function to put a notifier block on the dock notifier list.
+ * this notifier call chain will be called after a dock event, but
+ * before hotplugging any new devices.
+ */
+int register_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&dock_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_dock_notifier);
+
+
+
+
+/**
+ * unregister_dock_notifier - remove yourself from the dock notifier list
+ * @nb: the callers notifier block
+ */
+int unregister_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&dock_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_dock_notifier);
+
+
+
+/**
+ * register_hotplug_dock_device - register a hotplug function
+ * @handle: the handle of the device
+ * @handler: the acpi_notifier_handler to call after docking
+ * @context: device specific data
+ *
+ * If a driver would like to perform a hotplug operation after a dock
+ * event, they can register an acpi_notifiy_handler to be called by
+ * the dock driver after _DCK is executed.
+ */
+acpi_status
+register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
+		void *context)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_dependent_device *dd;
+
+	if (!ds)
+		return AE_ERROR;
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * or the dock station itself.
+	 */
+	if (is_dock(handle)) {
+		dd = alloc_dock_dependent_device(handle);
+		dd->handler = handler;
+		dd->context = context;
+		list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+		add_dock_dependent_device(ds, dd);
+		return AE_OK;
+	}
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+	if (is_dock(tmp)) {
+		dd = find_dock_dependent_device(ds, handle);
+		if (dd) {
+			dd->handler = handler;
+			dd->context = context;
+			list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+		}
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
+
+
+
+/**
+ * unregister_hotplug_dock_device - remove yourself from the hotplug list
+ * @handle: the acpi handle of the device
+ */
+acpi_status unregister_hotplug_dock_device(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_dependent_device *dd;
+
+	if (!ds)
+		return AE_ERROR;
+
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * or the dock station itself.
+	 */
+	if (is_dock(handle)) {
+		tmp = handle;
+		status = AE_OK;
+	} else {
+		status = acpi_bus_get_ejd(handle, &tmp);
+		if (ACPI_FAILURE(status))
+			return status;
+	}
+
+	if (is_dock(tmp)) {
+		dd = find_dock_dependent_device(ds, handle);
+		if (dd) {
+			list_del(&dd->hotplug_list);
+			kfree(dd);
+		}
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
+
+
+
+
+/**
+ * acpi_dock_notify - act upon an acpi dock notification
+ * @handle: the dock station handle
+ * @event: the acpi event
+ * @data: our driver data struct
+ *
+ * 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.
+ */
+static void acpi_dock_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct dock_station *ds = (struct dock_station *)data;
+	struct acpi_device *device;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_notify");
+
+	switch (event) {
+		case ACPI_NOTIFY_BUS_CHECK:
+			if (!dock_in_progress(ds) && dock_present(ds)) {
+				begin_dock(ds);
+				dock(ds);
+				if (!dock_present(ds)) {
+					printk(KERN_ERR PREFIX "Unable to dock!\n");
+					break;
+				}
+				atomic_notifier_call_chain(&dock_notifier_list,
+					 event, NULL);
+				hotplug_devices(ds, event);
+				complete_dock(ds);
+				if (acpi_bus_get_device(ds->handle, &device))
+					acpi_bus_generate_event(device,
+						event, 0);
+			}
+			break;
+		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.
+				 */
+				if (acpi_bus_get_device(ds->handle, &device))
+					acpi_bus_generate_event(device,
+						event, 0);
+
+				hotplug_devices(ds, event);
+				undock(ds);
+				eject_dock(ds);
+				if (dock_present(ds))
+					printk(KERN_ERR PREFIX "Unable to undock!\n");
+			}
+			break;
+	}
+	return_VOID;
+}
+
+
+
+
+/**
+ * find_dock_devices - find devices on the dock station
+ * @handle: the handle of the device we are examining
+ * @lvl: unused
+ * @context: the dock station private data
+ * @rv: unused
+ *
+ * This function is called by acpi_walk_namespace.  It will
+ * check to see if an object has an _EJD method.  If it does, then it
+ * will see if it is dependent on the dock station.
+ */
+static acpi_status
+find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_station *ds = (struct dock_station *) context;
+	struct dock_dependent_device *dd;
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return AE_OK;
+
+	if (tmp == ds->handle) {
+		dd = alloc_dock_dependent_device(handle);
+		add_dock_dependent_device(ds, dd);
+	}
+
+	return AE_OK;
+}
+
+
+
+/**
+ * acpi_dock_add - add a new dock station
+ * @handle: the dock station handle
+ *
+ * allocated and initialize a new dock station device.  Find all devices
+ * that are on the dock station, and register for dock event notifications.
+ */
+static int acpi_dock_add(acpi_handle handle)
+{
+	int ret;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_add");
+
+	/* allocate & initialize the dock_station private data */
+	ds = kzalloc(sizeof(*ds), GFP_KERNEL);
+	if (!ds)
+		return_VALUE(-ENOMEM);
+	ds->handle = handle;
+	INIT_LIST_HEAD(&ds->dependent_devices);
+	INIT_LIST_HEAD(&ds->hotplug_devices);
+
+	/* Find dependent devices */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock_devices, ds, NULL);
+
+	/* register for dock events */
+	status = acpi_install_notify_handler(ds->handle, ACPI_SYSTEM_NOTIFY,
+				acpi_dock_notify, ds);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Error installing notify handler\n"));
+		ret = -ENODEV;
+		goto dock_add_err;
+	}
+
+	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
+
+	return_VALUE(0);
+dock_add_err:
+	kfree(ds);
+	return_VALUE(ret);
+}
+
+
+
+/**
+ * acpi_dock_remove - free up resources related to the dock station
+ */
+static int acpi_dock_remove(void)
+{
+	struct dock_dependent_device *dd, *tmp;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_remove");
+
+	if (!ds)
+		return_VALUE(0);
+
+	/* remove dependent devices */
+	list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
+			list)
+		kfree(dd);
+
+	/* remove hotplug devices */
+	list_for_each_entry_safe(dd, tmp, &ds->hotplug_devices,
+			list)
+		kfree(dd);
+
+	/* remove dock notify handler */
+	status = acpi_remove_notify_handler(ds->handle,
+			ACPI_SYSTEM_NOTIFY, acpi_dock_notify);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Error removing notify handler\n"));
+
+	/* free dock station memory */
+	kfree(ds);
+	return_VALUE(0);
+}
+
+
+
+
+/**
+ * find_dock - look for a dock station
+ * @handle: acpi handle of a device
+ * @lvl: unused
+ * @context: counter of dock stations found
+ * @rv: unused
+ *
+ * This is called by acpi_walk_namespace to look for dock stations.
+ */
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+	acpi_status status = AE_OK;
+
+	if (is_dock(handle)) {
+		if (acpi_dock_add(handle) >= 0) {
+			(*count)++;
+			status = AE_CTRL_TERMINATE;
+		}
+	}
+	return status;
+}
+
+
+
+static int __init acpi_dock_init(void)
+{
+	int num = 0;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_init");
+
+	ds = NULL;
+
+	/* look for a dock station */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	if (!num)
+		return_VALUE(-ENODEV);
+
+	return_VALUE(0);
+}
+
+static void __exit acpi_dock_exit(void)
+{
+	ACPI_FUNCTION_TRACE("acpi_dock_exit");
+	acpi_dock_remove();
+	return_VOID;
+}
+
+module_init(acpi_dock_init);
+module_exit(acpi_dock_exit);
--- 2.6-git-kca2.orig/drivers/acpi/Kconfig
+++ 2.6-git-kca2/drivers/acpi/Kconfig
@@ -134,6 +134,12 @@ config ACPI_FAN
 	  This driver adds support for ACPI fan devices, allowing user-mode 
 	  applications to perform basic fan control (on, off, status).
 
+config ACPI_DOCK
+	tristate "Dock"
+	default y
+	help
+	  This driver adds support for ACPI controlled docking stations
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	default y
--- 2.6-git-kca2.orig/drivers/acpi/Makefile
+++ 2.6-git-kca2/drivers/acpi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
+obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o 
 obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
--- 2.6-git-kca2.orig/drivers/acpi/scan.c
+++ 2.6-git-kca2/drivers/acpi/scan.c
@@ -703,6 +703,29 @@ static int acpi_bus_find_driver(struct a
                                  Device Enumeration
    -------------------------------------------------------------------------- */
 
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		obj = buffer.pointer;
+		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+		acpi_os_free(buffer.pointer);
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+
 static int acpi_bus_get_flags(struct acpi_device *device)
 {
 	acpi_status status = AE_OK;
--- 2.6-git-kca2.orig/include/acpi/acpi_bus.h
+++ 2.6-git-kca2/include/acpi/acpi_bus.h
@@ -332,7 +332,7 @@ int acpi_bus_add(struct acpi_device **ch
 		 acpi_handle handle, int type);
 int acpi_bus_trim(struct acpi_device *start, int rmdevice);
 int acpi_bus_start(struct acpi_device *device);
-
+acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd);
 int acpi_match_ids(struct acpi_device *device, char *ids);
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
--- 2.6-git-kca2.orig/include/acpi/acpi_drivers.h
+++ 2.6-git-kca2/include/acpi/acpi_drivers.h
@@ -110,4 +110,13 @@ int acpi_processor_set_thermal_limit(acp
 
 extern int acpi_specific_hotkey_enabled;
 
+/*--------------------------------------------------------------------------
+                                  Dock Station
+  -------------------------------------------------------------------------- */
+extern int is_dock_device(acpi_handle handle);
+extern int register_dock_notifier(struct notifier_block *nb);
+extern int unregister_dock_notifier(struct notifier_block *nb);
+extern acpi_status register_hotplug_dock_device(acpi_handle handle,
+	acpi_notify_handler handler, void *context);
+extern acpi_status unregister_hotplug_dock_device(acpi_handle handle);
 #endif /*__ACPI_DRIVERS_H__*/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 2/3] acpiphp: use new dock driver
  2006-04-12 22:18 ` [patch 2/3] acpiphp: use new " Kristen Accardi
@ 2006-04-12 23:16   ` Christian Trefzer
  2006-04-28 23:55   ` [patch 2/3] acpiphp: use new dock driver v2 Kristen Accardi
  1 sibling, 0 replies; 24+ messages in thread
From: Christian Trefzer @ 2006-04-12 23:16 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel,
	mochel, arjan, muneda.takahiro, pavel, temnota

[-- Attachment #1: Type: text/plain, Size: 460 bytes --]

Hi Kristen,

wanted to try this one out on my Dell Latitude CPiA and C/Dock II, but
something seems to have happened to the patch along the way:

Applying 'acpiphp: use new dock driver'

fatal: corrupt patch at line 206
Patch failed at 0002.

I tried looking at the patch itself and found nothing obvious. Also,
patching manually resulted in the same behaviour. Is this just me?

Sorry for the noise, but I'm dying to try this one out : )

Kind regards,
Chris

[-- Attachment #2: Type: application/pgp-signature, Size: 829 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-12 22:18 ` [patch 1/3] acpi: dock driver Kristen Accardi
  2006-04-12 22:35   ` [patch 1/3] acpi: dock driver (refreshed) Kristen Accardi
@ 2006-04-13  5:27   ` Andrew Morton
  2006-04-14 22:02     ` Kristen Accardi
  2006-04-14 22:49     ` Kristen Accardi
  1 sibling, 2 replies; 24+ messages in thread
From: Andrew Morton @ 2006-04-13  5:27 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel,
	mochel, arjan, muneda.takahiro, pavel, temnota,
	kristen.c.accardi

Kristen Accardi <kristen.c.accardi@intel.com> wrote:
>
> Create a driver which lives in the acpi subsystem to handle dock events.  This 
> driver is not an acpi driver, because acpi drivers require that the object
> be present when the driver is loaded.
> 
> ...
>
> +/**
> + * add_dock_dependent_device - associate a device with the dock station
> + * @ds: The dock station
> + * @dd: The dependent device
> + *
> + * Add the dependent device to the dock's dependent device list.
> + */
> +static void
> +add_dock_dependent_device(struct dock_station *ds,
> +			  struct dock_dependent_device *dd)
> +{
> +	list_add_tail(&dd->list, &ds->dependent_devices);
> +}
> +

Does this not need any locking?

> +/**
> + * find_dock_dependent_device - get a device dependent on this dock
> + * @ds: the dock station
> + * @handle: the acpi_handle of the device we want
> + *
> + * iterate over the dependent device list for this dock.  If the
> + * dependent device matches the handle, return.
> + */
> +static struct dock_dependent_device *
> +find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
> +{
> +	struct dock_dependent_device *dd;
> +
> +	list_for_each_entry(dd, &ds->dependent_devices, list) {
> +		if (handle == dd->handle)
> +			return dd;
> +	}
> +	return NULL;
> +}

Nor this?

> +
> +
> +
> +

The driver has a lot of blank lines between functions.  I don't think this
adds any benefit - it just makes less of the code visible.

> +EXPORT_SYMBOL_GPL(is_dock_device);

I assume all these exports are used?

> +
> +
> +
> +
> +/**
> + * hotplug_devices - insert or remove devices on the dock station
> + * @ds: the dock station
> + * @event: either bus check or eject request
> + *
> + * Some devices on the dock station need to have drivers called
> + * to perform hotplug operations after a dock event has occurred.
> + * Traverse the list of dock devices that have registered a
> + * hotplug handler, and call the handler.
> + */
> +static void hotplug_devices(struct dock_station *ds, u32 event)
> +{
> +	struct dock_dependent_device *dd;
> +
> +	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
> +		if (dd->handler)
> +			dd->handler(dd->handle, event, dd->context);
> +	}
> +}
> +
> +
> +
> +

There's a reasonable chance that someone else will choose identifiers such
as `hotplug_devices' and `hotplug_list'.  Let's hope they're not put in a
header file which gets included here...


> +/**
> + * dock_in_progress - see if we are in the middle of handling a dock event
> + * @ds: the dock station
> + *
> + * Sometimes while docking, false dock events can be sent to the driver
> + * because good connections aren't made or some other reason.  Ignore these
> + * if we are in the middle of doing something.
> + */
> +static int dock_in_progress(struct dock_station *ds)
> +{
> +	if (ds->flags & DOCK_DOCKING ||
> +		(jiffies < (ds->last_dock_time + 10))) {

Peculiar mixture of paranoid and trusting parenthesisation there..

It'll malfunction if jiffies happens to wrap - use time_before() or
time_after() to fix.

> +static void acpi_dock_notify(acpi_handle handle, u32 event, void *data)
> +{
> +	struct dock_station *ds = (struct dock_station *)data;
> +	struct acpi_device *device;
> +
> +	ACPI_FUNCTION_TRACE("acpi_dock_notify");
> +
> +	switch (event) {
> +		case ACPI_NOTIFY_BUS_CHECK:

We normally indent thusly:

	switch (event) {
	case ACPI_NOTIFY_BUS_CHECK:

> +			if (!dock_in_progress(ds) && dock_present(ds)) {
> +				begin_dock(ds);
> +				dock(ds);
> +				if (!dock_present(ds)) {
> +					printk(KERN_ERR PREFIX "Unable to dock!\n");
> +					break;
> +				}
> +				atomic_notifier_call_chain(&dock_notifier_list,
> +					 event, NULL);
> +				hotplug_devices(ds, event);
> +				complete_dock(ds);
> +				if (acpi_bus_get_device(ds->handle, &device))
> +					acpi_bus_generate_event(device,
> +						event, 0);
> +			}

and if you do that here, this code will look nicer.

> +
> +/**
> + * acpi_dock_add - add a new dock station
> + * @handle: the dock station handle
> + *
> + * allocated and initialize a new dock station device.  Find all devices
> + * that are on the dock station, and register for dock event notifications.
> + */
> +static int acpi_dock_add(acpi_handle handle)
> +{
> +	int ret;
> +	acpi_status status;
> +
> +	ACPI_FUNCTION_TRACE("acpi_dock_add");
> +
> +	/* allocate & initialize the dock_station private data */
> +	ds = kzalloc(sizeof(*ds), GFP_KERNEL);

<wonders what ds is>

Oh, it's a file-wide `struct dock_station *'.

Suggest that it be given a more file-widey name.

Would it be better if `ds' be defined at compile time?  Perhaps not..

> +	if (!ds)
> +		return_VALUE(-ENOMEM);
> +	ds->handle = handle;
> +	INIT_LIST_HEAD(&ds->dependent_devices);
> +	INIT_LIST_HEAD(&ds->hotplug_devices);
> +
> +	/* Find dependent devices */
> +	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
> +			ACPI_UINT32_MAX, find_dock_devices, ds, NULL);
> +
> +	/* register for dock events */
> +	status = acpi_install_notify_handler(ds->handle, ACPI_SYSTEM_NOTIFY,
> +				acpi_dock_notify, ds);
> +	if (ACPI_FAILURE(status)) {
> +		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
> +			"Error installing notify handler\n"));
> +		ret = -ENODEV;
> +		goto dock_add_err;
> +	}
> +
> +	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
> +
> +	return_VALUE(0);
> +dock_add_err:
> +	kfree(ds);
> +	return_VALUE(ret);
> +}
> +
>
> ...
>
> --- 2.6-git-kca2.orig/drivers/acpi/Kconfig
> +++ 2.6-git-kca2/drivers/acpi/Kconfig
> @@ -134,6 +134,12 @@ config ACPI_FAN
>  	  This driver adds support for ACPI fan devices, allowing user-mode 
>  	  applications to perform basic fan control (on, off, status).
>  
> +config ACPI_DOCK
> +	tristate "Dock"
> +	default y
> +	help
> +	  This driver adds support for ACPI controlled docking stations

It doesn't depend upon anything else?



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 3/3] acpiphp: prevent duplicate slot numbers when no _SUN
  2006-04-12 22:18 ` [patch 3/3] acpiphp: prevent duplicate slot numbers when no _SUN Kristen Accardi
@ 2006-04-13 12:36   ` MUNEDA Takahiro
  2006-04-14 21:39     ` Kristen Accardi
  0 siblings, 1 reply; 24+ messages in thread
From: MUNEDA Takahiro @ 2006-04-13 12:36 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel,
	mochel, arjan, muneda.takahiro, pavel, temnota

At Wed, 12 Apr 2006 15:18:47 -0700,
Kristen Accardi <kristen.c.accardi@intel.com> wrote:
> 
> Dock bridges generally do not implement _SUN, yet show up as ejectable slots.
> If you have more than one ejectable slot that does not implement SUN, with the
> current code you will get duplicate slot numbers.  So, if there is no _SUN,
> use the current count of the number of slots found instead.
> 
> Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
> 
> ---
>  drivers/pci/hotplug/acpiphp_glue.c |    9 +++++++--
>  1 files changed, 7 insertions(+), 2 deletions(-)
> 
> --- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_glue.c
> +++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp_glue.c
> @@ -218,8 +218,13 @@ register_slot(acpi_handle handle, u32 lv
>  		newfunc->flags |= FUNC_HAS_DCK;
>  
>  	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
> -	if (ACPI_FAILURE(status))
> -		sun = -1;
> +	if (ACPI_FAILURE(status)) {
> +		/*
> +		 * use the count of the number of slots we've found
> +		 * for the number of the slot
> +		 */
> +		sun = bridge->nr_slots+1;
> +	}
>  
>  	/* search for objects that share the same slot */
>  	for (slot = bridge->slots; slot; slot = slot->next)
> 
> --

No, "sun = bridge->nr_slots+1" might have been defined for
another device. Please consider the following case.

  Device (PCI0) {                 /* Root bridge */
      Name (_HID, "PNP0A03")
      Device (P2PA) {             /* PCI-to-PCI bridge */
          Name (_ADR, ...)
          Device (S0F0) {         /* hotplug slot */
              Name (_ADR, ...)
              Name (_SUN, 0x01)
              Method (_EJ0, ...) { ... }
          }
          Device (S1F0) {         /* hotplug slot */
              Name (_ADR, ...)
              Name (_SUN, 0x02)
              Method (_EJ0, ...) { ... }
          }
          Device (GDCK) {         /* Docking Station */
              Method (_DCK, ...) { ... }
              Method (_EJ0, ...) { ... }
      }
      Device (P2PB) {             /* PCI-to-PCI bridge */
          Name (_ADR, ...)
          Device (S0F0) {         /* hotplug slot */
              Name (_ADR, ...)
              Name (_SUN, 0x03)
              Method (_EJ0, ...) { ... }
          }
          Device (S1F0) {         /* hotplug slot */
              Name (_ADR, ...)
              Name (_SUN, 0x04)
              Method (_EJ0, ...) { ... }
          }
      }
  }

In this case, there are two hotplug slots under the P2PA.
GDCK doesn't have SUN, so acpiphp sets SUN#3 for GDCK. But
SUN#3 is for the PCI0.P2PB.S0F0. sun is not unique!

But I have never seen the dsdt like above.

Thanks,
MUNE

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 3/3] acpiphp: prevent duplicate slot numbers when no _SUN
  2006-04-13 12:36   ` MUNEDA Takahiro
@ 2006-04-14 21:39     ` Kristen Accardi
  0 siblings, 0 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-14 21:39 UTC (permalink / raw)
  To: MUNEDA Takahiro
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel,
	mochel, arjan, pavel, temnota

On Thu, 2006-04-13 at 21:36 +0900, MUNEDA Takahiro wrote:
> At Wed, 12 Apr 2006 15:18:47 -0700,
> Kristen Accardi <kristen.c.accardi@intel.com> wrote:
> > 
> > Dock bridges generally do not implement _SUN, yet show up as ejectable slots.
> > If you have more than one ejectable slot that does not implement SUN, with the
> > current code you will get duplicate slot numbers.  So, if there is no _SUN,
> > use the current count of the number of slots found instead.
> > 
> > Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
> > 
> > ---
> >  drivers/pci/hotplug/acpiphp_glue.c |    9 +++++++--
> >  1 files changed, 7 insertions(+), 2 deletions(-)
> > 
> > --- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_glue.c
> > +++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp_glue.c
> > @@ -218,8 +218,13 @@ register_slot(acpi_handle handle, u32 lv
> >  		newfunc->flags |= FUNC_HAS_DCK;
> >  
> >  	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
> > -	if (ACPI_FAILURE(status))
> > -		sun = -1;
> > +	if (ACPI_FAILURE(status)) {
> > +		/*
> > +		 * use the count of the number of slots we've found
> > +		 * for the number of the slot
> > +		 */
> > +		sun = bridge->nr_slots+1;
> > +	}
> >  
> >  	/* search for objects that share the same slot */
> >  	for (slot = bridge->slots; slot; slot = slot->next)
> > 
> > --
> 
> No, "sun = bridge->nr_slots+1" might have been defined for
> another device. Please consider the following case.
> 
>   Device (PCI0) {                 /* Root bridge */
>       Name (_HID, "PNP0A03")
>       Device (P2PA) {             /* PCI-to-PCI bridge */
>           Name (_ADR, ...)
>           Device (S0F0) {         /* hotplug slot */
>               Name (_ADR, ...)
>               Name (_SUN, 0x01)
>               Method (_EJ0, ...) { ... }
>           }
>           Device (S1F0) {         /* hotplug slot */
>               Name (_ADR, ...)
>               Name (_SUN, 0x02)
>               Method (_EJ0, ...) { ... }
>           }
>           Device (GDCK) {         /* Docking Station */
>               Method (_DCK, ...) { ... }
>               Method (_EJ0, ...) { ... }
>       }
>       Device (P2PB) {             /* PCI-to-PCI bridge */
>           Name (_ADR, ...)
>           Device (S0F0) {         /* hotplug slot */
>               Name (_ADR, ...)
>               Name (_SUN, 0x03)
>               Method (_EJ0, ...) { ... }
>           }
>           Device (S1F0) {         /* hotplug slot */
>               Name (_ADR, ...)
>               Name (_SUN, 0x04)
>               Method (_EJ0, ...) { ... }
>           }
>       }
>   }
> 
> In this case, there are two hotplug slots under the P2PA.
> GDCK doesn't have SUN, so acpiphp sets SUN#3 for GDCK. But
> SUN#3 is for the PCI0.P2PB.S0F0. sun is not unique!
> 
> But I have never seen the dsdt like above.
> 
> Thanks,
> MUNE

I could invent other methods of making up sun, but it is always possible
that _SUN could be defined for whatever I make up - although of course
some numbers are less likely than others.  Personally I have never seen
a dsdt such as above either - maybe we should leave it and then change
it when we have an example of a case where we run into problems?
Alternatively, I can just bump the sun up to a number that is less
likely to be in use, such as bridge->nr_slots+100.  Opinions?

Kristen


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-13  5:27   ` [patch 1/3] acpi: dock driver Andrew Morton
@ 2006-04-14 22:02     ` Kristen Accardi
  2006-04-14 22:49     ` Kristen Accardi
  1 sibling, 0 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-14 22:02 UTC (permalink / raw)
  To: Andrew Morton
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel,
	mochel, arjan, muneda.takahiro, pavel, temnota

On Wed, 2006-04-12 at 22:27 -0700, Andrew Morton wrote:
> Kristen Accardi <kristen.c.accardi@intel.com> wrote:
> >
> > Create a driver which lives in the acpi subsystem to handle dock events.  This 
> > driver is not an acpi driver, because acpi drivers require that the object
> > be present when the driver is loaded.
> > 
> > ...
> >
> > +/**
> > + * add_dock_dependent_device - associate a device with the dock station
> > + * @ds: The dock station
> > + * @dd: The dependent device
> > + *
> > + * Add the dependent device to the dock's dependent device list.
> > + */
> > +static void
> > +add_dock_dependent_device(struct dock_station *ds,
> > +			  struct dock_dependent_device *dd)
> > +{
> > +	list_add_tail(&dd->list, &ds->dependent_devices);
> > +}
> > +
> 
> Does this not need any locking?

yes, I'll fix this.

> 
> > +/**
> > + * find_dock_dependent_device - get a device dependent on this dock
> > + * @ds: the dock station
> > + * @handle: the acpi_handle of the device we want
> > + *
> > + * iterate over the dependent device list for this dock.  If the
> > + * dependent device matches the handle, return.
> > + */
> > +static struct dock_dependent_device *
> > +find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
> > +{
> > +	struct dock_dependent_device *dd;
> > +
> > +	list_for_each_entry(dd, &ds->dependent_devices, list) {
> > +		if (handle == dd->handle)
> > +			return dd;
> > +	}
> > +	return NULL;
> > +}
> 
> Nor this?

yes, I'll fix this too.

> 
> > +
> > +
> > +
> > +
> 
> The driver has a lot of blank lines between functions.  I don't think this
> adds any benefit - it just makes less of the code visible.
> 
> > +EXPORT_SYMBOL_GPL(is_dock_device);
> 
> I assume all these exports are used?
> 

Yes, they are.

> > +
> > +
> > +
> > +
> > +/**
> > + * hotplug_devices - insert or remove devices on the dock station
> > + * @ds: the dock station
> > + * @event: either bus check or eject request
> > + *
> > + * Some devices on the dock station need to have drivers called
> > + * to perform hotplug operations after a dock event has occurred.
> > + * Traverse the list of dock devices that have registered a
> > + * hotplug handler, and call the handler.
> > + */
> > +static void hotplug_devices(struct dock_station *ds, u32 event)
> > +{
> > +	struct dock_dependent_device *dd;
> > +
> > +	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
> > +		if (dd->handler)
> > +			dd->handler(dd->handle, event, dd->context);
> > +	}
> > +}
> > +
> > +
> > +
> > +
> 
> There's a reasonable chance that someone else will choose identifiers such
> as `hotplug_devices' and `hotplug_list'.  Let's hope they're not put in a
> header file which gets included here...
> 

No, they aren't included, however, I'll change the function name just in
case the function is ever exported.

> 
> > +/**
> > + * dock_in_progress - see if we are in the middle of handling a dock event
> > + * @ds: the dock station
> > + *
> > + * Sometimes while docking, false dock events can be sent to the driver
> > + * because good connections aren't made or some other reason.  Ignore these
> > + * if we are in the middle of doing something.
> > + */
> > +static int dock_in_progress(struct dock_station *ds)
> > +{
> > +	if (ds->flags & DOCK_DOCKING ||
> > +		(jiffies < (ds->last_dock_time + 10))) {
> 
> Peculiar mixture of paranoid and trusting parenthesisation there..
> 
> It'll malfunction if jiffies happens to wrap - use time_before() or
> time_after() to fix.
> 

Thanks, I'll fix this.

> > +static void acpi_dock_notify(acpi_handle handle, u32 event, void *data)
> > +{
> > +	struct dock_station *ds = (struct dock_station *)data;
> > +	struct acpi_device *device;
> > +
> > +	ACPI_FUNCTION_TRACE("acpi_dock_notify");
> > +
> > +	switch (event) {
> > +		case ACPI_NOTIFY_BUS_CHECK:
> 
> We normally indent thusly:
> 
> 	switch (event) {
> 	case ACPI_NOTIFY_BUS_CHECK:
> 
> > +			if (!dock_in_progress(ds) && dock_present(ds)) {
> > +				begin_dock(ds);
> > +				dock(ds);
> > +				if (!dock_present(ds)) {
> > +					printk(KERN_ERR PREFIX "Unable to dock!\n");
> > +					break;
> > +				}
> > +				atomic_notifier_call_chain(&dock_notifier_list,
> > +					 event, NULL);
> > +				hotplug_devices(ds, event);
> > +				complete_dock(ds);
> > +				if (acpi_bus_get_device(ds->handle, &device))
> > +					acpi_bus_generate_event(device,
> > +						event, 0);
> > +			}
> 
> and if you do that here, this code will look nicer.
> 
> > +
> > +/**
> > + * acpi_dock_add - add a new dock station
> > + * @handle: the dock station handle
> > + *
> > + * allocated and initialize a new dock station device.  Find all devices
> > + * that are on the dock station, and register for dock event notifications.
> > + */
> > +static int acpi_dock_add(acpi_handle handle)
> > +{
> > +	int ret;
> > +	acpi_status status;
> > +
> > +	ACPI_FUNCTION_TRACE("acpi_dock_add");
> > +
> > +	/* allocate & initialize the dock_station private data */
> > +	ds = kzalloc(sizeof(*ds), GFP_KERNEL);
> 
> <wonders what ds is>
> 
> Oh, it's a file-wide `struct dock_station *'.
> 
> Suggest that it be given a more file-widey name.
> 
> Would it be better if `ds' be defined at compile time?  Perhaps not..
> 

Probably not.

> > +	if (!ds)
> > +		return_VALUE(-ENOMEM);
> > +	ds->handle = handle;
> > +	INIT_LIST_HEAD(&ds->dependent_devices);
> > +	INIT_LIST_HEAD(&ds->hotplug_devices);
> > +
> > +	/* Find dependent devices */
> > +	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
> > +			ACPI_UINT32_MAX, find_dock_devices, ds, NULL);
> > +
> > +	/* register for dock events */
> > +	status = acpi_install_notify_handler(ds->handle, ACPI_SYSTEM_NOTIFY,
> > +				acpi_dock_notify, ds);
> > +	if (ACPI_FAILURE(status)) {
> > +		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
> > +			"Error installing notify handler\n"));
> > +		ret = -ENODEV;
> > +		goto dock_add_err;
> > +	}
> > +
> > +	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
> > +
> > +	return_VALUE(0);
> > +dock_add_err:
> > +	kfree(ds);
> > +	return_VALUE(ret);
> > +}
> > +
> >
> > ...
> >
> > --- 2.6-git-kca2.orig/drivers/acpi/Kconfig
> > +++ 2.6-git-kca2/drivers/acpi/Kconfig
> > @@ -134,6 +134,12 @@ config ACPI_FAN
> >  	  This driver adds support for ACPI fan devices, allowing user-mode 
> >  	  applications to perform basic fan control (on, off, status).
> >  
> > +config ACPI_DOCK
> > +	tristate "Dock"
> > +	default y
> > +	help
> > +	  This driver adds support for ACPI controlled docking stations
> 
> It doesn't depend upon anything else?

It doesn't have to.  If you want to be able to hotplug any PCI devices
on the dock station, then it does depend on HOTPLUG_PCI, however, there
are dock stations which have no PCI devices on them which would not need
to do this.

I'll send a new version which incorporates your feedback soon.  Thanks
for reviewing, I really appreciate it.

Kristen

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-13  5:27   ` [patch 1/3] acpi: dock driver Andrew Morton
  2006-04-14 22:02     ` Kristen Accardi
@ 2006-04-14 22:49     ` Kristen Accardi
  2006-04-15 14:29       ` Prarit Bhargava
  2006-04-16 13:28       ` [patch 1/3] acpi: dock driver Prarit Bhargava
  1 sibling, 2 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-14 22:49 UTC (permalink / raw)
  To: Andrew Morton
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel,
	mochel, arjan, muneda.takahiro, pavel, temnota

Create a driver which lives in the acpi subsystem to handle dock events.  This 
driver is not an acpi driver, because acpi drivers require that the object
be present when the driver is loaded.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---
this version fixes the following issues from the previous version:
1.  Fix oops on driver remove reported by temnota
2.  Make more file-widey names. 
3.  Use time_before for jiffie compare
4.  fix locking issues on list_add and list iterate

 drivers/acpi/Kconfig        |    6 
 drivers/acpi/Makefile       |    1 
 drivers/acpi/dock.c         |  700 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c         |   23 +
 include/acpi/acpi_bus.h     |    2 
 include/acpi/acpi_drivers.h |    9 
 6 files changed, 740 insertions(+), 1 deletion(-)

--- /dev/null
+++ 2.6-git-kca2/drivers/acpi/dock.c
@@ -0,0 +1,700 @@
+/*
+ *  dock.c - ACPI dock station driver
+ *
+ *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_DOCK_COMPONENT 0x10000000
+#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
+#define _COMPONENT		ACPI_DOCK_COMPONENT
+
+ACPI_MODULE_NAME("dock")
+MODULE_AUTHOR("Kristen Carlson Accardi");
+MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static struct atomic_notifier_head dock_notifier_list;
+
+struct dock_station {
+	acpi_handle handle;
+	unsigned long last_dock_time;
+	u32 flags;
+	spinlock_t dd_lock;
+	spinlock_t hp_lock;
+	struct list_head dependent_devices;
+	struct list_head hotplug_devices;
+};
+
+
+struct dock_dependent_device {
+	struct list_head list;
+	struct list_head hotplug_list;
+	acpi_handle handle;
+	acpi_notify_handler handler;
+	void *context;
+};
+
+#define DOCK_DOCKING	0x00000001
+
+static struct dock_station *dock_station;
+
+/*****************************************************************************
+ *                         Dock Dependent device functions                   *
+ *****************************************************************************/
+/**
+ *  alloc_dock_dependent_device - allocate and init a dependent device
+ *  @handle: the acpi_handle of the dependent device
+ *
+ *  Allocate memory for a dependent device structure for a device referenced
+ *  by the acpi handle
+ */
+static struct dock_dependent_device * alloc_dock_dependent_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd) {
+		dd->handle = handle;
+		INIT_LIST_HEAD(&dd->list);
+		INIT_LIST_HEAD(&dd->hotplug_list);
+	}
+	return dd;
+}
+
+
+
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static void
+add_dock_dependent_device(struct dock_station *ds,
+			  struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->dd_lock);
+	list_add_tail(&dd->list, &ds->dependent_devices);
+	spin_unlock(&ds->dd_lock);
+}
+
+
+
+/**
+ * dock_add_hotplug_device - associate a hotplug handler with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device struct
+ *
+ * Add the dependent device to the dock's hotplug device list
+ */
+static void
+dock_add_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+	spin_unlock(&ds->hp_lock);
+}
+
+
+/**
+ * dock_del_hotplug_device - remove a hotplug handler from the dock station
+ * @ds: The dock station
+ * @dd: the dependent device struct
+ *
+ * Delete the dependent device from the dock's hotplug device list
+ */
+static void
+dock_del_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_del(&dd->hotplug_list);
+	spin_unlock(&ds->hp_lock);
+}
+
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @handle: the acpi_handle of the device we want
+ *
+ * iterate over the dependent device list for this dock.  If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->dd_lock);
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (handle == dd->handle) {
+			spin_unlock(&ds->dd_lock);
+			return dd;
+		}
+	}
+	spin_unlock(&ds->dd_lock);
+	return NULL;
+}
+
+
+
+/*****************************************************************************
+ *                         Dock functions                                    *
+ *****************************************************************************/
+/**
+ * is_dock - see if a device is a dock station
+ * @handle: acpi handle of the device
+ *
+ * If an acpi object has a _DCK method, then it is by definition a dock
+ * station, so return true.
+ */
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	return 1;
+}
+
+
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @handle: acpi handle of the device
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(acpi_handle handle)
+{
+	if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
+		return 1;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+
+
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method.  note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+	unsigned long sta;
+	acpi_status status;
+
+	if (ds) {
+		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+		if (ACPI_SUCCESS(status) && sta)
+			return 1;
+	}
+	return 0;
+}
+
+
+
+
+/**
+ * hotplug_dock_devices - insert or remove devices on the dock station
+ * @ds: the dock station
+ * @event: either bus check or eject request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_dock_devices(struct dock_station *ds, u32 event)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->hp_lock);
+	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+		if (dd->handler)
+			dd->handler(dd->handle, event, dd->context);
+	}
+	spin_unlock(&ds->hp_lock);
+}
+
+
+
+
+/**
+ * eject_dock - respond to a dock eject request
+ * @ds: the dock station
+ *
+ * This is called after _DCK is called, to execute the dock station's
+ * _EJ0 method.
+ */
+static void eject_dock(struct dock_station *ds)
+{
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+
+	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &buffer);
+	obj = buffer.pointer;
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+
+	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
+					&arg_list, NULL)) || dock_present(ds))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to eject dock!\n",
+			obj->string.pointer));
+
+	acpi_os_free(buffer.pointer);
+}
+
+
+
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static acpi_status handle_dock(struct dock_station *ds, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	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;
+
+	ACPI_FUNCTION_TRACE(__FUNCTION__);
+
+	printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = dock;
+	status = acpi_evaluate_object(ds->handle, "_DCK",
+					&arg_list, &buffer);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to execute _DCK\n",
+			obj->string.pointer));
+	acpi_os_free(buffer.pointer);
+	acpi_os_free(name_buffer.pointer);
+
+	return_VALUE(status);
+}
+
+
+
+static inline void dock(struct dock_station *ds)
+{
+	handle_dock(ds, 1);
+}
+
+
+static inline void undock(struct dock_station *ds)
+{
+	handle_dock(ds, 0);
+}
+
+
+static inline void begin_dock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_DOCKING;
+}
+
+
+static inline void complete_dock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_DOCKING);
+	ds->last_dock_time = jiffies;
+}
+
+
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason.  Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+	if ((ds->flags & DOCK_DOCKING) ||
+		time_before(jiffies, (ds->last_dock_time + HZ)))
+		return 1;
+	return 0;
+}
+
+
+
+/**
+ * register_dock_notifier - add yourself to the dock notifier list
+ * @nb: the callers notifier block
+ *
+ * If a driver wishes to be notified about dock events, they can
+ * use this function to put a notifier block on the dock notifier list.
+ * this notifier call chain will be called after a dock event, but
+ * before hotplugging any new devices.
+ */
+int register_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&dock_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_dock_notifier);
+
+
+
+
+/**
+ * unregister_dock_notifier - remove yourself from the dock notifier list
+ * @nb: the callers notifier block
+ */
+int unregister_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&dock_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_dock_notifier);
+
+
+
+/**
+ * register_hotplug_dock_device - register a hotplug function
+ * @handle: the handle of the device
+ * @handler: the acpi_notifier_handler to call after docking
+ * @context: device specific data
+ *
+ * If a driver would like to perform a hotplug operation after a dock
+ * event, they can register an acpi_notifiy_handler to be called by
+ * the dock driver after _DCK is executed.
+ */
+acpi_status
+register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
+		void *context)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return AE_ERROR;
+
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * this would include the dock station itself
+	 */
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd) {
+		dd->handler = handler;
+		dd->context = context;
+		dock_add_hotplug_device(dock_station, dd);
+		return AE_OK;
+	}
+
+	return AE_ERROR;
+}
+EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
+
+
+
+/**
+ * unregister_hotplug_dock_device - remove yourself from the hotplug list
+ * @handle: the acpi handle of the device
+ */
+acpi_status unregister_hotplug_dock_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return AE_ERROR;
+
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd)
+		dock_del_hotplug_device(dock_station, dd);
+	return AE_OK;
+}
+EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
+
+
+
+
+/**
+ * acpi_dock_notify - act upon an acpi dock notification
+ * @handle: the dock station handle
+ * @event: the acpi event
+ * @data: our driver data struct
+ *
+ * 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.
+ */
+static void acpi_dock_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct dock_station *ds = (struct dock_station *)data;
+	struct acpi_device *device;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_notify");
+
+	switch (event) {
+	case ACPI_NOTIFY_BUS_CHECK:
+		if (!dock_in_progress(ds) && dock_present(ds)) {
+			begin_dock(ds);
+			dock(ds);
+			if (!dock_present(ds)) {
+				printk(KERN_ERR PREFIX "Unable to dock!\n");
+				break;
+			}
+			atomic_notifier_call_chain(&dock_notifier_list,
+			 	event, NULL);
+			hotplug_dock_devices(ds, event);
+			complete_dock(ds);
+			if (acpi_bus_get_device(ds->handle, &device))
+				acpi_bus_generate_event(device, event, 0);
+		}
+		break;
+	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.
+		 	 */
+			if (acpi_bus_get_device(ds->handle, &device))
+				acpi_bus_generate_event(device, event, 0);
+
+			hotplug_dock_devices(ds, event);
+			undock(ds);
+			eject_dock(ds);
+			if (dock_present(ds))
+				printk(KERN_ERR PREFIX "Unable to undock!\n");
+		}
+		break;
+	}
+	return_VOID;
+}
+
+
+
+
+/**
+ * find_dock_devices - find devices on the dock station
+ * @handle: the handle of the device we are examining
+ * @lvl: unused
+ * @context: the dock station private data
+ * @rv: unused
+ *
+ * This function is called by acpi_walk_namespace.  It will
+ * check to see if an object has an _EJD method.  If it does, then it
+ * will see if it is dependent on the dock station.
+ */
+static acpi_status
+find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_station *ds = (struct dock_station *) context;
+	struct dock_dependent_device *dd;
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return AE_OK;
+
+	if (tmp == ds->handle) {
+		dd = alloc_dock_dependent_device(handle);
+		add_dock_dependent_device(ds, dd);
+	}
+
+	return AE_OK;
+}
+
+
+
+/**
+ * acpi_dock_add - add a new dock station
+ * @handle: the dock station handle
+ *
+ * allocated and initialize a new dock station device.  Find all devices
+ * that are on the dock station, and register for dock event notifications.
+ */
+static int acpi_dock_add(acpi_handle handle)
+{
+	int ret;
+	acpi_status status;
+	struct dock_dependent_device *dd;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_add");
+
+	/* allocate & initialize the dock_station private data */
+	dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
+	if (!dock_station)
+		return_VALUE(-ENOMEM);
+	dock_station->handle = handle;
+	INIT_LIST_HEAD(&dock_station->dependent_devices);
+	INIT_LIST_HEAD(&dock_station->hotplug_devices);
+	spin_lock_init(&dock_station->dd_lock);
+	spin_lock_init(&dock_station->hp_lock);
+
+	/* Find dependent devices */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock_devices, dock_station, NULL);
+
+	/* add the dock station as a device dependent on itself */
+	dd = alloc_dock_dependent_device(handle);
+	add_dock_dependent_device(dock_station, dd);
+
+	/* register for dock events */
+	status = acpi_install_notify_handler(dock_station->handle,
+			ACPI_SYSTEM_NOTIFY, acpi_dock_notify, dock_station);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Error installing notify handler\n"));
+		ret = -ENODEV;
+		goto dock_add_err;
+	}
+
+	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
+
+	return_VALUE(0);
+dock_add_err:
+	kfree(dock_station);
+	kfree(dd);
+	return_VALUE(ret);
+}
+
+
+
+/**
+ * acpi_dock_remove - free up resources related to the dock station
+ */
+static int acpi_dock_remove(void)
+{
+	struct dock_dependent_device *dd, *tmp;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_remove");
+
+	if (!dock_station)
+		return_VALUE(0);
+
+	/* remove dependent devices */
+	list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
+			list)
+		kfree(dd);
+
+	/* remove dock notify handler */
+	status = acpi_remove_notify_handler(dock_station->handle,
+			ACPI_SYSTEM_NOTIFY, acpi_dock_notify);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Error removing notify handler\n"));
+
+	/* free dock station memory */
+	kfree(dock_station);
+	return_VALUE(0);
+}
+
+
+
+
+/**
+ * find_dock - look for a dock station
+ * @handle: acpi handle of a device
+ * @lvl: unused
+ * @context: counter of dock stations found
+ * @rv: unused
+ *
+ * This is called by acpi_walk_namespace to look for dock stations.
+ */
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+	acpi_status status = AE_OK;
+
+	if (is_dock(handle)) {
+		if (acpi_dock_add(handle) >= 0) {
+			(*count)++;
+			status = AE_CTRL_TERMINATE;
+		}
+	}
+	return status;
+}
+
+
+
+static int __init acpi_dock_init(void)
+{
+	int num = 0;
+
+	ACPI_FUNCTION_TRACE("acpi_dock_init");
+
+	dock_station = NULL;
+
+	/* look for a dock station */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	if (!num)
+		return_VALUE(-ENODEV);
+
+	return_VALUE(0);
+}
+
+static void __exit acpi_dock_exit(void)
+{
+	ACPI_FUNCTION_TRACE("acpi_dock_exit");
+	acpi_dock_remove();
+	return_VOID;
+}
+
+module_init(acpi_dock_init);
+module_exit(acpi_dock_exit);
--- 2.6-git-kca2.orig/drivers/acpi/Kconfig
+++ 2.6-git-kca2/drivers/acpi/Kconfig
@@ -134,6 +134,12 @@ config ACPI_FAN
 	  This driver adds support for ACPI fan devices, allowing user-mode 
 	  applications to perform basic fan control (on, off, status).
 
+config ACPI_DOCK
+	tristate "Dock"
+	default y
+	help
+	  This driver adds support for ACPI controlled docking stations
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	default y
--- 2.6-git-kca2.orig/drivers/acpi/Makefile
+++ 2.6-git-kca2/drivers/acpi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
+obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o 
 obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
--- 2.6-git-kca2.orig/drivers/acpi/scan.c
+++ 2.6-git-kca2/drivers/acpi/scan.c
@@ -703,6 +703,29 @@ static int acpi_bus_find_driver(struct a
                                  Device Enumeration
    -------------------------------------------------------------------------- */
 
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		obj = buffer.pointer;
+		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+		acpi_os_free(buffer.pointer);
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+
 static int acpi_bus_get_flags(struct acpi_device *device)
 {
 	acpi_status status = AE_OK;
--- 2.6-git-kca2.orig/include/acpi/acpi_bus.h
+++ 2.6-git-kca2/include/acpi/acpi_bus.h
@@ -332,7 +332,7 @@ int acpi_bus_add(struct acpi_device **ch
 		 acpi_handle handle, int type);
 int acpi_bus_trim(struct acpi_device *start, int rmdevice);
 int acpi_bus_start(struct acpi_device *device);
-
+acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd);
 int acpi_match_ids(struct acpi_device *device, char *ids);
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
--- 2.6-git-kca2.orig/include/acpi/acpi_drivers.h
+++ 2.6-git-kca2/include/acpi/acpi_drivers.h
@@ -110,4 +110,13 @@ int acpi_processor_set_thermal_limit(acp
 
 extern int acpi_specific_hotkey_enabled;
 
+/*--------------------------------------------------------------------------
+                                  Dock Station
+  -------------------------------------------------------------------------- */
+extern int is_dock_device(acpi_handle handle);
+extern int register_dock_notifier(struct notifier_block *nb);
+extern int unregister_dock_notifier(struct notifier_block *nb);
+extern acpi_status register_hotplug_dock_device(acpi_handle handle,
+	acpi_notify_handler handler, void *context);
+extern acpi_status unregister_hotplug_dock_device(acpi_handle handle);
 #endif /*__ACPI_DRIVERS_H__*/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-14 22:49     ` Kristen Accardi
@ 2006-04-15 14:29       ` Prarit Bhargava
  2006-04-18 18:03         ` Kristen Accardi
  2006-04-16 13:28       ` [patch 1/3] acpi: dock driver Prarit Bhargava
  1 sibling, 1 reply; 24+ messages in thread
From: Prarit Bhargava @ 2006-04-15 14:29 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: Andrew Morton, len.brown, greg, linux-acpi, pcihpd-discuss,
	linux-kernel, mochel, arjan, muneda.takahiro, pavel, temnota


> +static struct dock_dependent_device * alloc_dock_dependent_device(acpi_handle handle)
> +{
> +	struct dock_dependent_device *dd;
> +
> +	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
> +	if (dd) {
> +		dd->handle = handle;
> +		INIT_LIST_HEAD(&dd->list);
> +		INIT_LIST_HEAD(&dd->hotplug_list);
> +	}
> +	return dd;
> +}
> +

Er ... what happens if dd isn't alloc'd?  It looks like the rest of the 
code assumes that dd is a valid pointer ...

P.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-14 22:49     ` Kristen Accardi
  2006-04-15 14:29       ` Prarit Bhargava
@ 2006-04-16 13:28       ` Prarit Bhargava
  1 sibling, 0 replies; 24+ messages in thread
From: Prarit Bhargava @ 2006-04-16 13:28 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: Andrew Morton, len.brown, greg, linux-acpi, pcihpd-discuss,
	linux-kernel, mochel, arjan, muneda.takahiro, pavel, temnota

I'm not sure if my previous reply made it out to the list and everyone 
else.  I'm in the process of switching email addresses :(.  Sorry if 
this is a repeat Kristen, et al.... My apologies if this is the case ...

>+static struct dock_dependent_device * alloc_dock_dependent_device(acpi_handle handle)
>+{
>+	struct dock_dependent_device *dd;
>+
>+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
>+	if (dd) {
>+		dd->handle = handle;
>+		INIT_LIST_HEAD(&dd->list);
>+		INIT_LIST_HEAD(&dd->hotplug_list);
>+	}
>+	return dd;
>+}
>+
>
>  
>
What do you do if this returns NULL (ie, kzalloc did not succeed)?  Then 
you blindly use the pointer elsewhere in the code?

P.

P.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-15 14:29       ` Prarit Bhargava
@ 2006-04-18 18:03         ` Kristen Accardi
  2006-04-18 22:54           ` Patrick Mochel
  2006-04-28 23:51           ` [patch 1/3] acpi: dock driver v3 Kristen Accardi
  0 siblings, 2 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-18 18:03 UTC (permalink / raw)
  To: Prarit Bhargava
  Cc: Andrew Morton, len.brown, greg, linux-acpi, pcihpd-discuss,
	linux-kernel, mochel, arjan, muneda.takahiro, pavel, temnota

Create a driver which lives in the acpi subsystem to handle dock events.  This 
driver is not an acpi driver, because acpi drivers require that the object
be present when the driver is loaded.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---
Changes from last version:
1. Ran through Lindent as Len requested, and then fixed up strange Lindent stuff.
2. Removed ACPI_FUNCTION_TRACE etc.
3. Verified that ACPI_DEBUG printing was needed only with acpi debug enabled.
4. Changed calls to acpi_os_free to kfree
5. Fixed bug found by Prarit where callers of alloc_dock_dependent_device do not
   check return value.
6. Fixed bug in driver init where last_dock_time is not properly initialized. 
   This caused intermittant failures to dock/undock.
7. Added dependency on !ACPI_IBM_DOCK
8. Added check for ACPI_NOTIFY_DEVICE_CHECK to dock_notify, which seemed to have
   gotten lost from the last version I sent out.

Thanks for reviewing!

 drivers/acpi/Kconfig        |    7 
 drivers/acpi/Makefile       |    1 
 drivers/acpi/dock.c         |  652 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c         |   23 +
 include/acpi/acpi_bus.h     |    2 
 include/acpi/acpi_drivers.h |    9 
 6 files changed, 693 insertions(+), 1 deletion(-)

--- /dev/null
+++ 2.6-git-kca2/drivers/acpi/dock.c
@@ -0,0 +1,652 @@
+/*
+ *  dock.c - ACPI dock station driver
+ *
+ *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_DOCK_COMPONENT 0x10000000
+#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
+#define _COMPONENT		ACPI_DOCK_COMPONENT
+
+ACPI_MODULE_NAME("dock")
+MODULE_AUTHOR("Kristen Carlson Accardi");
+MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static struct atomic_notifier_head dock_notifier_list;
+
+struct dock_station {
+	acpi_handle handle;
+	unsigned long last_dock_time;
+	u32 flags;
+	spinlock_t dd_lock;
+	spinlock_t hp_lock;
+	struct list_head dependent_devices;
+	struct list_head hotplug_devices;
+};
+
+struct dock_dependent_device {
+	struct list_head list;
+	struct list_head hotplug_list;
+	acpi_handle handle;
+	acpi_notify_handler handler;
+	void *context;
+};
+
+#define DOCK_DOCKING	0x00000001
+
+static struct dock_station *dock_station;
+
+/*****************************************************************************
+ *                         Dock Dependent device functions                   *
+ *****************************************************************************/
+/**
+ *  alloc_dock_dependent_device - allocate and init a dependent device
+ *  @handle: the acpi_handle of the dependent device
+ *
+ *  Allocate memory for a dependent device structure for a device referenced
+ *  by the acpi handle
+ */
+static struct dock_dependent_device *
+alloc_dock_dependent_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd) {
+		dd->handle = handle;
+		INIT_LIST_HEAD(&dd->list);
+		INIT_LIST_HEAD(&dd->hotplug_list);
+	}
+	return dd;
+}
+
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static void
+add_dock_dependent_device(struct dock_station *ds,
+			  struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->dd_lock);
+	list_add_tail(&dd->list, &ds->dependent_devices);
+	spin_unlock(&ds->dd_lock);
+}
+
+/**
+ * dock_add_hotplug_device - associate a hotplug handler with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device struct
+ *
+ * Add the dependent device to the dock's hotplug device list
+ */
+static void
+dock_add_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * dock_del_hotplug_device - remove a hotplug handler from the dock station
+ * @ds: The dock station
+ * @dd: the dependent device struct
+ *
+ * Delete the dependent device from the dock's hotplug device list
+ */
+static void
+dock_del_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_del(&dd->hotplug_list);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @handle: the acpi_handle of the device we want
+ *
+ * iterate over the dependent device list for this dock.  If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->dd_lock);
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (handle == dd->handle) {
+			spin_unlock(&ds->dd_lock);
+			return dd;
+		}
+	}
+	spin_unlock(&ds->dd_lock);
+	return NULL;
+}
+
+/*****************************************************************************
+ *                         Dock functions                                    *
+ *****************************************************************************/
+/**
+ * is_dock - see if a device is a dock station
+ * @handle: acpi handle of the device
+ *
+ * If an acpi object has a _DCK method, then it is by definition a dock
+ * station, so return true.
+ */
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	return 1;
+}
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @handle: acpi handle of the device
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(acpi_handle handle)
+{
+	if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
+		return 1;
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method.  note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+	unsigned long sta;
+	acpi_status status;
+
+	if (ds) {
+		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+		if (ACPI_SUCCESS(status) && sta)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * hotplug_dock_devices - insert or remove devices on the dock station
+ * @ds: the dock station
+ * @event: either bus check or eject request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_dock_devices(struct dock_station *ds, u32 event)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->hp_lock);
+	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+		if (dd->handler)
+			dd->handler(dd->handle, event, dd->context);
+	}
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * eject_dock - respond to a dock eject request
+ * @ds: the dock station
+ *
+ * This is called after _DCK is called, to execute the dock station's
+ * _EJ0 method.
+ */
+static void eject_dock(struct dock_station *ds)
+{
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+
+	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &buffer);
+	obj = buffer.pointer;
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+
+	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
+					      &arg_list, NULL))
+	    || dock_present(ds))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to eject dock!\n",
+				  obj->string.pointer));
+
+	kfree(buffer.pointer);
+}
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static acpi_status handle_dock(struct dock_station *ds, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	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");
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = dock;
+	status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "%s: failed to execute _DCK\n",
+				  obj->string.pointer));
+	kfree(buffer.pointer);
+	kfree(name_buffer.pointer);
+
+	return status;
+}
+
+static inline void dock(struct dock_station *ds)
+{
+	handle_dock(ds, 1);
+}
+
+static inline void undock(struct dock_station *ds)
+{
+	handle_dock(ds, 0);
+}
+
+static inline void begin_dock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_DOCKING;
+}
+
+static inline void complete_dock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_DOCKING);
+	ds->last_dock_time = jiffies;
+}
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason.  Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+	if ((ds->flags & DOCK_DOCKING) ||
+	    time_before(jiffies, (ds->last_dock_time + HZ)))
+		return 1;
+	return 0;
+}
+
+/**
+ * register_dock_notifier - add yourself to the dock notifier list
+ * @nb: the callers notifier block
+ *
+ * If a driver wishes to be notified about dock events, they can
+ * use this function to put a notifier block on the dock notifier list.
+ * this notifier call chain will be called after a dock event, but
+ * before hotplugging any new devices.
+ */
+int register_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(register_dock_notifier);
+
+/**
+ * unregister_dock_notifier - remove yourself from the dock notifier list
+ * @nb: the callers notifier block
+ */
+int unregister_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(unregister_dock_notifier);
+
+/**
+ * register_hotplug_dock_device - register a hotplug function
+ * @handle: the handle of the device
+ * @handler: the acpi_notifier_handler to call after docking
+ * @context: device specific data
+ *
+ * If a driver would like to perform a hotplug operation after a dock
+ * event, they can register an acpi_notifiy_handler to be called by
+ * the dock driver after _DCK is executed.
+ */
+acpi_status
+register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
+			     void *context)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return AE_ERROR;
+
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * this would include the dock station itself
+	 */
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd) {
+		dd->handler = handler;
+		dd->context = context;
+		dock_add_hotplug_device(dock_station, dd);
+		return AE_OK;
+	}
+
+	return AE_ERROR;
+}
+
+EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
+
+/**
+ * unregister_hotplug_dock_device - remove yourself from the hotplug list
+ * @handle: the acpi handle of the device
+ */
+acpi_status unregister_hotplug_dock_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return AE_ERROR;
+
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd)
+		dock_del_hotplug_device(dock_station, dd);
+	return AE_OK;
+}
+
+EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
+
+/**
+ * acpi_dock_notify - act upon an acpi dock notification
+ * @handle: the dock station handle
+ * @event: the acpi event
+ * @data: our driver data struct
+ *
+ * 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.
+ */
+static void acpi_dock_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct dock_station *ds = (struct dock_station *)data;
+	struct acpi_device *device;
+
+	switch (event) {
+	case ACPI_NOTIFY_BUS_CHECK:
+	case ACPI_NOTIFY_DEVICE_CHECK:
+		if (!dock_in_progress(ds) && dock_present(ds)) {
+			begin_dock(ds);
+			dock(ds);
+			if (!dock_present(ds)) {
+				printk(KERN_ERR PREFIX "Unable to dock!\n");
+				break;
+			}
+			atomic_notifier_call_chain(&dock_notifier_list,
+						   event, NULL);
+			hotplug_dock_devices(ds, event);
+			complete_dock(ds);
+			if (acpi_bus_get_device(ds->handle, &device))
+				acpi_bus_generate_event(device, event, 0);
+		}
+		break;
+	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.
+			 */
+			if (acpi_bus_get_device(ds->handle, &device))
+				acpi_bus_generate_event(device, event, 0);
+
+			hotplug_dock_devices(ds, event);
+			undock(ds);
+			eject_dock(ds);
+			if (dock_present(ds))
+				printk(KERN_ERR PREFIX "Unable to undock!\n");
+		}
+		break;
+	default:
+		printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
+	}
+}
+
+/**
+ * find_dock_devices - find devices on the dock station
+ * @handle: the handle of the device we are examining
+ * @lvl: unused
+ * @context: the dock station private data
+ * @rv: unused
+ *
+ * This function is called by acpi_walk_namespace.  It will
+ * check to see if an object has an _EJD method.  If it does, then it
+ * will see if it is dependent on the dock station.
+ */
+static acpi_status
+find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_station *ds = (struct dock_station *)context;
+	struct dock_dependent_device *dd;
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return AE_OK;
+
+	if (tmp == ds->handle) {
+		dd = alloc_dock_dependent_device(handle);
+		if (dd)
+			add_dock_dependent_device(ds, dd);
+	}
+
+	return AE_OK;
+}
+
+/**
+ * acpi_dock_add - add a new dock station
+ * @handle: the dock station handle
+ *
+ * allocated and initialize a new dock station device.  Find all devices
+ * that are on the dock station, and register for dock event notifications.
+ */
+static int acpi_dock_add(acpi_handle handle)
+{
+	int ret;
+	acpi_status status;
+	struct dock_dependent_device *dd;
+
+	/* allocate & initialize the dock_station private data */
+	dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
+	if (!dock_station)
+		return -ENOMEM;
+	dock_station->handle = handle;
+	dock_station->last_dock_time = jiffies - HZ;
+	INIT_LIST_HEAD(&dock_station->dependent_devices);
+	INIT_LIST_HEAD(&dock_station->hotplug_devices);
+	spin_lock_init(&dock_station->dd_lock);
+	spin_lock_init(&dock_station->hp_lock);
+
+	/* Find dependent devices */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock_devices, dock_station,
+			    NULL);
+
+	/* add the dock station as a device dependent on itself */
+	dd = alloc_dock_dependent_device(handle);
+	if (!dd) {
+		kfree(dock_station);
+		return -ENOMEM;
+	}
+	add_dock_dependent_device(dock_station, dd);
+
+	/* register for dock events */
+	status = acpi_install_notify_handler(dock_station->handle,
+					     ACPI_SYSTEM_NOTIFY,
+					     acpi_dock_notify, dock_station);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing notify handler\n"));
+		ret = -ENODEV;
+		goto dock_add_err;
+	}
+
+	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
+
+	return 0;
+
+dock_add_err:
+	kfree(dock_station);
+	kfree(dd);
+	return ret;
+}
+
+/**
+ * acpi_dock_remove - free up resources related to the dock station
+ */
+static int acpi_dock_remove(void)
+{
+	struct dock_dependent_device *dd, *tmp;
+	acpi_status status;
+
+	if (!dock_station)
+		return 0;
+
+	/* remove dependent devices */
+	list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
+				 list)
+	    kfree(dd);
+
+	/* remove dock notify handler */
+	status = acpi_remove_notify_handler(dock_station->handle,
+					    ACPI_SYSTEM_NOTIFY,
+					    acpi_dock_notify);
+	if (ACPI_FAILURE(status))
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error removing notify handler\n"));
+
+	/* free dock station memory */
+	kfree(dock_station);
+	return 0;
+}
+
+/**
+ * find_dock - look for a dock station
+ * @handle: acpi handle of a device
+ * @lvl: unused
+ * @context: counter of dock stations found
+ * @rv: unused
+ *
+ * This is called by acpi_walk_namespace to look for dock stations.
+ */
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+	acpi_status status = AE_OK;
+
+	if (is_dock(handle)) {
+		if (acpi_dock_add(handle) >= 0) {
+			(*count)++;
+			status = AE_CTRL_TERMINATE;
+		}
+	}
+	return status;
+}
+
+static int __init acpi_dock_init(void)
+{
+	int num = 0;
+
+	dock_station = NULL;
+
+	/* look for a dock station */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	if (!num)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void __exit acpi_dock_exit(void)
+{
+	acpi_dock_remove();
+}
+
+module_init(acpi_dock_init);
+module_exit(acpi_dock_exit);
--- 2.6-git-kca2.orig/drivers/acpi/Kconfig
+++ 2.6-git-kca2/drivers/acpi/Kconfig
@@ -134,6 +134,13 @@ config ACPI_FAN
 	  This driver adds support for ACPI fan devices, allowing user-mode 
 	  applications to perform basic fan control (on, off, status).
 
+config ACPI_DOCK
+	tristate "Dock"
+	depends on !ACPI_IBM_DOCK
+	default y
+	help
+	  This driver adds support for ACPI controlled docking stations
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	default y
--- 2.6-git-kca2.orig/drivers/acpi/Makefile
+++ 2.6-git-kca2/drivers/acpi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
+obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o 
 obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
--- 2.6-git-kca2.orig/drivers/acpi/scan.c
+++ 2.6-git-kca2/drivers/acpi/scan.c
@@ -703,6 +703,29 @@ static int acpi_bus_find_driver(struct a
                                  Device Enumeration
    -------------------------------------------------------------------------- */
 
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		obj = buffer.pointer;
+		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+		acpi_os_free(buffer.pointer);
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+
 static int acpi_bus_get_flags(struct acpi_device *device)
 {
 	acpi_status status = AE_OK;
--- 2.6-git-kca2.orig/include/acpi/acpi_bus.h
+++ 2.6-git-kca2/include/acpi/acpi_bus.h
@@ -332,7 +332,7 @@ int acpi_bus_add(struct acpi_device **ch
 		 acpi_handle handle, int type);
 int acpi_bus_trim(struct acpi_device *start, int rmdevice);
 int acpi_bus_start(struct acpi_device *device);
-
+acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd);
 int acpi_match_ids(struct acpi_device *device, char *ids);
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
--- 2.6-git-kca2.orig/include/acpi/acpi_drivers.h
+++ 2.6-git-kca2/include/acpi/acpi_drivers.h
@@ -110,4 +110,13 @@ int acpi_processor_set_thermal_limit(acp
 
 extern int acpi_specific_hotkey_enabled;
 
+/*--------------------------------------------------------------------------
+                                  Dock Station
+  -------------------------------------------------------------------------- */
+extern int is_dock_device(acpi_handle handle);
+extern int register_dock_notifier(struct notifier_block *nb);
+extern int unregister_dock_notifier(struct notifier_block *nb);
+extern acpi_status register_hotplug_dock_device(acpi_handle handle,
+	acpi_notify_handler handler, void *context);
+extern acpi_status unregister_hotplug_dock_device(acpi_handle handle);
 #endif /*__ACPI_DRIVERS_H__*/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-18 18:03         ` Kristen Accardi
@ 2006-04-18 22:54           ` Patrick Mochel
  2006-04-19 17:08             ` Kristen Accardi
  2006-04-28 23:51           ` [patch 1/3] acpi: dock driver v3 Kristen Accardi
  1 sibling, 1 reply; 24+ messages in thread
From: Patrick Mochel @ 2006-04-18 22:54 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: Prarit Bhargava, Andrew Morton, len.brown, greg, linux-acpi,
	pcihpd-discuss, linux-kernel, arjan, muneda.takahiro, pavel,
	temnota

On Tue, Apr 18, 2006 at 11:03:16AM -0700, Kristen Accardi wrote:
> Create a driver which lives in the acpi subsystem to handle dock events.  This 
> driver is not an acpi driver, because acpi drivers require that the object
> be present when the driver is loaded.

A few comments.. 


> --- /dev/null
> +++ 2.6-git-kca2/drivers/acpi/dock.c
> @@ -0,0 +1,652 @@

> +#define ACPI_DOCK_COMPONENT 0x10000000
> +#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
> +#define _COMPONENT		ACPI_DOCK_COMPONENT

These aren't necessary for code that is outside of the ACPI-CA. 

> +struct dock_station {
> +	acpi_handle handle;
> +	unsigned long last_dock_time;
> +	u32 flags;
> +	spinlock_t dd_lock;
> +	spinlock_t hp_lock;
> +	struct list_head dependent_devices;
> +	struct list_head hotplug_devices;
> +};
> +
> +struct dock_dependent_device {
> +	struct list_head list;
> +	struct list_head hotplug_list;
> +	acpi_handle handle;
> +	acpi_notify_handler handler;
> +	void *context;
> +};
> +
> +#define DOCK_DOCKING	0x00000001
> +
> +static struct dock_station *dock_station;

Does this need to be dynamically allocated? Static initialization
would be a bit cleaner and obviate the need for the NULL checks in
several of the functions below. 

> +/**
> + * eject_dock - respond to a dock eject request
> + * @ds: the dock station
> + *
> + * This is called after _DCK is called, to execute the dock station's
> + * _EJ0 method.
> + */
> +static void eject_dock(struct dock_station *ds)
> +{
> +	struct acpi_object_list arg_list;
> +	union acpi_object arg;
> +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> +	union acpi_object *obj;
> +
> +	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &buffer);
> +	obj = buffer.pointer;
> +
> +	arg_list.count = 1;
> +	arg_list.pointer = &arg;
> +	arg.type = ACPI_TYPE_INTEGER;
> +	arg.integer.value = 1;

Minor nit (that is replicated in many of the ACPI drivers). This can be
done by just describing the data better: 

	struct acpi_object arg = {
		.type		= ACPI_TYPE_INTEGER,
		.integer	= {
			.value	= 1,
		},
	};
	struct acpi_object_list arg_list = {
		.count		= 1,
		.pointer	= &arg,
	};

	... 

In the long run, since the same exact code exists in dozens of places
in the ACPI drivers, there should just be a helper for it. E.g.: 


	int ret;
	unsigned long value;

	ret = acpi_get_int(ds->handle, "_EJO", &value);
	if (!ret)
		/* Use Value */
	else
		/* Error */

...and get rid of the awkward object/object list handling. 

> +static inline void begin_dock(struct dock_station *ds)
> +{
> +	ds->flags |= DOCK_DOCKING;
> +}
> +
> +static inline void complete_dock(struct dock_station *ds)
> +{
> +	ds->flags &= ~(DOCK_DOCKING);
> +	ds->last_dock_time = jiffies;
> +}
> +
> +/**
> + * dock_in_progress - see if we are in the middle of handling a dock event
> + * @ds: the dock station
> + *
> + * Sometimes while docking, false dock events can be sent to the driver
> + * because good connections aren't made or some other reason.  Ignore these
> + * if we are in the middle of doing something.
> + */
> +static int dock_in_progress(struct dock_station *ds)
> +{
> +	if ((ds->flags & DOCK_DOCKING) ||
> +	    time_before(jiffies, (ds->last_dock_time + HZ)))
> +		return 1;
> +	return 0;
> +}

These seem racy. It seems the flag should should at least be an atomic_t. But,
if it's that, then it might as well be a mutex, eh? And, if it's a mutex, then
do we need the other spinlocks? 

> +acpi_status
> +register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
> +			     void *context)

If this is called from outside drivers/acpi/, you should return an int with a 
real errno value. The AE_* values shouldn't be used outside of the ACPI CA. 


> +acpi_status unregister_hotplug_dock_device(acpi_handle handle)

Does unregister need to return an error? 


Thanks,


	Pat

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-18 22:54           ` Patrick Mochel
@ 2006-04-19 17:08             ` Kristen Accardi
  2006-04-19 17:28               ` Patrick Mochel
  0 siblings, 1 reply; 24+ messages in thread
From: Kristen Accardi @ 2006-04-19 17:08 UTC (permalink / raw)
  To: Patrick Mochel
  Cc: Prarit Bhargava, Andrew Morton, len.brown, greg, linux-acpi,
	pcihpd-discuss, linux-kernel, arjan, muneda.takahiro, pavel,
	temnota

On Tue, 2006-04-18 at 15:54 -0700, Patrick Mochel wrote:
> On Tue, Apr 18, 2006 at 11:03:16AM -0700, Kristen Accardi wrote:
> > Create a driver which lives in the acpi subsystem to handle dock events.  This 
> > driver is not an acpi driver, because acpi drivers require that the object
> > be present when the driver is loaded.
> 
> A few comments.. 
> 
> 
> > --- /dev/null
> > +++ 2.6-git-kca2/drivers/acpi/dock.c
> > @@ -0,0 +1,652 @@
> 
> > +#define ACPI_DOCK_COMPONENT 0x10000000
> > +#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
> > +#define _COMPONENT		ACPI_DOCK_COMPONENT
> 
> These aren't necessary for code that is outside of the ACPI-CA. 

Originally I did not include these, but it turns out if you wish to use
the ACPI_DEBUG macro, you need to have these things defined.  I did go
ahead and use this macro in a couple places, mainly because I felt that
even though this isn't strictly an acpi driver (using the acpi driver
model), it does live in drivers/acpi and perhaps people might expect to
be able to debug it the same way.

> 
> > +struct dock_station {
> > +	acpi_handle handle;
> > +	unsigned long last_dock_time;
> > +	u32 flags;
> > +	spinlock_t dd_lock;
> > +	spinlock_t hp_lock;
> > +	struct list_head dependent_devices;
> > +	struct list_head hotplug_devices;
> > +};
> > +
> > +struct dock_dependent_device {
> > +	struct list_head list;
> > +	struct list_head hotplug_list;
> > +	acpi_handle handle;
> > +	acpi_notify_handler handler;
> > +	void *context;
> > +};
> > +
> > +#define DOCK_DOCKING	0x00000001
> > +
> > +static struct dock_station *dock_station;
> 
> Does this need to be dynamically allocated? Static initialization
> would be a bit cleaner and obviate the need for the NULL checks in
> several of the functions below. 
> 

It could be statically allocated, but I have a preference towards not
allocating statically in this case.  I will consider the option of
making it static.

> > +/**
> > + * eject_dock - respond to a dock eject request
> > + * @ds: the dock station
> > + *
> > + * This is called after _DCK is called, to execute the dock station's
> > + * _EJ0 method.
> > + */
> > +static void eject_dock(struct dock_station *ds)
> > +{
> > +	struct acpi_object_list arg_list;
> > +	union acpi_object arg;
> > +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> > +	union acpi_object *obj;
> > +
> > +	acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &buffer);
> > +	obj = buffer.pointer;
> > +
> > +	arg_list.count = 1;
> > +	arg_list.pointer = &arg;
> > +	arg.type = ACPI_TYPE_INTEGER;
> > +	arg.integer.value = 1;
> 
> Minor nit (that is replicated in many of the ACPI drivers). This can be
> done by just describing the data better: 
> 
> 	struct acpi_object arg = {
> 		.type		= ACPI_TYPE_INTEGER,
> 		.integer	= {
> 			.value	= 1,
> 		},
> 	};
> 	struct acpi_object_list arg_list = {
> 		.count		= 1,
> 		.pointer	= &arg,
> 	};
> 
> 	... 
> 
> In the long run, since the same exact code exists in dozens of places
> in the ACPI drivers, there should just be a helper for it. E.g.: 
> 
> 
> 	int ret;
> 	unsigned long value;
> 
> 	ret = acpi_get_int(ds->handle, "_EJO", &value);
> 	if (!ret)
> 		/* Use Value */
> 	else
> 		/* Error */
> 
> ...and get rid of the awkward object/object list handling. 
> 
> > +static inline void begin_dock(struct dock_station *ds)
> > +{
> > +	ds->flags |= DOCK_DOCKING;
> > +}
> > +
> > +static inline void complete_dock(struct dock_station *ds)
> > +{
> > +	ds->flags &= ~(DOCK_DOCKING);
> > +	ds->last_dock_time = jiffies;
> > +}
> > +
> > +/**
> > + * dock_in_progress - see if we are in the middle of handling a dock event
> > + * @ds: the dock station
> > + *
> > + * Sometimes while docking, false dock events can be sent to the driver
> > + * because good connections aren't made or some other reason.  Ignore these
> > + * if we are in the middle of doing something.
> > + */
> > +static int dock_in_progress(struct dock_station *ds)
> > +{
> > +	if ((ds->flags & DOCK_DOCKING) ||
> > +	    time_before(jiffies, (ds->last_dock_time + HZ)))
> > +		return 1;
> > +	return 0;
> > +}
> 
> These seem racy. It seems the flag should should at least be an atomic_t. But,
> if it's that, then it might as well be a mutex, eh? And, if it's a mutex, then
> do we need the other spinlocks? 
> 

yes, the flag might be racy.  we do need the other spinlocks however,
because they are locking lists within the dock_station struct, but not
the entire struct (unless I just change to something that locks the
entire struct).

> > +acpi_status
> > +register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
> > +			     void *context)
> 
> If this is called from outside drivers/acpi/, you should return an int with a 
> real errno value. The AE_* values shouldn't be used outside of the ACPI CA. 
> 

Really?  We use these all over the place in drivers/pci/hotplug.  In
fact, you kinda have to use them if you are calling certain acpi
symbols, since they return these types.

For example, here are some functions will return acpi_status that we use
in hotplug land.

pci_osc_control_set() 
acpi_run_oshp()
acpi_walk_namespace requires its use.

I felt that by returning acpi_status I was being consistent with how
other acpi calls acted.  Is this another example of the iceberg that Len
was talking about in a previous email?? (ugh.)


> 
> > +acpi_status unregister_hotplug_dock_device(acpi_handle handle)
> 
> Does unregister need to return an error? 
> 

No probably not.

> 
> Thanks,
> 
> 
> 	Pat

thanks for reviewing again :).

Kristen

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-19 17:08             ` Kristen Accardi
@ 2006-04-19 17:28               ` Patrick Mochel
  2006-04-19 18:28                 ` Kristen Accardi
  0 siblings, 1 reply; 24+ messages in thread
From: Patrick Mochel @ 2006-04-19 17:28 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: Prarit Bhargava, Andrew Morton, len.brown, greg, linux-acpi,
	pcihpd-discuss, linux-kernel, arjan, muneda.takahiro, pavel,
	temnota

On Wed, Apr 19, 2006 at 10:08:57AM -0700, Kristen Accardi wrote:
> On Tue, 2006-04-18 at 15:54 -0700, Patrick Mochel wrote:

> > > +acpi_status
> > > +register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
> > > +			     void *context)
> > 
> > If this is called from outside drivers/acpi/, you should return an int with a 
> > real errno value. The AE_* values shouldn't be used outside of the ACPI CA. 
> > 
> 
> Really?  We use these all over the place in drivers/pci/hotplug.  In
> fact, you kinda have to use them if you are calling certain acpi
> symbols, since they return these types.
> 
> For example, here are some functions will return acpi_status that we use
> in hotplug land.
> 
> pci_osc_control_set() 
> acpi_run_oshp()
> acpi_walk_namespace requires its use.

Well, it's one thing to use a function that returns a non-standard error-value,
but it's another to add more functions that do. :-) 

> I felt that by returning acpi_status I was being consistent with how
> other acpi calls acted.  Is this another example of the iceberg that Len
> was talking about in a previous email?? (ugh.)

I believe so. 

We have a standard, well-defined error namespace that lives in include/*/errno.h.
ACPI defines its own error namespace because it must be portable, and even though
most OSes will define the standard errno values, some do not, so it cannot 
assume that it will be there. I'm not sure why the choice was to redefine similar
error values instead of reusing the errno values, but that's moot at this point..

The only place where the ACPI error values need to be used is in the ACPI CA. The
functions exposed to the OS from the CA will return AE_* because the same source
runs everywhere. However, Linux-specific code doesn't need to do that. It is free
to use Linux-specific error reporting (except in the OSL layer that the CA uses,
because it is expecting well-defined return values, as specified in the CA 
Programmers Reference). 

My standpoint is that Linux-specific code should not be using any ACPI CAisms at
all because since the code is Linux-specific, it doesn't need to be portable in 
the same manner that the CA is. This is true for all of drivers/acpi/*.c, with the
exception of drivers/acpi/osl.c, but even some of that source can be cleaned up
to be more Linux-friendly.

Further rationale is that there is no way to enforce the CAisms in Linux-specific
code. You will frequently find mixed return values. Sometimes a function is 
declared as 

	acpi_status acpi_foo()

and return -1 and 0. Or vice versa. 

The ACPI drivers were initially written in the same style that the CA was written,
which makes it confusing when you look at them. But, they don't need to be that
way. They can look like real Linux drivers and become a lot more palatable. 

Eventually, all of the CAisms should be pushed down to the thin layer that sits 
above the interpreter. All exported functions should return ints, and those that
deal directly with the CA interface should simply translate the AE_* error
values into an errno return. 

Thanks,


	Pat


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-19 18:28                 ` Kristen Accardi
@ 2006-04-19 18:20                   ` Patrick Mochel
  0 siblings, 0 replies; 24+ messages in thread
From: Patrick Mochel @ 2006-04-19 18:20 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: Andrew Morton, len.brown, greg, linux-acpi, pcihpd-discuss,
	linux-kernel, arjan, muneda.takahiro, pavel, temnota

On Wed, Apr 19, 2006 at 11:28:42AM -0700, Kristen Accardi wrote:

> Well, I will certainly change the dock code, but pulling this stuff out
> of the hotplug drivers will take longer since it would require changing
> the offending acpi interfaces.  

If you ever decide to do this, you can make it much easier on yourself 
by doing a "bait and switch": create a new function that wraps the
currently exported one and exposes a clean, sane interface. Then, 
convert the users to call this new function. When they're all converted,
un-export the old one, then take it out back and do away with it. This
allows a step-by-step conversion without having to coordinate a big
"flag day" cutover.. 


	Pat

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver
  2006-04-19 17:28               ` Patrick Mochel
@ 2006-04-19 18:28                 ` Kristen Accardi
  2006-04-19 18:20                   ` Patrick Mochel
  0 siblings, 1 reply; 24+ messages in thread
From: Kristen Accardi @ 2006-04-19 18:28 UTC (permalink / raw)
  To: Patrick Mochel
  Cc: Prarit Bhargava, Andrew Morton, len.brown, greg, linux-acpi,
	pcihpd-discuss, linux-kernel, arjan, muneda.takahiro, pavel,
	temnota

On Wed, 2006-04-19 at 10:28 -0700, Patrick Mochel wrote:
> On Wed, Apr 19, 2006 at 10:08:57AM -0700, Kristen Accardi wrote:
> > On Tue, 2006-04-18 at 15:54 -0700, Patrick Mochel wrote:
> 
> > > > +acpi_status
> > > > +register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
> > > > +			     void *context)
> > > 
> > > If this is called from outside drivers/acpi/, you should return an int with a 
> > > real errno value. The AE_* values shouldn't be used outside of the ACPI CA. 
> > > 
> > 
> > Really?  We use these all over the place in drivers/pci/hotplug.  In
> > fact, you kinda have to use them if you are calling certain acpi
> > symbols, since they return these types.
> > 
> > For example, here are some functions will return acpi_status that we use
> > in hotplug land.
> > 
> > pci_osc_control_set() 
> > acpi_run_oshp()
> > acpi_walk_namespace requires its use.
> 
> Well, it's one thing to use a function that returns a non-standard error-value,
> but it's another to add more functions that do. :-) 
> 
> > I felt that by returning acpi_status I was being consistent with how
> > other acpi calls acted.  Is this another example of the iceberg that Len
> > was talking about in a previous email?? (ugh.)
> 
> I believe so. 
> 
> We have a standard, well-defined error namespace that lives in include/*/errno.h.
> ACPI defines its own error namespace because it must be portable, and even though
> most OSes will define the standard errno values, some do not, so it cannot 
> assume that it will be there. I'm not sure why the choice was to redefine similar
> error values instead of reusing the errno values, but that's moot at this point..
> 
> The only place where the ACPI error values need to be used is in the ACPI CA. The
> functions exposed to the OS from the CA will return AE_* because the same source
> runs everywhere. However, Linux-specific code doesn't need to do that. It is free
> to use Linux-specific error reporting (except in the OSL layer that the CA uses,
> because it is expecting well-defined return values, as specified in the CA 
> Programmers Reference). 
> 
> My standpoint is that Linux-specific code should not be using any ACPI CAisms at
> all because since the code is Linux-specific, it doesn't need to be portable in 
> the same manner that the CA is. This is true for all of drivers/acpi/*.c, with the
> exception of drivers/acpi/osl.c, but even some of that source can be cleaned up
> to be more Linux-friendly.
> 
> Further rationale is that there is no way to enforce the CAisms in Linux-specific
> code. You will frequently find mixed return values. Sometimes a function is 
> declared as 
> 
> 	acpi_status acpi_foo()
> 
> and return -1 and 0. Or vice versa. 
> 
> The ACPI drivers were initially written in the same style that the CA was written,
> which makes it confusing when you look at them. But, they don't need to be that
> way. They can look like real Linux drivers and become a lot more palatable. 
> 
> Eventually, all of the CAisms should be pushed down to the thin layer that sits 
> above the interpreter. All exported functions should return ints, and those that
> deal directly with the CA interface should simply translate the AE_* error
> values into an errno return. 
> 
> Thanks,
> 
> 
> 	Pat

Well, I will certainly change the dock code, but pulling this stuff out
of the hotplug drivers will take longer since it would require changing
the offending acpi interfaces.  

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver v3
  2006-04-18 18:03         ` Kristen Accardi
  2006-04-18 22:54           ` Patrick Mochel
@ 2006-04-28 23:51           ` Kristen Accardi
  2006-05-11 18:45             ` [patch 1/3] acpi: dock driver v4 Kristen Accardi
  1 sibling, 1 reply; 24+ messages in thread
From: Kristen Accardi @ 2006-04-28 23:51 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Prarit Bhargava, len.brown, greg, linux-acpi, pcihpd-discuss,
	linux-kernel, mochel, arjan, muneda.takahiro, pavel, temnota

dock driver v3

Create a driver which lives in the acpi subsystem to handle dock events.  This 
driver is not an acpi driver, because acpi drivers require that the object
be present when the driver is loaded.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---

Changes from last version:
1.  Fix build problem so that acpiphp can be built without CONFIG_ACPI_DOCK
2.  remove all acpi debug messages
3.  change exported APIs to not use ACPI-CA isms
4.  remove return values from unregister functions
5.  Handle ACPI_DEVICE_CHECK properly.  When this is sent on an object with 
    _DCK, this should be treated as an undock request.
6.  Remove bus_event generation, as this is causing problems on some systems.
    I will add this back in after I figure out what was wrong with it.

Kristen


 drivers/acpi/Kconfig        |    7 
 drivers/acpi/Makefile       |    1 
 drivers/acpi/dock.c         |  650 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c         |   23 +
 include/acpi/acpi_bus.h     |    2 
 include/acpi/acpi_drivers.h |   17 +
 6 files changed, 699 insertions(+), 1 deletion(-)

--- /dev/null
+++ 2.6-git-kca2/drivers/acpi/dock.c
@@ -0,0 +1,650 @@
+/*
+ *  dock.c - ACPI dock station driver
+ *
+ *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
+
+ACPI_MODULE_NAME("dock")
+MODULE_AUTHOR("Kristen Carlson Accardi");
+MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static struct atomic_notifier_head dock_notifier_list;
+
+struct dock_station {
+	acpi_handle handle;
+	unsigned long last_dock_time;
+	u32 flags;
+	spinlock_t dd_lock;
+	spinlock_t hp_lock;
+	struct list_head dependent_devices;
+	struct list_head hotplug_devices;
+};
+
+struct dock_dependent_device {
+	struct list_head list;
+	struct list_head hotplug_list;
+	acpi_handle handle;
+	acpi_notify_handler handler;
+	void *context;
+};
+
+#define DOCK_DOCKING	0x00000001
+
+static struct dock_station *dock_station;
+
+/*****************************************************************************
+ *                         Dock Dependent device functions                   *
+ *****************************************************************************/
+/**
+ *  alloc_dock_dependent_device - allocate and init a dependent device
+ *  @handle: the acpi_handle of the dependent device
+ *
+ *  Allocate memory for a dependent device structure for a device referenced
+ *  by the acpi handle
+ */
+static struct dock_dependent_device *
+alloc_dock_dependent_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd) {
+		dd->handle = handle;
+		INIT_LIST_HEAD(&dd->list);
+		INIT_LIST_HEAD(&dd->hotplug_list);
+	}
+	return dd;
+}
+
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static void
+add_dock_dependent_device(struct dock_station *ds,
+			  struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->dd_lock);
+	list_add_tail(&dd->list, &ds->dependent_devices);
+	spin_unlock(&ds->dd_lock);
+}
+
+/**
+ * dock_add_hotplug_device - associate a hotplug handler with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device struct
+ *
+ * Add the dependent device to the dock's hotplug device list
+ */
+static void
+dock_add_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * dock_del_hotplug_device - remove a hotplug handler from the dock station
+ * @ds: The dock station
+ * @dd: the dependent device struct
+ *
+ * Delete the dependent device from the dock's hotplug device list
+ */
+static void
+dock_del_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_del(&dd->hotplug_list);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @handle: the acpi_handle of the device we want
+ *
+ * iterate over the dependent device list for this dock.  If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->dd_lock);
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (handle == dd->handle) {
+			spin_unlock(&ds->dd_lock);
+			return dd;
+		}
+	}
+	spin_unlock(&ds->dd_lock);
+	return NULL;
+}
+
+/*****************************************************************************
+ *                         Dock functions                                    *
+ *****************************************************************************/
+/**
+ * is_dock - see if a device is a dock station
+ * @handle: acpi handle of the device
+ *
+ * If an acpi object has a _DCK method, then it is by definition a dock
+ * station, so return true.
+ */
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	return 1;
+}
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @handle: acpi handle of the device
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(acpi_handle handle)
+{
+	if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
+		return 1;
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method.  note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+	unsigned long sta;
+	acpi_status status;
+
+	if (ds) {
+		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+		if (ACPI_SUCCESS(status) && sta)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * hotplug_dock_devices - insert or remove devices on the dock station
+ * @ds: the dock station
+ * @event: either bus check or eject request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_dock_devices(struct dock_station *ds, u32 event)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->hp_lock);
+	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+		if (dd->handler)
+			dd->handler(dd->handle, event, dd->context);
+	}
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * eject_dock - respond to a dock eject request
+ * @ds: the dock station
+ *
+ * This is called after _DCK is called, to execute the dock station's
+ * _EJ0 method.
+ */
+static void eject_dock(struct dock_station *ds)
+{
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	acpi_status status;
+	acpi_handle tmp;
+
+	/* all dock devices should have _EJ0, but check anyway */
+	status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
+	if (ACPI_FAILURE(status)) {
+		pr_debug("No _EJ0 support for dock device\n");
+		return;
+	}
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+
+	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
+					      &arg_list, NULL)))
+		pr_debug("Failed to evaluate _EJ0!\n");
+}
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static acpi_status handle_dock(struct dock_station *ds, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	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");
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	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);
+	kfree(buffer.pointer);
+	kfree(name_buffer.pointer);
+
+	return status;
+}
+
+static inline void dock(struct dock_station *ds)
+{
+	handle_dock(ds, 1);
+}
+
+static inline void undock(struct dock_station *ds)
+{
+	handle_dock(ds, 0);
+}
+
+static inline void begin_dock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_DOCKING;
+}
+
+static inline void complete_dock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_DOCKING);
+	ds->last_dock_time = jiffies;
+}
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason.  Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+	if ((ds->flags & DOCK_DOCKING) ||
+	    time_before(jiffies, (ds->last_dock_time + HZ)))
+		return 1;
+	return 0;
+}
+
+/**
+ * register_dock_notifier - add yourself to the dock notifier list
+ * @nb: the callers notifier block
+ *
+ * If a driver wishes to be notified about dock events, they can
+ * use this function to put a notifier block on the dock notifier list.
+ * this notifier call chain will be called after a dock event, but
+ * before hotplugging any new devices.
+ */
+int register_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(register_dock_notifier);
+
+/**
+ * unregister_dock_notifier - remove yourself from the dock notifier list
+ * @nb: the callers notifier block
+ */
+void unregister_dock_notifier(struct notifier_block *nb)
+{
+	atomic_notifier_chain_unregister(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(unregister_dock_notifier);
+
+/**
+ * register_hotplug_dock_device - register a hotplug function
+ * @handle: the handle of the device
+ * @handler: the acpi_notifier_handler to call after docking
+ * @context: device specific data
+ *
+ * If a driver would like to perform a hotplug operation after a dock
+ * event, they can register an acpi_notifiy_handler to be called by
+ * the dock driver after _DCK is executed.
+ */
+int
+register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
+			     void *context)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return -ENODEV;
+
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * this would include the dock station itself
+	 */
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd) {
+		dd->handler = handler;
+		dd->context = context;
+		dock_add_hotplug_device(dock_station, dd);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
+
+/**
+ * unregister_hotplug_dock_device - remove yourself from the hotplug list
+ * @handle: the acpi handle of the device
+ */
+void unregister_hotplug_dock_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return;
+
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd)
+		dock_del_hotplug_device(dock_station, dd);
+}
+
+EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
+
+/**
+ * acpi_dock_notify - act upon an acpi dock notification
+ * @handle: the dock station handle
+ * @event: the acpi event
+ * @data: our driver data struct
+ *
+ * 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.
+ */
+static void acpi_dock_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct dock_station *ds = (struct dock_station *)data;
+
+	switch (event) {
+	case ACPI_NOTIFY_BUS_CHECK:
+		if (!dock_in_progress(ds) && dock_present(ds)) {
+			begin_dock(ds);
+			dock(ds);
+			if (!dock_present(ds)) {
+				printk(KERN_ERR PREFIX "Unable to dock!\n");
+				break;
+			}
+			atomic_notifier_call_chain(&dock_notifier_list,
+						   event, NULL);
+			hotplug_dock_devices(ds, event);
+			complete_dock(ds);
+			/* TBD - generate an event */
+		}
+		break;
+	case ACPI_NOTIFY_DEVICE_CHECK:
+	/*
+         * According to acpi spec 3.0a, if a DEVICE_CHECK notification
+         * is sent and _DCK is present, it is assumed to mean an
+         * undock request.  This notify routine will only be called
+         * for objects defining _DCK, so we will fall through to eject
+         * request here.  However, we will pass an eject request through
+	 * 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.
+			 */
+			/* TBD - generate an 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");
+		}
+		break;
+	default:
+		printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
+	}
+}
+
+/**
+ * find_dock_devices - find devices on the dock station
+ * @handle: the handle of the device we are examining
+ * @lvl: unused
+ * @context: the dock station private data
+ * @rv: unused
+ *
+ * This function is called by acpi_walk_namespace.  It will
+ * check to see if an object has an _EJD method.  If it does, then it
+ * will see if it is dependent on the dock station.
+ */
+static acpi_status
+find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_station *ds = (struct dock_station *)context;
+	struct dock_dependent_device *dd;
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return AE_OK;
+
+	if (tmp == ds->handle) {
+		dd = alloc_dock_dependent_device(handle);
+		if (dd)
+			add_dock_dependent_device(ds, dd);
+	}
+
+	return AE_OK;
+}
+
+/**
+ * acpi_dock_add - add a new dock station
+ * @handle: the dock station handle
+ *
+ * allocated and initialize a new dock station device.  Find all devices
+ * that are on the dock station, and register for dock event notifications.
+ */
+static int acpi_dock_add(acpi_handle handle)
+{
+	int ret;
+	acpi_status status;
+	struct dock_dependent_device *dd;
+
+	/* allocate & initialize the dock_station private data */
+	dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
+	if (!dock_station)
+		return -ENOMEM;
+	dock_station->handle = handle;
+	dock_station->last_dock_time = jiffies - HZ;
+	INIT_LIST_HEAD(&dock_station->dependent_devices);
+	INIT_LIST_HEAD(&dock_station->hotplug_devices);
+	spin_lock_init(&dock_station->dd_lock);
+	spin_lock_init(&dock_station->hp_lock);
+
+	/* Find dependent devices */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock_devices, dock_station,
+			    NULL);
+
+	/* add the dock station as a device dependent on itself */
+	dd = alloc_dock_dependent_device(handle);
+	if (!dd) {
+		kfree(dock_station);
+		return -ENOMEM;
+	}
+	add_dock_dependent_device(dock_station, dd);
+
+	/* register for dock events */
+	status = acpi_install_notify_handler(dock_station->handle,
+					     ACPI_SYSTEM_NOTIFY,
+					     acpi_dock_notify, dock_station);
+
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Error installing notify handler\n");
+		ret = -ENODEV;
+		goto dock_add_err;
+	}
+
+	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
+
+	return 0;
+
+dock_add_err:
+	kfree(dock_station);
+	kfree(dd);
+	return ret;
+}
+
+/**
+ * acpi_dock_remove - free up resources related to the dock station
+ */
+static int acpi_dock_remove(void)
+{
+	struct dock_dependent_device *dd, *tmp;
+	acpi_status status;
+
+	if (!dock_station)
+		return 0;
+
+	/* remove dependent devices */
+	list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
+				 list)
+	    kfree(dd);
+
+	/* remove dock notify handler */
+	status = acpi_remove_notify_handler(dock_station->handle,
+					    ACPI_SYSTEM_NOTIFY,
+					    acpi_dock_notify);
+	if (ACPI_FAILURE(status))
+		printk(KERN_ERR "Error removing notify handler\n");
+
+	/* free dock station memory */
+	kfree(dock_station);
+	return 0;
+}
+
+/**
+ * find_dock - look for a dock station
+ * @handle: acpi handle of a device
+ * @lvl: unused
+ * @context: counter of dock stations found
+ * @rv: unused
+ *
+ * This is called by acpi_walk_namespace to look for dock stations.
+ */
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+	acpi_status status = AE_OK;
+
+	if (is_dock(handle)) {
+		if (acpi_dock_add(handle) >= 0) {
+			(*count)++;
+			status = AE_CTRL_TERMINATE;
+		}
+	}
+	return status;
+}
+
+static int __init acpi_dock_init(void)
+{
+	int num = 0;
+
+	dock_station = NULL;
+
+	/* look for a dock station */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	if (!num)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void __exit acpi_dock_exit(void)
+{
+	acpi_dock_remove();
+}
+
+module_init(acpi_dock_init);
+module_exit(acpi_dock_exit);
--- 2.6-git-kca2.orig/drivers/acpi/Kconfig
+++ 2.6-git-kca2/drivers/acpi/Kconfig
@@ -134,6 +134,13 @@ config ACPI_FAN
 	  This driver adds support for ACPI fan devices, allowing user-mode 
 	  applications to perform basic fan control (on, off, status).
 
+config ACPI_DOCK
+	tristate "Dock"
+	depends on !ACPI_IBM_DOCK
+	default y
+	help
+	  This driver adds support for ACPI controlled docking stations
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	default y
--- 2.6-git-kca2.orig/drivers/acpi/Makefile
+++ 2.6-git-kca2/drivers/acpi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
+obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o 
 obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
--- 2.6-git-kca2.orig/drivers/acpi/scan.c
+++ 2.6-git-kca2/drivers/acpi/scan.c
@@ -703,6 +703,29 @@ static int acpi_bus_find_driver(struct a
                                  Device Enumeration
    -------------------------------------------------------------------------- */
 
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		obj = buffer.pointer;
+		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+		acpi_os_free(buffer.pointer);
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+
 static int acpi_bus_get_flags(struct acpi_device *device)
 {
 	acpi_status status = AE_OK;
--- 2.6-git-kca2.orig/include/acpi/acpi_bus.h
+++ 2.6-git-kca2/include/acpi/acpi_bus.h
@@ -332,7 +332,7 @@ int acpi_bus_add(struct acpi_device **ch
 		 acpi_handle handle, int type);
 int acpi_bus_trim(struct acpi_device *start, int rmdevice);
 int acpi_bus_start(struct acpi_device *device);
-
+acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd);
 int acpi_match_ids(struct acpi_device *device, char *ids);
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
--- 2.6-git-kca2.orig/include/acpi/acpi_drivers.h
+++ 2.6-git-kca2/include/acpi/acpi_drivers.h
@@ -110,4 +110,21 @@ int acpi_processor_set_thermal_limit(acp
 
 extern int acpi_specific_hotkey_enabled;
 
+/*--------------------------------------------------------------------------
+                                  Dock Station
+  -------------------------------------------------------------------------- */
+#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE)
+extern int is_dock_device(acpi_handle handle);
+extern int register_dock_notifier(struct notifier_block *nb);
+extern void unregister_dock_notifier(struct notifier_block *nb);
+extern int register_hotplug_dock_device(acpi_handle handle,
+	acpi_notify_handler handler, void *context);
+extern void unregister_hotplug_dock_device(acpi_handle handle);
+#else
+#define is_dock_device(h)			(0)
+#define register_dock_notifier(nb) 		(-ENODEV)
+#define unregister_dock_notifier(nb)           	do { } while(0)
+#define register_hotplug_dock_device(h1, h2, c)	(-ENODEV)
+#define unregister_hotplug_dock_device(h)       do { } while(0)
+#endif
 #endif /*__ACPI_DRIVERS_H__*/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 2/3] acpiphp: use new dock driver v2
  2006-04-12 22:18 ` [patch 2/3] acpiphp: use new " Kristen Accardi
  2006-04-12 23:16   ` Christian Trefzer
@ 2006-04-28 23:55   ` Kristen Accardi
  1 sibling, 0 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-04-28 23:55 UTC (permalink / raw)
  To: akpm
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel,
	mochel, arjan, muneda.takahiro, pavel, temnota

Modify the acpiphp driver to use the acpi dock driver for dock notifications.
Only load the acpiphp driver if we find we have pci dock devices

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---
Changes from last version:

* Change acpiphp to use the new return values that were modified on dock v3.

Kristen

 drivers/pci/hotplug/acpiphp_dock.c |  438 -------------------------------------
 drivers/pci/hotplug/Makefile       |    3 
 drivers/pci/hotplug/acpiphp.h      |   35 --
 drivers/pci/hotplug/acpiphp_core.c |   19 -
 drivers/pci/hotplug/acpiphp_glue.c |  111 +++++++--
 5 files changed, 87 insertions(+), 519 deletions(-)

--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp_glue.c
@@ -116,6 +116,59 @@ is_ejectable_slot(acpi_handle handle, u3
 	}
 }
 
+/* callback routine to check for the existance of a pci dock device */
+static acpi_status
+is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+
+	if (is_dock_device(handle)) {
+		(*count)++;
+		return AE_CTRL_TERMINATE;
+	} else {
+		return AE_OK;
+	}
+}
+
+
+
+
+/*
+ * the _DCK method can do funny things... and sometimes not
+ * hah-hah funny.
+ *
+ * TBD - figure out a way to only call fixups for
+ * systems that require them.
+ */
+static int post_dock_fixups(struct notifier_block *nb, unsigned long val,
+	void *v)
+{
+	struct acpiphp_func *func = container_of(nb, struct acpiphp_func, nb);
+	struct pci_bus *bus = func->slot->bridge->pci_bus;
+	u32 buses;
+
+	if (!bus->self)
+		return  NOTIFY_OK;
+
+	/* fixup bad _DCK function that rewrites
+	 * secondary bridge on slot
+	 */
+	pci_read_config_dword(bus->self,
+			PCI_PRIMARY_BUS,
+			&buses);
+
+	if (((buses >> 8) & 0xff) != bus->secondary) {
+		buses = (buses & 0xff000000)
+	     		| ((unsigned int)(bus->primary)     <<  0)
+	     		| ((unsigned int)(bus->secondary)   <<  8)
+	     		| ((unsigned int)(bus->subordinate) << 16);
+		pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
+	}
+	return NOTIFY_OK;
+}
+
+
+
 
 /* callback routine to register each ACPI PCI slot object */
 static acpi_status
@@ -124,7 +177,6 @@ register_slot(acpi_handle handle, u32 lv
 	struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context;
 	struct acpiphp_slot *slot;
 	struct acpiphp_func *newfunc;
-	struct dependent_device *dd;
 	acpi_handle tmp;
 	acpi_status status = AE_OK;
 	unsigned long adr, sun;
@@ -137,7 +189,7 @@ register_slot(acpi_handle handle, u32 lv
 
 	status = acpi_get_handle(handle, "_EJ0", &tmp);
 
-	if (ACPI_FAILURE(status) && !(is_dependent_device(handle)))
+	if (ACPI_FAILURE(status) && !(is_dock_device(handle)))
 		return AE_OK;
 
 	device = (adr >> 16) & 0xffff;
@@ -162,18 +214,8 @@ register_slot(acpi_handle handle, u32 lv
 	if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp)))
 		newfunc->flags |= FUNC_HAS_PS3;
 
-	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) {
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp)))
 		newfunc->flags |= FUNC_HAS_DCK;
-		/* add to devices dependent on dock station,
-		 * because this may actually be the dock bridge
-		 */
-		dd = alloc_dependent_device(handle);
-                if (!dd)
-                        err("Can't allocate memory for "
-				"new dependent device!\n");
-		else
-			add_dependent_device(dd);
-	}
 
 	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
 	if (ACPI_FAILURE(status))
@@ -225,20 +267,23 @@ register_slot(acpi_handle handle, u32 lv
 		slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
 	}
 
-	/* if this is a device dependent on a dock station,
-	 * associate the acpiphp_func to the dependent_device
- 	 * struct.
-	 */
-	if ((dd = get_dependent_device(handle))) {
-		newfunc->flags |= FUNC_IS_DD;
-		/*
-		 * we don't want any devices which is dependent
-		 * on the dock to have it's _EJ0 method executed.
-		 * because we need to run _DCK first.
+	if (is_dock_device(handle)) {
+		/* we don't want to call this device's _EJ0
+		 * because we want the dock notify handler
+		 * to call it after it calls _DCK
 		 */
 		newfunc->flags &= ~FUNC_HAS_EJ0;
-		dd->func = newfunc;
-		add_pci_dependent_device(dd);
+		if (register_hotplug_dock_device(handle,
+			handle_hotplug_event_func, newfunc))
+			dbg("failed to register dock device\n");
+
+		/* we need to be notified when dock events happen
+		 * outside of the hotplug operation, since we may
+		 * need to do fixups before we can hotplug.
+		 */
+		newfunc->nb.notifier_call = post_dock_fixups;
+		if (register_dock_notifier(&newfunc->nb))
+			dbg("failed to register a dock notifier");
 	}
 
 	/* install notify handler */
@@ -277,6 +322,15 @@ static int detect_ejectable_slots(acpi_h
 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1,
 				     is_ejectable_slot, (void *)&count, NULL);
 
+	/*
+	 * we also need to add this bridge if there is a dock bridge or
+	 * other pci device on a dock station (removable)
+	 */
+	if (!count)
+		status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
+				(u32)1, is_pci_dock_device, (void *)&count,
+				NULL);
+
 	return count;
 }
 
@@ -413,8 +467,7 @@ find_p2p_bridge(acpi_handle handle, u32 
 		goto out;
 
 	/* check if this bridge has ejectable slots */
-	if ((detect_ejectable_slots(handle) > 0) ||
-		(detect_dependent_devices(handle) > 0)) {
+	if ((detect_ejectable_slots(handle) > 0)) {
 		dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
 		add_p2p_bridge(handle, dev);
 	}
@@ -522,6 +575,10 @@ static void cleanup_bridge(struct acpiph
 		list_for_each_safe (list, tmp, &slot->funcs) {
 			struct acpiphp_func *func;
 			func = list_entry(list, struct acpiphp_func, sibling);
+			if (is_dock_device(func->handle)) {
+				unregister_hotplug_dock_device(func->handle);
+				unregister_dock_notifier(&func->nb);
+			}
 			if (!(func->flags & FUNC_HAS_DCK)) {
 				status = acpi_remove_notify_handler(func->handle,
 						ACPI_SYSTEM_NOTIFY,
--- 2.6-git-kca2.orig/drivers/pci/hotplug/Makefile
+++ 2.6-git-kca2/drivers/pci/hotplug/Makefile
@@ -40,8 +40,7 @@ ibmphp-objs		:=	ibmphp_core.o	\
 				ibmphp_hpc.o
 
 acpiphp-objs		:=	acpiphp_core.o	\
-				acpiphp_glue.o  \
-				acpiphp_dock.o
+				acpiphp_glue.o
 
 rpaphp-objs		:=	rpaphp_core.o	\
 				rpaphp_pci.o	\
--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp.h
+++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp.h
@@ -125,7 +125,7 @@ struct acpiphp_func {
 
 	struct list_head sibling;
 	struct pci_dev *pci_dev;
-
+	struct notifier_block nb;
 	acpi_handle	handle;
 
 	u8		function;	/* pci function# */
@@ -146,24 +146,6 @@ struct acpiphp_attention_info
 };
 
 
-struct dependent_device {
-	struct list_head device_list;
-	struct list_head pci_list;
-	acpi_handle handle;
-	struct acpiphp_func *func;
-};
-
-
-struct acpiphp_dock_station {
-	acpi_handle handle;
-	u32 last_dock_time;
-	u32 flags;
-	struct acpiphp_func *dock_bridge;
-	struct list_head dependent_devices;
-	struct list_head pci_dependent_devices;
-};
-
-
 /* PCI bus bridge HID */
 #define ACPI_PCI_HOST_HID		"PNP0A03"
 
@@ -202,11 +184,6 @@ struct acpiphp_dock_station {
 #define FUNC_HAS_PS2		(0x00000040)
 #define FUNC_HAS_PS3		(0x00000080)
 #define FUNC_HAS_DCK            (0x00000100)
-#define FUNC_IS_DD              (0x00000200)
-
-/* dock station flags */
-#define DOCK_DOCKING            (0x00000001)
-#define DOCK_HAS_BRIDGE         (0x00000002)
 
 /* function prototypes */
 
@@ -231,16 +208,6 @@ extern u8 acpiphp_get_latch_status (stru
 extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
 extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
 
-/* acpiphp_dock.c */
-extern int find_dock_station(void);
-extern void remove_dock_station(void);
-extern void add_dependent_device(struct dependent_device *new_dd);
-extern void add_pci_dependent_device(struct dependent_device *new_dd);
-extern struct dependent_device *get_dependent_device(acpi_handle handle);
-extern int is_dependent_device(acpi_handle handle);
-extern int detect_dependent_devices(acpi_handle *bridge_handle);
-extern struct dependent_device *alloc_dependent_device(acpi_handle handle);
-
 /* variables */
 extern int acpiphp_debug;
 
--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_core.c
+++ 2.6-git-kca2/drivers/pci/hotplug/acpiphp_core.c
@@ -416,27 +416,12 @@ void acpiphp_unregister_hotplug_slot(str
 
 static int __init acpiphp_init(void)
 {
-	int retval;
-	int docking_station;
-
 	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
 
 	acpiphp_debug = debug;
 
-	docking_station = find_dock_station();
-
 	/* read all the ACPI info from the system */
-	retval = init_acpi();
-
-	/* if we have found a docking station, we should
-	 * go ahead and load even if init_acpi has found
-	 * no slots.  This handles the case when the _DCK
-	 * method not defined under the actual dock bridge
-	 */
-	if (docking_station)
-		return 0;
-	else
-		return retval;
+	return init_acpi();
 }
 
 
@@ -444,8 +429,6 @@ static void __exit acpiphp_exit(void)
 {
 	/* deallocate internal data structures etc. */
 	acpiphp_glue_exit();
-
-	remove_dock_station();
 }
 
 module_init(acpiphp_init);
--- 2.6-git-kca2.orig/drivers/pci/hotplug/acpiphp_dock.c
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * ACPI PCI HotPlug dock functions to ACPI CA subsystem
- *
- * Copyright (C) 2006 Kristen Carlson Accardi (kristen.c.accardi@intel.com)
- * Copyright (C) 2006 Intel Corporation
- *
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Send feedback to <kristen.c.accardi@intel.com>
- *
- */
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/smp_lock.h>
-#include <linux/mutex.h>
-
-#include "../pci.h"
-#include "pci_hotplug.h"
-#include "acpiphp.h"
-
-static struct acpiphp_dock_station *ds;
-#define MY_NAME "acpiphp_dock"
-
-
-int is_dependent_device(acpi_handle handle)
-{
-	return (get_dependent_device(handle) ? 1 : 0);
-}
-
-
-static acpi_status
-find_dependent_device(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	int *count = (int *)context;
-
-	if (is_dependent_device(handle)) {
-		(*count)++;
-		return AE_CTRL_TERMINATE;
-	} else {
-		return AE_OK;
-	}
-}
-
-
-
-
-void add_dependent_device(struct dependent_device *new_dd)
-{
-	list_add_tail(&new_dd->device_list, &ds->dependent_devices);
-}
-
-
-void add_pci_dependent_device(struct dependent_device *new_dd)
-{
-	list_add_tail(&new_dd->pci_list, &ds->pci_dependent_devices);
-}
-
-
-
-struct dependent_device * get_dependent_device(acpi_handle handle)
-{
-	struct dependent_device *dd;
-
-	if (!ds)
-		return NULL;
-
-	list_for_each_entry(dd, &ds->dependent_devices, device_list) {
-		if (handle == dd->handle)
-			return dd;
-	}
-	return NULL;
-}
-
-
-
-struct dependent_device *alloc_dependent_device(acpi_handle handle)
-{
-	struct dependent_device *dd;
-
-	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
-	if (dd) {
-		INIT_LIST_HEAD(&dd->pci_list);
-		INIT_LIST_HEAD(&dd->device_list);
-		dd->handle = handle;
-	}
-	return dd;
-}
-
-
-
-static int is_dock(acpi_handle handle)
-{
-	acpi_status status;
-	acpi_handle tmp;
-
-	status = acpi_get_handle(handle, "_DCK", &tmp);
-	if (ACPI_FAILURE(status)) {
-		return 0;
-	}
-	return 1;
-}
-
-
-
-static int dock_present(void)
-{
-	unsigned long sta;
-	acpi_status status;
-
-	if (ds) {
-		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
-		if (ACPI_SUCCESS(status) && sta)
-			return 1;
-	}
-	return 0;
-}
-
-
-
-static void eject_dock(void)
-{
-	struct acpi_object_list arg_list;
-	union acpi_object arg;
-
-	arg_list.count = 1;
-	arg_list.pointer = &arg;
-	arg.type = ACPI_TYPE_INTEGER;
-	arg.integer.value = 1;
-
-	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
-					&arg_list, NULL)) || dock_present())
-		warn("%s: failed to eject dock!\n", __FUNCTION__);
-
-	return;
-}
-
-
-
-
-static acpi_status handle_dock(int dock)
-{
-	acpi_status status;
-	struct acpi_object_list arg_list;
-	union acpi_object arg;
-	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-
-	dbg("%s: %s\n", __FUNCTION__, dock ? "docking" : "undocking");
-
-	/* _DCK method has one argument */
-	arg_list.count = 1;
-	arg_list.pointer = &arg;
-	arg.type = ACPI_TYPE_INTEGER;
-	arg.integer.value = dock;
-	status = acpi_evaluate_object(ds->handle, "_DCK",
-					&arg_list, &buffer);
-	if (ACPI_FAILURE(status))
-		err("%s: failed to execute _DCK\n", __FUNCTION__);
-	acpi_os_free(buffer.pointer);
-
-	return status;
-}
-
-
-
-static inline void dock(void)
-{
-	handle_dock(1);
-}
-
-
-
-static inline void undock(void)
-{
-	handle_dock(0);
-}
-
-
-
-/*
- * the _DCK method can do funny things... and sometimes not
- * hah-hah funny.
- *
- * TBD - figure out a way to only call fixups for
- * systems that require them.
- */
-static void post_dock_fixups(void)
-{
-	struct pci_bus *bus;
-	u32 buses;
-	struct dependent_device *dd;
-
-	list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list) {
-		bus = dd->func->slot->bridge->pci_bus;
-
-		/* fixup bad _DCK function that rewrites
-	 	 * secondary bridge on slot
-	 	 */
-		pci_read_config_dword(bus->self,
-				PCI_PRIMARY_BUS,
-				&buses);
-
-		if (((buses >> 8) & 0xff) != bus->secondary) {
-			buses = (buses & 0xff000000)
-	     			| ((unsigned int)(bus->primary)     <<  0)
-	     			| ((unsigned int)(bus->secondary)   <<  8)
-	     			| ((unsigned int)(bus->subordinate) << 16);
-			pci_write_config_dword(bus->self,
-					PCI_PRIMARY_BUS,
-					buses);
-		}
-	}
-}
-
-
-
-static void hotplug_pci(u32 type)
-{
-	struct dependent_device *dd;
-
-	list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list)
-		handle_hotplug_event_func(dd->handle, type, dd->func);
-}
-
-
-
-static inline void begin_dock(void)
-{
-	ds->flags |= DOCK_DOCKING;
-}
-
-
-static inline void complete_dock(void)
-{
-	ds->flags &= ~(DOCK_DOCKING);
-	ds->last_dock_time = jiffies;
-}
-
-
-static int dock_in_progress(void)
-{
-	if (ds->flags & DOCK_DOCKING ||
-		ds->last_dock_time == jiffies) {
-		dbg("dock in progress\n");
-		return 1;
-	}
-	return 0;
-}
-
-
-
-static void
-handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
-{
-	dbg("%s: enter\n", __FUNCTION__);
-
-	switch (type) {
-		case ACPI_NOTIFY_BUS_CHECK:
-			dbg("BUS Check\n");
-			if (!dock_in_progress() && dock_present()) {
-				begin_dock();
-				dock();
-				if (!dock_present()) {
-					err("Unable to dock!\n");
-					break;
-				}
-				post_dock_fixups();
-				hotplug_pci(type);
-				complete_dock();
-			}
-			break;
-		case ACPI_NOTIFY_EJECT_REQUEST:
-			dbg("EJECT request\n");
-			if (!dock_in_progress() && dock_present()) {
-				hotplug_pci(type);
-				undock();
-				eject_dock();
-				if (dock_present())
-					err("Unable to undock!\n");
-			}
-			break;
-	}
-}
-
-
-
-
-static acpi_status
-find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	acpi_status status;
-	acpi_handle tmp;
-	acpi_handle dck_handle = (acpi_handle) context;
-	char objname[64];
-	struct acpi_buffer buffer = { .length = sizeof(objname),
-				      .pointer = objname };
-	struct acpi_buffer ejd_buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-	union acpi_object *ejd_obj;
-
-	status = acpi_get_handle(handle, "_EJD", &tmp);
-	if (ACPI_FAILURE(status))
-		return AE_OK;
-
-	/* make sure we are dependent on the dock device,
-	 * by executing the _EJD method, then getting a handle
-	 * to the device referenced by that name.  If that
-	 * device handle is the same handle as the dock station
-	 * handle, then we are a device dependent on the dock station
-	 */
-	acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);
-	status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);
-	if (ACPI_FAILURE(status)) {
-		err("Unable to execute _EJD!\n");
-		goto find_ejd_out;
-	}
-	ejd_obj = ejd_buffer.pointer;
-	status = acpi_get_handle(NULL, ejd_obj->string.pointer, &tmp);
-	if (ACPI_FAILURE(status))
-		goto find_ejd_out;
-
-	if (tmp == dck_handle) {
-		struct dependent_device *dd;
-		dbg("%s: found device dependent on dock\n", __FUNCTION__);
-		dd = alloc_dependent_device(handle);
-		if (!dd) {
-			err("Can't allocate memory for dependent device!\n");
-			goto find_ejd_out;
-		}
-		add_dependent_device(dd);
-	}
-
-find_ejd_out:
-	acpi_os_free(ejd_buffer.pointer);
-	return AE_OK;
-}
-
-
-
-int detect_dependent_devices(acpi_handle *bridge_handle)
-{
-	acpi_status status;
-	int count;
-
-	count = 0;
-
-	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
-					(u32)1, find_dependent_device,
-					(void *)&count, NULL);
-
-	return count;
-}
-
-
-
-
-
-static acpi_status
-find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	int *count = (int *)context;
-
-	if (is_dock(handle)) {
-		dbg("%s: found dock\n", __FUNCTION__);
-		ds = kzalloc(sizeof(*ds), GFP_KERNEL);
-		ds->handle = handle;
-		INIT_LIST_HEAD(&ds->dependent_devices);
-		INIT_LIST_HEAD(&ds->pci_dependent_devices);
-
-		/* look for devices dependent on dock station */
-		acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-			ACPI_UINT32_MAX, find_dock_ejd, handle, NULL);
-
-		acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
-			handle_hotplug_event_dock, ds);
-		(*count)++;
-	}
-
-	return AE_OK;
-}
-
-
-
-
-int find_dock_station(void)
-{
-	int num = 0;
-
-	ds = NULL;
-
-	/* start from the root object, because some laptops define
-	 * _DCK methods outside the scope of PCI (IBM x-series laptop)
-	 */
-	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-			ACPI_UINT32_MAX, find_dock, &num, NULL);
-
-	return num;
-}
-
-
-
-void remove_dock_station(void)
-{
-	struct dependent_device *dd, *tmp;
-	if (ds) {
-		if (ACPI_FAILURE(acpi_remove_notify_handler(ds->handle,
-			ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))
-			err("failed to remove dock notify handler\n");
-
-		/* free all dependent devices */
-		list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
-				device_list)
-			kfree(dd);
-
-		/* no need to touch the pci_dependent_device list,
-		 * cause all memory was freed above
-		 */
-		kfree(ds);
-	}
-}
-
-

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [patch 1/3] acpi: dock driver v4
  2006-04-28 23:51           ` [patch 1/3] acpi: dock driver v3 Kristen Accardi
@ 2006-05-11 18:45             ` Kristen Accardi
  2006-06-01 23:05               ` [patch 1/3] acpi: dock driver v6 Kristen Accardi
  0 siblings, 1 reply; 24+ messages in thread
From: Kristen Accardi @ 2006-05-11 18:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Prarit Bhargava, len.brown, greg, linux-acpi, pcihpd-discuss,
	linux-kernel, mochel, arjan, muneda.takahiro, pavel, temnota

Create a driver which lives in the acpi subsystem to handle dock events.  This 
driver is not an acpi driver, because acpi drivers require that the object
be present when the driver is loaded.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---

Changed from last version:
* added dock uevents.  Because this requires an acpi_device struct, if no
  acpi_device struct exists, the dock_event() will create one via
  acpi_bus_add() first.

 drivers/acpi/Kconfig        |    7 
 drivers/acpi/Makefile       |    1 
 drivers/acpi/dock.c         |  680 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c         |   23 +
 include/acpi/acpi_bus.h     |    2 
 include/acpi/acpi_drivers.h |   17 +
 6 files changed, 729 insertions(+), 1 deletion(-)

--- /dev/null
+++ 2.6-git-kca2/drivers/acpi/dock.c
@@ -0,0 +1,680 @@
+/*
+ *  dock.c - ACPI dock station driver
+ *
+ *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
+
+ACPI_MODULE_NAME("dock")
+MODULE_AUTHOR("Kristen Carlson Accardi");
+MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static struct atomic_notifier_head dock_notifier_list;
+
+struct dock_station {
+	acpi_handle handle;
+	unsigned long last_dock_time;
+	u32 flags;
+	spinlock_t dd_lock;
+	spinlock_t hp_lock;
+	struct list_head dependent_devices;
+	struct list_head hotplug_devices;
+};
+
+struct dock_dependent_device {
+	struct list_head list;
+	struct list_head hotplug_list;
+	acpi_handle handle;
+	acpi_notify_handler handler;
+	void *context;
+};
+
+#define DOCK_DOCKING	0x00000001
+#define DOCK_EVENT	KOBJ_DOCK
+#define UNDOCK_EVENT	KOBJ_UNDOCK
+
+static struct dock_station *dock_station;
+
+/*****************************************************************************
+ *                         Dock Dependent device functions                   *
+ *****************************************************************************/
+/**
+ *  alloc_dock_dependent_device - allocate and init a dependent device
+ *  @handle: the acpi_handle of the dependent device
+ *
+ *  Allocate memory for a dependent device structure for a device referenced
+ *  by the acpi handle
+ */
+static struct dock_dependent_device *
+alloc_dock_dependent_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd) {
+		dd->handle = handle;
+		INIT_LIST_HEAD(&dd->list);
+		INIT_LIST_HEAD(&dd->hotplug_list);
+	}
+	return dd;
+}
+
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static void
+add_dock_dependent_device(struct dock_station *ds,
+			  struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->dd_lock);
+	list_add_tail(&dd->list, &ds->dependent_devices);
+	spin_unlock(&ds->dd_lock);
+}
+
+/**
+ * dock_add_hotplug_device - associate a hotplug handler with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device struct
+ *
+ * Add the dependent device to the dock's hotplug device list
+ */
+static void
+dock_add_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * dock_del_hotplug_device - remove a hotplug handler from the dock station
+ * @ds: The dock station
+ * @dd: the dependent device struct
+ *
+ * Delete the dependent device from the dock's hotplug device list
+ */
+static void
+dock_del_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_del(&dd->hotplug_list);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @handle: the acpi_handle of the device we want
+ *
+ * iterate over the dependent device list for this dock.  If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->dd_lock);
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (handle == dd->handle) {
+			spin_unlock(&ds->dd_lock);
+			return dd;
+		}
+	}
+	spin_unlock(&ds->dd_lock);
+	return NULL;
+}
+
+/*****************************************************************************
+ *                         Dock functions                                    *
+ *****************************************************************************/
+/**
+ * is_dock - see if a device is a dock station
+ * @handle: acpi handle of the device
+ *
+ * If an acpi object has a _DCK method, then it is by definition a dock
+ * station, so return true.
+ */
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	return 1;
+}
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @handle: acpi handle of the device
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(acpi_handle handle)
+{
+	if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
+		return 1;
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method.  note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+	unsigned long sta;
+	acpi_status status;
+
+	if (ds) {
+		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+		if (ACPI_SUCCESS(status) && sta)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * hotplug_dock_devices - insert or remove devices on the dock station
+ * @ds: the dock station
+ * @event: either bus check or eject request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_dock_devices(struct dock_station *ds, u32 event)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->hp_lock);
+	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+		if (dd->handler)
+			dd->handler(dd->handle, event, dd->context);
+	}
+	spin_unlock(&ds->hp_lock);
+}
+
+
+static void dock_event(struct dock_station *ds, u32 event, int num)
+{
+	struct acpi_device *device;
+	struct acpi_device *parent_device;
+	acpi_handle parent;
+	int ret;
+
+	if (acpi_bus_get_device(ds->handle, &device)) {
+		/*
+		 * no device created for this object,
+		 * so we should create one.
+		 */
+		acpi_get_parent(ds->handle, &parent);
+		if (acpi_bus_get_device(parent, &parent_device))
+			parent_device = NULL;
+
+		ret = acpi_bus_add(&device, parent_device, ds->handle,
+			ACPI_BUS_TYPE_DEVICE);
+		if (ret) {
+			pr_debug("error adding bus, %x\n",
+				-ret_val);
+			return;
+		}
+	}
+	kobject_uevent(&device->kobj, num);
+}
+
+
+
+/**
+ * eject_dock - respond to a dock eject request
+ * @ds: the dock station
+ *
+ * This is called after _DCK is called, to execute the dock station's
+ * _EJ0 method.
+ */
+static void eject_dock(struct dock_station *ds)
+{
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	acpi_status status;
+	acpi_handle tmp;
+
+	/* all dock devices should have _EJ0, but check anyway */
+	status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
+	if (ACPI_FAILURE(status)) {
+		pr_debug("No _EJ0 support for dock device\n");
+		return;
+	}
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+
+	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
+					      &arg_list, NULL)))
+		pr_debug("Failed to evaluate _EJ0!\n");
+}
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static void handle_dock(struct dock_station *ds, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	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");
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	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);
+	kfree(buffer.pointer);
+	kfree(name_buffer.pointer);
+}
+
+static inline void dock(struct dock_station *ds)
+{
+	handle_dock(ds, 1);
+}
+
+static inline void undock(struct dock_station *ds)
+{
+	handle_dock(ds, 0);
+}
+
+static inline void begin_dock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_DOCKING;
+}
+
+static inline void complete_dock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_DOCKING);
+	ds->last_dock_time = jiffies;
+}
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason.  Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+	if ((ds->flags & DOCK_DOCKING) ||
+	    time_before(jiffies, (ds->last_dock_time + HZ)))
+		return 1;
+	return 0;
+}
+
+/**
+ * register_dock_notifier - add yourself to the dock notifier list
+ * @nb: the callers notifier block
+ *
+ * If a driver wishes to be notified about dock events, they can
+ * use this function to put a notifier block on the dock notifier list.
+ * this notifier call chain will be called after a dock event, but
+ * before hotplugging any new devices.
+ */
+int register_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(register_dock_notifier);
+
+/**
+ * unregister_dock_notifier - remove yourself from the dock notifier list
+ * @nb: the callers notifier block
+ */
+void unregister_dock_notifier(struct notifier_block *nb)
+{
+	atomic_notifier_chain_unregister(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(unregister_dock_notifier);
+
+/**
+ * register_hotplug_dock_device - register a hotplug function
+ * @handle: the handle of the device
+ * @handler: the acpi_notifier_handler to call after docking
+ * @context: device specific data
+ *
+ * If a driver would like to perform a hotplug operation after a dock
+ * event, they can register an acpi_notifiy_handler to be called by
+ * the dock driver after _DCK is executed.
+ */
+int
+register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
+			     void *context)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return -ENODEV;
+
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * this would include the dock station itself
+	 */
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd) {
+		dd->handler = handler;
+		dd->context = context;
+		dock_add_hotplug_device(dock_station, dd);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
+
+/**
+ * unregister_hotplug_dock_device - remove yourself from the hotplug list
+ * @handle: the acpi handle of the device
+ */
+void unregister_hotplug_dock_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return;
+
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd)
+		dock_del_hotplug_device(dock_station, dd);
+}
+
+EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
+
+/**
+ * dock_notify - act upon an acpi dock notification
+ * @handle: the dock station handle
+ * @event: the acpi event
+ * @data: our driver data struct
+ *
+ * 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.
+ */
+static void dock_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct dock_station *ds = (struct dock_station *)data;
+
+	switch (event) {
+	case ACPI_NOTIFY_BUS_CHECK:
+		if (!dock_in_progress(ds) && dock_present(ds)) {
+			begin_dock(ds);
+			dock(ds);
+			if (!dock_present(ds)) {
+				printk(KERN_ERR PREFIX "Unable to dock!\n");
+				break;
+			}
+			atomic_notifier_call_chain(&dock_notifier_list,
+						   event, NULL);
+			hotplug_dock_devices(ds, event);
+			complete_dock(ds);
+			dock_event(ds, event, DOCK_EVENT);
+		}
+		break;
+	case ACPI_NOTIFY_DEVICE_CHECK:
+	/*
+         * According to acpi spec 3.0a, if a DEVICE_CHECK notification
+         * is sent and _DCK is present, it is assumed to mean an
+         * undock request.  This notify routine will only be called
+         * for objects defining _DCK, so we will fall through to eject
+         * request here.  However, we will pass an eject request through
+	 * 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");
+		}
+		break;
+	default:
+		printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
+	}
+}
+
+/**
+ * find_dock_devices - find devices on the dock station
+ * @handle: the handle of the device we are examining
+ * @lvl: unused
+ * @context: the dock station private data
+ * @rv: unused
+ *
+ * This function is called by acpi_walk_namespace.  It will
+ * check to see if an object has an _EJD method.  If it does, then it
+ * will see if it is dependent on the dock station.
+ */
+static acpi_status
+find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_station *ds = (struct dock_station *)context;
+	struct dock_dependent_device *dd;
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return AE_OK;
+
+	if (tmp == ds->handle) {
+		dd = alloc_dock_dependent_device(handle);
+		if (dd)
+			add_dock_dependent_device(ds, dd);
+	}
+
+	return AE_OK;
+}
+
+/**
+ * dock_add - add a new dock station
+ * @handle: the dock station handle
+ *
+ * allocated and initialize a new dock station device.  Find all devices
+ * that are on the dock station, and register for dock event notifications.
+ */
+static int dock_add(acpi_handle handle)
+{
+	int ret;
+	acpi_status status;
+	struct dock_dependent_device *dd;
+
+	/* allocate & initialize the dock_station private data */
+	dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
+	if (!dock_station)
+		return -ENOMEM;
+	dock_station->handle = handle;
+	dock_station->last_dock_time = jiffies - HZ;
+	INIT_LIST_HEAD(&dock_station->dependent_devices);
+	INIT_LIST_HEAD(&dock_station->hotplug_devices);
+	spin_lock_init(&dock_station->dd_lock);
+	spin_lock_init(&dock_station->hp_lock);
+
+	/* Find dependent devices */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock_devices, dock_station,
+			    NULL);
+
+	/* add the dock station as a device dependent on itself */
+	dd = alloc_dock_dependent_device(handle);
+	if (!dd) {
+		kfree(dock_station);
+		return -ENOMEM;
+	}
+	add_dock_dependent_device(dock_station, dd);
+
+	/* register for dock events */
+	status = acpi_install_notify_handler(dock_station->handle,
+					     ACPI_SYSTEM_NOTIFY,
+					     dock_notify, dock_station);
+
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Error installing notify handler\n");
+		ret = -ENODEV;
+		goto dock_add_err;
+	}
+
+	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
+
+	return 0;
+
+dock_add_err:
+	kfree(dock_station);
+	kfree(dd);
+	return ret;
+}
+
+/**
+ * dock_remove - free up resources related to the dock station
+ */
+static int dock_remove(void)
+{
+	struct dock_dependent_device *dd, *tmp;
+	acpi_status status;
+
+	if (!dock_station)
+		return 0;
+
+	/* remove dependent devices */
+	list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
+				 list)
+	    kfree(dd);
+
+	/* remove dock notify handler */
+	status = acpi_remove_notify_handler(dock_station->handle,
+					    ACPI_SYSTEM_NOTIFY,
+					    dock_notify);
+	if (ACPI_FAILURE(status))
+		printk(KERN_ERR "Error removing notify handler\n");
+
+	/* free dock station memory */
+	kfree(dock_station);
+	return 0;
+}
+
+/**
+ * find_dock - look for a dock station
+ * @handle: acpi handle of a device
+ * @lvl: unused
+ * @context: counter of dock stations found
+ * @rv: unused
+ *
+ * This is called by acpi_walk_namespace to look for dock stations.
+ */
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+	acpi_status status = AE_OK;
+
+	if (is_dock(handle)) {
+		if (dock_add(handle) >= 0) {
+			(*count)++;
+			status = AE_CTRL_TERMINATE;
+		}
+	}
+	return status;
+}
+
+static int __init dock_init(void)
+{
+	int num = 0;
+
+	dock_station = NULL;
+
+	/* look for a dock station */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	if (!num)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void __exit dock_exit(void)
+{
+	dock_remove();
+}
+
+module_init(dock_init);
+module_exit(dock_exit);
--- 2.6-git-kca2.orig/drivers/acpi/Kconfig
+++ 2.6-git-kca2/drivers/acpi/Kconfig
@@ -134,6 +134,13 @@ config ACPI_FAN
 	  This driver adds support for ACPI fan devices, allowing user-mode 
 	  applications to perform basic fan control (on, off, status).
 
+config ACPI_DOCK
+	tristate "Dock"
+	depends on !ACPI_IBM_DOCK
+	default y
+	help
+	  This driver adds support for ACPI controlled docking stations
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	default y
--- 2.6-git-kca2.orig/drivers/acpi/Makefile
+++ 2.6-git-kca2/drivers/acpi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
+obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o 
 obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
--- 2.6-git-kca2.orig/drivers/acpi/scan.c
+++ 2.6-git-kca2/drivers/acpi/scan.c
@@ -703,6 +703,29 @@ static int acpi_bus_find_driver(struct a
                                  Device Enumeration
    -------------------------------------------------------------------------- */
 
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		obj = buffer.pointer;
+		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+		acpi_os_free(buffer.pointer);
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+
 static int acpi_bus_get_flags(struct acpi_device *device)
 {
 	acpi_status status = AE_OK;
--- 2.6-git-kca2.orig/include/acpi/acpi_bus.h
+++ 2.6-git-kca2/include/acpi/acpi_bus.h
@@ -332,7 +332,7 @@ int acpi_bus_add(struct acpi_device **ch
 		 acpi_handle handle, int type);
 int acpi_bus_trim(struct acpi_device *start, int rmdevice);
 int acpi_bus_start(struct acpi_device *device);
-
+acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd);
 int acpi_match_ids(struct acpi_device *device, char *ids);
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
--- 2.6-git-kca2.orig/include/acpi/acpi_drivers.h
+++ 2.6-git-kca2/include/acpi/acpi_drivers.h
@@ -110,4 +110,21 @@ int acpi_processor_set_thermal_limit(acp
 
 extern int acpi_specific_hotkey_enabled;
 
+/*--------------------------------------------------------------------------
+                                  Dock Station
+  -------------------------------------------------------------------------- */
+#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE)
+extern int is_dock_device(acpi_handle handle);
+extern int register_dock_notifier(struct notifier_block *nb);
+extern void unregister_dock_notifier(struct notifier_block *nb);
+extern int register_hotplug_dock_device(acpi_handle handle,
+	acpi_notify_handler handler, void *context);
+extern void unregister_hotplug_dock_device(acpi_handle handle);
+#else
+#define is_dock_device(h)			(0)
+#define register_dock_notifier(nb) 		(-ENODEV)
+#define unregister_dock_notifier(nb)           	do { } while(0)
+#define register_hotplug_dock_device(h1, h2, c)	(-ENODEV)
+#define unregister_hotplug_dock_device(h)       do { } while(0)
+#endif
 #endif /*__ACPI_DRIVERS_H__*/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver v6
  2006-05-11 18:45             ` [patch 1/3] acpi: dock driver v4 Kristen Accardi
@ 2006-06-01 23:05               ` Kristen Accardi
  2006-06-01 23:20                 ` Andrew Morton
  0 siblings, 1 reply; 24+ messages in thread
From: Kristen Accardi @ 2006-06-01 23:05 UTC (permalink / raw)
  To: Andrew Morton
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel, pavel

Subject: acpi: dock driver v6

Create a driver which lives in the acpi subsystem to handle dock events.  This 
driver is not an acpi driver, because acpi drivers require that the object
be present when the driver is loaded.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

---

Changed from last version:
* fixed kernel oops where is_dock_device can be called on systems
  with no dock station from acpiphp and cause panic
* finished _EJD support - dependent devices are now added/removed
  causing any drivers that may exist to be called during a dock.
  Unfortunately, I do not have a system which has drivers for any
  of the dependent devices on my dock, so I can't actually test this.
* changed link order of the driver to make sure dock is searched 
  for prior to any devices querying for it.
* fixed bug where driver would not compile while in debug mode

Open issue: still having problems tracking down a system lockup on x32
  when compiled statically.

 drivers/acpi/Kconfig        |    7 
 drivers/acpi/Makefile       |    1 
 drivers/acpi/dock.c         |  739 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c         |   23 +
 include/acpi/acpi_bus.h     |    2 
 include/acpi/acpi_drivers.h |   17 +
 6 files changed, 788 insertions(+), 1 deletion(-)

--- /dev/null
+++ 2.6-git-kca2/drivers/acpi/dock.c
@@ -0,0 +1,739 @@
+/*
+ *  dock.c - ACPI dock station driver
+ *
+ *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
+
+ACPI_MODULE_NAME("dock")
+MODULE_AUTHOR("Kristen Carlson Accardi");
+MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static struct atomic_notifier_head dock_notifier_list;
+
+struct dock_station {
+	acpi_handle handle;
+	unsigned long last_dock_time;
+	u32 flags;
+	spinlock_t dd_lock;
+	spinlock_t hp_lock;
+	struct list_head dependent_devices;
+	struct list_head hotplug_devices;
+};
+
+struct dock_dependent_device {
+	struct list_head list;
+	struct list_head hotplug_list;
+	acpi_handle handle;
+	acpi_notify_handler handler;
+	void *context;
+};
+
+#define DOCK_DOCKING	0x00000001
+#define DOCK_EVENT	KOBJ_DOCK
+#define UNDOCK_EVENT	KOBJ_UNDOCK
+
+static struct dock_station *dock_station;
+
+/*****************************************************************************
+ *                         Dock Dependent device functions                   *
+ *****************************************************************************/
+/**
+ *  alloc_dock_dependent_device - allocate and init a dependent device
+ *  @handle: the acpi_handle of the dependent device
+ *
+ *  Allocate memory for a dependent device structure for a device referenced
+ *  by the acpi handle
+ */
+static struct dock_dependent_device *
+alloc_dock_dependent_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (dd) {
+		dd->handle = handle;
+		INIT_LIST_HEAD(&dd->list);
+		INIT_LIST_HEAD(&dd->hotplug_list);
+	}
+	return dd;
+}
+
+/**
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device
+ *
+ * Add the dependent device to the dock's dependent device list.
+ */
+static void
+add_dock_dependent_device(struct dock_station *ds,
+			  struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->dd_lock);
+	list_add_tail(&dd->list, &ds->dependent_devices);
+	spin_unlock(&ds->dd_lock);
+}
+
+/**
+ * dock_add_hotplug_device - associate a hotplug handler with the dock station
+ * @ds: The dock station
+ * @dd: The dependent device struct
+ *
+ * Add the dependent device to the dock's hotplug device list
+ */
+static void
+dock_add_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * dock_del_hotplug_device - remove a hotplug handler from the dock station
+ * @ds: The dock station
+ * @dd: the dependent device struct
+ *
+ * Delete the dependent device from the dock's hotplug device list
+ */
+static void
+dock_del_hotplug_device(struct dock_station *ds,
+			struct dock_dependent_device *dd)
+{
+	spin_lock(&ds->hp_lock);
+	list_del(&dd->hotplug_list);
+	spin_unlock(&ds->hp_lock);
+}
+
+/**
+ * find_dock_dependent_device - get a device dependent on this dock
+ * @ds: the dock station
+ * @handle: the acpi_handle of the device we want
+ *
+ * iterate over the dependent device list for this dock.  If the
+ * dependent device matches the handle, return.
+ */
+static struct dock_dependent_device *
+find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->dd_lock);
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (handle == dd->handle) {
+			spin_unlock(&ds->dd_lock);
+			return dd;
+		}
+	}
+	spin_unlock(&ds->dd_lock);
+	return NULL;
+}
+
+/*****************************************************************************
+ *                         Dock functions                                    *
+ *****************************************************************************/
+/**
+ * is_dock - see if a device is a dock station
+ * @handle: acpi handle of the device
+ *
+ * If an acpi object has a _DCK method, then it is by definition a dock
+ * station, so return true.
+ */
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	return 1;
+}
+
+/**
+ * is_dock_device - see if a device is on a dock station
+ * @handle: acpi handle of the device
+ *
+ * If this device is either the dock station itself,
+ * or is a device dependent on the dock station, then it
+ * is a dock device
+ */
+int is_dock_device(acpi_handle handle)
+{
+	if (!dock_station)
+		return 0;
+
+	if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
+		return 1;
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(is_dock_device);
+
+/**
+ * dock_present - see if the dock station is present.
+ * @ds: the dock station
+ *
+ * execute the _STA method.  note that present does not
+ * imply that we are docked.
+ */
+static int dock_present(struct dock_station *ds)
+{
+	unsigned long sta;
+	acpi_status status;
+
+	if (ds) {
+		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
+		if (ACPI_SUCCESS(status) && sta)
+			return 1;
+	}
+	return 0;
+}
+
+
+
+/**
+ * dock_create_acpi_device - add new devices to acpi
+ * @handle - handle of the device to add
+ *
+ *  This function will create a new acpi_device for the given
+ *  handle if one does not exist already.  This should cause
+ *  acpi to scan for drivers for the given devices, and call
+ *  matching driver's add routine.
+ *
+ *  Returns a pointer to the acpi_device corresponding to the handle.
+ */
+static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
+{
+	struct acpi_device *device = NULL;
+	struct acpi_device *parent_device;
+	acpi_handle parent;
+	int ret;
+
+	if (acpi_bus_get_device(handle, &device)) {
+		/*
+		 * no device created for this object,
+		 * so we should create one.
+		 */
+		acpi_get_parent(handle, &parent);
+		if (acpi_bus_get_device(parent, &parent_device))
+			parent_device = NULL;
+
+		ret = acpi_bus_add(&device, parent_device, handle,
+			ACPI_BUS_TYPE_DEVICE);
+		if (ret) {
+			pr_debug("error adding bus, %x\n",
+				-ret);
+			return NULL;
+		}
+	}
+	return device;
+}
+
+/**
+ * dock_remove_acpi_device - remove the acpi_device struct from acpi
+ * @handle - the handle of the device to remove
+ *
+ *  Tell acpi to remove the acpi_device.  This should cause any loaded
+ *  driver to have it's remove routine called.
+ */
+static void dock_remove_acpi_device(acpi_handle handle)
+{
+	struct acpi_device *device;
+	int ret;
+
+	if (acpi_bus_get_device(handle, &device)) {
+		ret = acpi_bus_trim(device, 1);
+		if (ret)
+			pr_debug("error removing bus, %x\n", -ret);
+	}
+}
+
+
+/**
+ * hotplug_dock_devices - insert or remove devices on the dock station
+ * @ds: the dock station
+ * @event: either bus check or eject request
+ *
+ * Some devices on the dock station need to have drivers called
+ * to perform hotplug operations after a dock event has occurred.
+ * Traverse the list of dock devices that have registered a
+ * hotplug handler, and call the handler.
+ */
+static void hotplug_dock_devices(struct dock_station *ds, u32 event)
+{
+	struct dock_dependent_device *dd;
+
+	spin_lock(&ds->hp_lock);
+
+	/*
+	 * First call driver specific hotplug functions
+	 */
+	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+		if (dd->handler)
+			dd->handler(dd->handle, event, dd->context);
+	}
+
+	/*
+	 * Now make sure that an acpi_device is created for each
+	 * dependent device, or removed if this is an eject request.
+	 * This will cause acpi_drivers to be stopped/started if they
+	 * exist
+	 */
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (event == ACPI_NOTIFY_EJECT_REQUEST)
+			dock_remove_acpi_device(dd->handle);
+		else
+			dock_create_acpi_device(dd->handle);
+	}
+	spin_unlock(&ds->hp_lock);
+}
+
+static void dock_event(struct dock_station *ds, u32 event, int num)
+{
+	struct acpi_device *device;
+
+	device = dock_create_acpi_device(ds->handle);
+	if (device)
+		kobject_uevent(&device->kobj, num);
+}
+
+/**
+ * eject_dock - respond to a dock eject request
+ * @ds: the dock station
+ *
+ * This is called after _DCK is called, to execute the dock station's
+ * _EJ0 method.
+ */
+static void eject_dock(struct dock_station *ds)
+{
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	acpi_status status;
+	acpi_handle tmp;
+
+	/* all dock devices should have _EJ0, but check anyway */
+	status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
+	if (ACPI_FAILURE(status)) {
+		pr_debug("No _EJ0 support for dock device\n");
+		return;
+	}
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+
+	if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
+					      &arg_list, NULL)))
+		pr_debug("Failed to evaluate _EJ0!\n");
+}
+
+/**
+ * handle_dock - handle a dock event
+ * @ds: the dock station
+ * @dock: to dock, or undock - that is the question
+ *
+ * Execute the _DCK method in response to an acpi event
+ */
+static void handle_dock(struct dock_station *ds, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	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");
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	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);
+	kfree(buffer.pointer);
+	kfree(name_buffer.pointer);
+}
+
+static inline void dock(struct dock_station *ds)
+{
+	handle_dock(ds, 1);
+}
+
+static inline void undock(struct dock_station *ds)
+{
+	handle_dock(ds, 0);
+}
+
+static inline void begin_dock(struct dock_station *ds)
+{
+	ds->flags |= DOCK_DOCKING;
+}
+
+static inline void complete_dock(struct dock_station *ds)
+{
+	ds->flags &= ~(DOCK_DOCKING);
+	ds->last_dock_time = jiffies;
+}
+
+/**
+ * dock_in_progress - see if we are in the middle of handling a dock event
+ * @ds: the dock station
+ *
+ * Sometimes while docking, false dock events can be sent to the driver
+ * because good connections aren't made or some other reason.  Ignore these
+ * if we are in the middle of doing something.
+ */
+static int dock_in_progress(struct dock_station *ds)
+{
+	if ((ds->flags & DOCK_DOCKING) ||
+	    time_before(jiffies, (ds->last_dock_time + HZ)))
+		return 1;
+	return 0;
+}
+
+/**
+ * register_dock_notifier - add yourself to the dock notifier list
+ * @nb: the callers notifier block
+ *
+ * If a driver wishes to be notified about dock events, they can
+ * use this function to put a notifier block on the dock notifier list.
+ * this notifier call chain will be called after a dock event, but
+ * before hotplugging any new devices.
+ */
+int register_dock_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(register_dock_notifier);
+
+/**
+ * unregister_dock_notifier - remove yourself from the dock notifier list
+ * @nb: the callers notifier block
+ */
+void unregister_dock_notifier(struct notifier_block *nb)
+{
+	atomic_notifier_chain_unregister(&dock_notifier_list, nb);
+}
+
+EXPORT_SYMBOL_GPL(unregister_dock_notifier);
+
+/**
+ * register_hotplug_dock_device - register a hotplug function
+ * @handle: the handle of the device
+ * @handler: the acpi_notifier_handler to call after docking
+ * @context: device specific data
+ *
+ * If a driver would like to perform a hotplug operation after a dock
+ * event, they can register an acpi_notifiy_handler to be called by
+ * the dock driver after _DCK is executed.
+ */
+int
+register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
+			     void *context)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return -ENODEV;
+
+	/*
+	 * make sure this handle is for a device dependent on the dock,
+	 * this would include the dock station itself
+	 */
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd) {
+		dd->handler = handler;
+		dd->context = context;
+		dock_add_hotplug_device(dock_station, dd);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
+
+/**
+ * unregister_hotplug_dock_device - remove yourself from the hotplug list
+ * @handle: the acpi handle of the device
+ */
+void unregister_hotplug_dock_device(acpi_handle handle)
+{
+	struct dock_dependent_device *dd;
+
+	if (!dock_station)
+		return;
+
+	dd = find_dock_dependent_device(dock_station, handle);
+	if (dd)
+		dock_del_hotplug_device(dock_station, dd);
+}
+
+EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
+
+/**
+ * dock_notify - act upon an acpi dock notification
+ * @handle: the dock station handle
+ * @event: the acpi event
+ * @data: our driver data struct
+ *
+ * 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.
+ */
+static void dock_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct dock_station *ds = (struct dock_station *)data;
+
+	switch (event) {
+	case ACPI_NOTIFY_BUS_CHECK:
+		if (!dock_in_progress(ds) && dock_present(ds)) {
+			begin_dock(ds);
+			dock(ds);
+			if (!dock_present(ds)) {
+				printk(KERN_ERR PREFIX "Unable to dock!\n");
+				break;
+			}
+			atomic_notifier_call_chain(&dock_notifier_list,
+						   event, NULL);
+			hotplug_dock_devices(ds, event);
+			complete_dock(ds);
+			dock_event(ds, event, DOCK_EVENT);
+		}
+		break;
+	case ACPI_NOTIFY_DEVICE_CHECK:
+	/*
+         * According to acpi spec 3.0a, if a DEVICE_CHECK notification
+         * is sent and _DCK is present, it is assumed to mean an
+         * undock request.  This notify routine will only be called
+         * for objects defining _DCK, so we will fall through to eject
+         * request here.  However, we will pass an eject request through
+	 * 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");
+		}
+		break;
+	default:
+		printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
+	}
+}
+
+/**
+ * find_dock_devices - find devices on the dock station
+ * @handle: the handle of the device we are examining
+ * @lvl: unused
+ * @context: the dock station private data
+ * @rv: unused
+ *
+ * This function is called by acpi_walk_namespace.  It will
+ * check to see if an object has an _EJD method.  If it does, then it
+ * will see if it is dependent on the dock station.
+ */
+static acpi_status
+find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct dock_station *ds = (struct dock_station *)context;
+	struct dock_dependent_device *dd;
+
+	status = acpi_bus_get_ejd(handle, &tmp);
+	if (ACPI_FAILURE(status))
+		return AE_OK;
+
+	if (tmp == ds->handle) {
+		dd = alloc_dock_dependent_device(handle);
+		if (dd)
+			add_dock_dependent_device(ds, dd);
+	}
+
+	return AE_OK;
+}
+
+/**
+ * dock_add - add a new dock station
+ * @handle: the dock station handle
+ *
+ * allocated and initialize a new dock station device.  Find all devices
+ * that are on the dock station, and register for dock event notifications.
+ */
+static int dock_add(acpi_handle handle)
+{
+	int ret;
+	acpi_status status;
+	struct dock_dependent_device *dd;
+
+	/* allocate & initialize the dock_station private data */
+	dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
+	if (!dock_station)
+		return -ENOMEM;
+	dock_station->handle = handle;
+	dock_station->last_dock_time = jiffies - HZ;
+	INIT_LIST_HEAD(&dock_station->dependent_devices);
+	INIT_LIST_HEAD(&dock_station->hotplug_devices);
+	spin_lock_init(&dock_station->dd_lock);
+	spin_lock_init(&dock_station->hp_lock);
+
+	/* Find dependent devices */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock_devices, dock_station,
+			    NULL);
+
+	/* add the dock station as a device dependent on itself */
+	dd = alloc_dock_dependent_device(handle);
+	if (!dd) {
+		kfree(dock_station);
+		return -ENOMEM;
+	}
+	add_dock_dependent_device(dock_station, dd);
+
+	/* register for dock events */
+	status = acpi_install_notify_handler(dock_station->handle,
+					     ACPI_SYSTEM_NOTIFY,
+					     dock_notify, dock_station);
+
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Error installing notify handler\n");
+		ret = -ENODEV;
+		goto dock_add_err;
+	}
+
+	printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
+
+	return 0;
+
+dock_add_err:
+	kfree(dock_station);
+	kfree(dd);
+	return ret;
+}
+
+/**
+ * dock_remove - free up resources related to the dock station
+ */
+static int dock_remove(void)
+{
+	struct dock_dependent_device *dd, *tmp;
+	acpi_status status;
+
+	if (!dock_station)
+		return 0;
+
+	/* remove dependent devices */
+	list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
+				 list)
+	    kfree(dd);
+
+	/* remove dock notify handler */
+	status = acpi_remove_notify_handler(dock_station->handle,
+					    ACPI_SYSTEM_NOTIFY,
+					    dock_notify);
+	if (ACPI_FAILURE(status))
+		printk(KERN_ERR "Error removing notify handler\n");
+
+	/* free dock station memory */
+	kfree(dock_station);
+	return 0;
+}
+
+/**
+ * find_dock - look for a dock station
+ * @handle: acpi handle of a device
+ * @lvl: unused
+ * @context: counter of dock stations found
+ * @rv: unused
+ *
+ * This is called by acpi_walk_namespace to look for dock stations.
+ */
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+	acpi_status status = AE_OK;
+
+	if (is_dock(handle)) {
+		if (dock_add(handle) >= 0) {
+			(*count)++;
+			status = AE_CTRL_TERMINATE;
+		}
+	}
+	return status;
+}
+
+static int __init dock_init(void)
+{
+	int num = 0;
+
+	dock_station = NULL;
+
+	/* look for a dock station */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	if (!num)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void __exit dock_exit(void)
+{
+	dock_remove();
+}
+
+postcore_initcall(dock_init);
+module_exit(dock_exit);
--- 2.6-git-kca2.orig/drivers/acpi/Kconfig
+++ 2.6-git-kca2/drivers/acpi/Kconfig
@@ -134,6 +134,13 @@ config ACPI_FAN
 	  This driver adds support for ACPI fan devices, allowing user-mode 
 	  applications to perform basic fan control (on, off, status).
 
+config ACPI_DOCK
+	tristate "Dock"
+	depends on !ACPI_IBM_DOCK
+	default y
+	help
+	  This driver adds support for ACPI controlled docking stations
+
 config ACPI_PROCESSOR
 	tristate "Processor"
 	default y
--- 2.6-git-kca2.orig/drivers/acpi/Makefile
+++ 2.6-git-kca2/drivers/acpi/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_EC)		+= ec.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
+obj-$(CONFIG_ACPI_DOCK)		+= dock.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o 
 obj-$(CONFIG_ACPI_HOTKEY)	+= hotkey.o
 obj-y				+= pci_root.o pci_link.o pci_irq.o pci_bind.o
--- 2.6-git-kca2.orig/drivers/acpi/scan.c
+++ 2.6-git-kca2/drivers/acpi/scan.c
@@ -703,6 +703,29 @@ static int acpi_bus_find_driver(struct a
                                  Device Enumeration
    -------------------------------------------------------------------------- */
 
+acpi_status
+acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		obj = buffer.pointer;
+		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
+		acpi_os_free(buffer.pointer);
+	}
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_get_ejd);
+
+
 static int acpi_bus_get_flags(struct acpi_device *device)
 {
 	acpi_status status = AE_OK;
--- 2.6-git-kca2.orig/include/acpi/acpi_bus.h
+++ 2.6-git-kca2/include/acpi/acpi_bus.h
@@ -332,7 +332,7 @@ int acpi_bus_add(struct acpi_device **ch
 		 acpi_handle handle, int type);
 int acpi_bus_trim(struct acpi_device *start, int rmdevice);
 int acpi_bus_start(struct acpi_device *device);
-
+acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd);
 int acpi_match_ids(struct acpi_device *device, char *ids);
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
--- 2.6-git-kca2.orig/include/acpi/acpi_drivers.h
+++ 2.6-git-kca2/include/acpi/acpi_drivers.h
@@ -110,4 +110,21 @@ int acpi_processor_set_thermal_limit(acp
 
 extern int acpi_specific_hotkey_enabled;
 
+/*--------------------------------------------------------------------------
+                                  Dock Station
+  -------------------------------------------------------------------------- */
+#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE)
+extern int is_dock_device(acpi_handle handle);
+extern int register_dock_notifier(struct notifier_block *nb);
+extern void unregister_dock_notifier(struct notifier_block *nb);
+extern int register_hotplug_dock_device(acpi_handle handle,
+	acpi_notify_handler handler, void *context);
+extern void unregister_hotplug_dock_device(acpi_handle handle);
+#else
+#define is_dock_device(h)			(0)
+#define register_dock_notifier(nb) 		(-ENODEV)
+#define unregister_dock_notifier(nb)           	do { } while(0)
+#define register_hotplug_dock_device(h1, h2, c)	(-ENODEV)
+#define unregister_hotplug_dock_device(h)       do { } while(0)
+#endif
 #endif /*__ACPI_DRIVERS_H__*/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver v6
  2006-06-01 23:05               ` [patch 1/3] acpi: dock driver v6 Kristen Accardi
@ 2006-06-01 23:20                 ` Andrew Morton
  2006-06-02  0:53                   ` Kristen Accardi
  0 siblings, 1 reply; 24+ messages in thread
From: Andrew Morton @ 2006-06-01 23:20 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel, pavel

Kristen Accardi <kristen.c.accardi@intel.com> wrote:
>
> Changed from last version:

It would be much preferred if you could issue patches against the previous
version please (ie: the thing in -mm), instead of reissuing the patch each
time.

It tells us that you, the developer, have been testing the code which we
intend to send upstream, rather than testing some possibly-divergent thing
which lives on your hard-drive.

And it makes it much easier to review the changes.  And it saves me from
having to generate the incremental diff so _I_ can see what you've changed.


And lo, when I did that:

diff -puN drivers/acpi/dock.c~acpi-dock-driver-v6 drivers/acpi/dock.c
--- 25/drivers/acpi/dock.c~acpi-dock-driver-v6	Thu Jun  1 16:12:31 2006
+++ 25-akpm/drivers/acpi/dock.c	Thu Jun  1 16:12:31 2006
@@ -190,6 +190,9 @@ static int is_dock(acpi_handle handle)
  */
 int is_dock_device(acpi_handle handle)
 {
+	if (!dock_station)
+		return 0;
+
 	if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
 		return 1;
 
@@ -218,6 +221,66 @@ static int dock_present(struct dock_stat
 	return 0;
 }
 
+
+
+/**
+ * dock_create_acpi_device - add new devices to acpi
+ * @handle - handle of the device to add
+ *
+ *  This function will create a new acpi_device for the given
+ *  handle if one does not exist already.  This should cause
+ *  acpi to scan for drivers for the given devices, and call
+ *  matching driver's add routine.
+ *
+ *  Returns a pointer to the acpi_device corresponding to the handle.
+ */
+static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
+{
+	struct acpi_device *device = NULL;
+	struct acpi_device *parent_device;
+	acpi_handle parent;
+	int ret;
+
+	if (acpi_bus_get_device(handle, &device)) {
+		/*
+		 * no device created for this object,
+		 * so we should create one.
+		 */
+		acpi_get_parent(handle, &parent);
+		if (acpi_bus_get_device(parent, &parent_device))
+			parent_device = NULL;
+
+		ret = acpi_bus_add(&device, parent_device, handle,
+			ACPI_BUS_TYPE_DEVICE);
+		if (ret) {
+			pr_debug("error adding bus, %x\n",
+				-ret);
+			return NULL;
+		}
+	}
+	return device;
+}
+
+/**
+ * dock_remove_acpi_device - remove the acpi_device struct from acpi
+ * @handle - the handle of the device to remove
+ *
+ *  Tell acpi to remove the acpi_device.  This should cause any loaded
+ *  driver to have it's remove routine called.
+ */
+static void dock_remove_acpi_device(acpi_handle handle)
+{
+	struct acpi_device *device;
+	int ret;
+
+	if (acpi_bus_get_device(handle, &device)) {
+		ret = acpi_bus_trim(device, 1);
+		if (ret)
+			pr_debug("error removing bus, %x\n", -ret);
+	}
+}
+
+
 /**
  * hotplug_dock_devices - insert or remove devices on the dock station
  * @ds: the dock station
@@ -233,39 +296,37 @@ static void hotplug_dock_devices(struct 
 	struct dock_dependent_device *dd;
 
 	spin_lock(&ds->hp_lock);
+
+	/*
+	 * First call driver specific hotplug functions
+	 */
 	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
 		if (dd->handler)
 			dd->handler(dd->handle, event, dd->context);
 	}
+
+	/*
+	 * Now make sure that an acpi_device is created for each
+	 * dependent device, or removed if this is an eject request.
+	 * This will cause acpi_drivers to be stopped/started if they
+	 * exist
+	 */
+	list_for_each_entry(dd, &ds->dependent_devices, list) {
+		if (event == ACPI_NOTIFY_EJECT_REQUEST)
+			dock_remove_acpi_device(dd->handle);
+		else
+			dock_create_acpi_device(dd->handle);
+	}
 	spin_unlock(&ds->hp_lock);
 }
 
-
 static void dock_event(struct dock_station *ds, u32 event, int num)
 {
 	struct acpi_device *device;
-	struct acpi_device *parent_device;
-	acpi_handle parent;
-	int ret;
 
-	if (acpi_bus_get_device(ds->handle, &device)) {
-		/*
-		 * no device created for this object,
-		 * so we should create one.
-		 */
-		acpi_get_parent(ds->handle, &parent);
-		if (acpi_bus_get_device(parent, &parent_device))
-			parent_device = NULL;
-
-		ret = acpi_bus_add(&device, parent_device, ds->handle,
-			ACPI_BUS_TYPE_DEVICE);
-		if (ret) {
-			pr_debug("error adding bus, %x\n",
-				-ret_val);
-			return;
-		}
-	}
-	kobject_uevent(&device->kobj, num);
+	device = dock_create_acpi_device(ds->handle);
+	if (device)
+		kobject_uevent(&device->kobj, num);
 }
 
 /**
@@ -674,5 +735,5 @@ static void __exit dock_exit(void)
 	dock_remove();
 }
 
-module_init(dock_init);
+postcore_initcall(dock_init);
 module_exit(dock_exit);
diff -puN drivers/acpi/scan.c~acpi-dock-driver-v6 drivers/acpi/scan.c
--- 25/drivers/acpi/scan.c~acpi-dock-driver-v6	Thu Jun  1 16:12:31 2006
+++ 25-akpm/drivers/acpi/scan.c	Thu Jun  1 16:12:31 2006
@@ -670,7 +670,7 @@ acpi_bus_get_ejd(acpi_handle handle, acp
 	if (ACPI_SUCCESS(status)) {
 		obj = buffer.pointer;
 		status = acpi_get_handle(NULL, obj->string.pointer, ejd);
-		kfree(buffer.pointer);
+		acpi_os_free(buffer.pointer);
 	}
 	return status;
 }
_

I see an acpi_os_free().   There ain't any such function.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [patch 1/3] acpi: dock driver v6
  2006-06-01 23:20                 ` Andrew Morton
@ 2006-06-02  0:53                   ` Kristen Accardi
  0 siblings, 0 replies; 24+ messages in thread
From: Kristen Accardi @ 2006-06-02  0:53 UTC (permalink / raw)
  To: Andrew Morton
  Cc: len.brown, greg, linux-acpi, pcihpd-discuss, linux-kernel, pavel

On Thu, 2006-06-01 at 16:20 -0700, Andrew Morton wrote:
> Kristen Accardi <kristen.c.accardi@intel.com> wrote:
> >
> > Changed from last version:
> 
> It would be much preferred if you could issue patches against the previous
> version please (ie: the thing in -mm), instead of reissuing the patch each
> time.
> 

Sorry, another maintainer I routinely post patches too always preferred
me to just redo the whole patch so that he could just drop the old one.
I'll change my procedure for this patch.  If you do have a set of
guidelines for when to know whether to redo entire patches vs. patch
your patch so to speak, it'd be educational for me to hear it - as it is
I just get trained by the person I usually submit to and adopt his
process, making things work really well for me submitting to him, but
probably not so well for me when I submit to others.

I also usually patch/test against 2.6-git - mainly because I imagined
that this hopefully limits weird bugs to problems that I've actually
introduced instead of someone elses bugs - which is why I didn't notice
that acpi_os_free had been removed (although even if it hadn't I
shouldn't be using it anyway - ugh.).  Thanks for fixing the mistake,
looks like I should test against -mm and -git in the future.

Kristen

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2006-06-02  0:41 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20060412221027.472109000@intel.com>
2006-04-12 22:18 ` [patch 1/3] acpi: dock driver Kristen Accardi
2006-04-12 22:35   ` [patch 1/3] acpi: dock driver (refreshed) Kristen Accardi
2006-04-13  5:27   ` [patch 1/3] acpi: dock driver Andrew Morton
2006-04-14 22:02     ` Kristen Accardi
2006-04-14 22:49     ` Kristen Accardi
2006-04-15 14:29       ` Prarit Bhargava
2006-04-18 18:03         ` Kristen Accardi
2006-04-18 22:54           ` Patrick Mochel
2006-04-19 17:08             ` Kristen Accardi
2006-04-19 17:28               ` Patrick Mochel
2006-04-19 18:28                 ` Kristen Accardi
2006-04-19 18:20                   ` Patrick Mochel
2006-04-28 23:51           ` [patch 1/3] acpi: dock driver v3 Kristen Accardi
2006-05-11 18:45             ` [patch 1/3] acpi: dock driver v4 Kristen Accardi
2006-06-01 23:05               ` [patch 1/3] acpi: dock driver v6 Kristen Accardi
2006-06-01 23:20                 ` Andrew Morton
2006-06-02  0:53                   ` Kristen Accardi
2006-04-16 13:28       ` [patch 1/3] acpi: dock driver Prarit Bhargava
2006-04-12 22:18 ` [patch 2/3] acpiphp: use new " Kristen Accardi
2006-04-12 23:16   ` Christian Trefzer
2006-04-28 23:55   ` [patch 2/3] acpiphp: use new dock driver v2 Kristen Accardi
2006-04-12 22:18 ` [patch 3/3] acpiphp: prevent duplicate slot numbers when no _SUN Kristen Accardi
2006-04-13 12:36   ` MUNEDA Takahiro
2006-04-14 21:39     ` Kristen Accardi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).