[i2c] [patch 2.6.21-rc3-git +i2c 2/5] i2c stack can remove()

David Brownell david-b at pacbell.net
Fri Mar 9 06:11:09 CET 2007


More update for new style driver support:  add a remove() method, and
use it in the relevant code paths.  Also add i2c_unregister_driver()
for the exclusive use of new-style drivers.

Again, nothing will use this yet since there's nothing to create devices
feeding this infrastructure.

Signed-off-by: David Brownell <dbrownell at users.sourceforge.net>

---
 drivers/i2c/i2c-core.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++--
 include/linux/i2c.h    |    3 +
 2 files changed, 99 insertions(+), 4 deletions(-)

Index: at91/include/linux/i2c.h
===================================================================
--- at91.orig/include/linux/i2c.h	2007-03-08 13:47:48.000000000 -0800
+++ at91/include/linux/i2c.h	2007-03-08 13:47:49.000000000 -0800
@@ -130,6 +130,7 @@ struct i2c_driver {
 	 * it's done by infrastructure.  (NEW STYLE DRIVERS ONLY)
 	 */
 	int (*probe)(struct i2c_client *);
+	int (*remove)(struct i2c_client *);
 
 	/* driver model interfaces that don't relate to enumeration  */
 	void (*shutdown)(struct i2c_client *);
@@ -302,6 +303,8 @@ extern int i2c_add_adapter(struct i2c_ad
 extern int i2c_del_adapter(struct i2c_adapter *);
 
 extern int i2c_register_driver(struct module *, struct i2c_driver *);
+extern void i2c_unregister_driver(struct i2c_driver *);
+
 extern int i2c_del_driver(struct i2c_driver *);
 
 static inline int i2c_add_driver(struct i2c_driver *driver)
Index: at91/drivers/i2c/i2c-core.c
===================================================================
--- at91.orig/drivers/i2c/i2c-core.c	2007-03-08 13:47:48.000000000 -0800
+++ at91/drivers/i2c/i2c-core.c	2007-03-08 13:47:49.000000000 -0800
@@ -100,7 +100,24 @@ static int i2c_device_probe(struct devic
 
 static int i2c_device_remove(struct device *dev)
 {
-	return 0;
+	struct i2c_client	*client = to_i2c_client(dev);
+	struct i2c_driver	*driver;
+	int			status;
+
+	if (!dev->driver)
+		return 0;
+
+	driver = to_i2c_driver(dev->driver);
+	if (driver->remove) {
+		dev_dbg(dev, "remove\n");
+		status = driver->remove(client);
+	} else {
+		dev->driver = NULL;
+		status = 0;
+	}
+	if (status == 0)
+		client->driver = NULL;
+	return status;
 }
 
 static void i2c_device_shutdown(struct device *dev)
@@ -177,6 +194,42 @@ struct bus_type i2c_bus_type = {
 	.resume		= i2c_device_resume,
 };
 
+static void i2c_unregister_device(struct i2c_client *client)
+{
+	struct i2c_adapter	*adap = client->adapter;
+	struct i2c_driver	*driver = client->driver;
+	int			status;
+
+	if (driver && !driver->remove) {
+		dev_err(&client->dev, "can't unregister devices "
+			"with legacy drivers\n");
+		WARN_ON(1);
+		return;
+	}
+
+	/* unbind driver */
+	if (driver) {
+		/* REVISIT driver model core would handle this for us, and
+		 * once i2c_detach_client() stops updating lists, it should.
+		 */
+		status = i2c_device_remove(&client->dev);
+		if (status < 0) {
+			dev_err(&client->dev, "unbind failed (%d)\n", status);
+			return;
+		}
+	}
+
+	/* now remove the device, and free its memory */
+	status = i2c_detach_client(client);
+	if (status < 0)
+		dev_err(&adap->dev, "detach failed (%d) for client [%s] "
+			"at address 0x%02x\n",
+			status, client->name, client->addr);
+	else
+		put_device(&client->dev);
+}
+
+
 /* ------------------------------------------------------------------------- */
 
 /* I2C bus adapters -- one roots each I2C or SMBUS segment */
@@ -311,9 +364,19 @@ int i2c_del_adapter(struct i2c_adapter *
 	/* detach any active clients. This must be done first, because
 	 * it can fail; in which case we give up. */
 	list_for_each_safe(item, _n, &adap->clients) {
+		struct i2c_driver	*driver;
+
 		client = list_entry(item, struct i2c_client, list);
+		driver = client->driver;
 
-		if ((res=client->driver->detach_client(client))) {
+		/* new style, follow standard driver model */
+		if (!driver || driver->remove) {
+			i2c_unregister_device(client);
+			continue;
+		}
+
+		/* legacy drivers create and remove clients themselves */
+		if ((res = driver->detach_client(client))) {
 			dev_err(&adap->dev, "detach_client failed for client "
 				"[%s] at address 0x%02x\n", client->name,
 				client->addr);
@@ -356,7 +419,7 @@ int i2c_register_driver(struct module *o
 	int res;
 
 	/* new style driver methods can't mix with legacy ones */
-	if (driver->probe) {
+	if (driver->probe || driver->remove) {
 		if (driver->attach_adapter || driver->detach_adapter
 				|| driver->detach_client) {
 			printk(KERN_WARNING
@@ -392,6 +455,32 @@ int i2c_register_driver(struct module *o
 }
 EXPORT_SYMBOL(i2c_register_driver);
 
+/**
+ * i2c_unregister_driver - unregister non-legacy I2C driver
+ * @driver: the driver being unregistered
+ *
+ * Following the normal driver model conventions, when drivers are
+ * unregistered they receive remove() calls to unbind each i2c_client
+ * that had previously been bound to them by probe().  Those i2c_client
+ * handles must not be used after remove() returns; a different driver
+ * could have been bound to that device after remove() returns.
+ */
+void i2c_unregister_driver(struct i2c_driver *driver)
+{
+	if (!driver->remove) {
+		printk(KERN_WARNING "i2c-core: legacy driver [%s] "
+				"can't use i2c_unregister_driver\n",
+				driver->driver.name);
+		return;
+	}
+	driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL(i2c_unregister_driver);
+
+/**
+ * i2c_del_driver - unregister legacy I2C driver
+ * @driver: the driver being unregistered
+ */
 int i2c_del_driver(struct i2c_driver *driver)
 {
 	struct list_head   *item1, *item2, *_n;
@@ -400,12 +489,15 @@ int i2c_del_driver(struct i2c_driver *dr
 
 	int res = 0;
 
-	mutex_lock(&core_lists);
+	/* new-style drivers use i2c_unregister_driver() */
+	if (driver->remove)
+		return -EINVAL;
 
 	/* Have a look at each adapter, if clients of this driver are still
 	 * attached. If so, detach them to be able to kill the driver
 	 * afterwards.
 	 */
+	mutex_lock(&core_lists);
 	list_for_each(item1,&adapters) {
 		adap = list_entry(item1, struct i2c_adapter, list);
 		if (driver->detach_adapter) {



More information about the i2c mailing list