[i2c] [patch 2.6.20-rc1 1/6] part II: I2C supports probe() and hotplug/coldplug

David Brownell david-b at pacbell.net
Tue Dec 19 00:04:27 CET 2006


First of a series of I2C infrastructure updates to support enumeration
using the standard Linux driver model.

This patch updates probe() and associated hotplug/coldplug support, but
not remove().  Nothing yet _uses_ it to create I2C devices, so the
hotplug/coldplug mechanisms will be the only externally visible change.
This patch will be an overall NOP since the I2C stack doesn't yet create
clients/devices except as part of binding them to legacy drivers.

Terminology being adopted:  "legacy drivers" create devices (i2c_client)
themselves, while "new style" ones follow the driver model (the i2c_client
is handed to the probe routine).  It's an either/or thing; the two models
don't mix, and drivers that try won't even be registered.

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

---
 drivers/i2c/i2c-core.c |   99 ++++++++++++++++++++++++++++++++++++++++++-------
 include/linux/i2c.h    |   10 +++-
 2 files changed, 94 insertions(+), 15 deletions(-)

Index: g26/include/linux/i2c.h
===================================================================
--- g26.orig/include/linux/i2c.h	2006-12-18 13:26:25.000000000 -0800
+++ g26/include/linux/i2c.h	2006-12-18 13:26:27.000000000 -0800
@@ -112,7 +112,7 @@ struct i2c_driver {
 	 * can be used by the driver to test if the bus meets its conditions
 	 * & seek for the presence of the chip(s) it supports. If found, it
 	 * registers the client(s) that are on the bus to the i2c admin. via
-	 * i2c_attach_client.
+	 * i2c_attach_client.  (LEGACY I2C DRIVERS ONLY)
 	 */
 	int (*attach_adapter)(struct i2c_adapter *);
 	int (*detach_adapter)(struct i2c_adapter *);
@@ -120,10 +120,16 @@ struct i2c_driver {
 	/* tells the driver that a client is about to be deleted & gives it
 	 * the chance to remove its private data. Also, if the client struct
 	 * has been dynamically allocated by the driver in the function above,
-	 * it must be freed here.
+	 * it must be freed here.  (LEGACY I2C DRIVERS ONLY)
 	 */
 	int (*detach_client)(struct i2c_client *);
 
+	/* Standard driver model interfaces, for "new style" i2c drivers.
+	 * With the driver model, device enumeration is NEVER done by drivers;
+	 * it's done by infrastructure.  (NEW STYLE DRIVERS ONLY)
+	 */
+	int (*probe)(struct i2c_client *);
+
 	/* driver model interfaces that don't relate to enumeration  */
 	void (*shutdown)(struct i2c_client *);
 	int (*suspend)(struct i2c_client *, pm_message_t mesg);
Index: g26/drivers/i2c/i2c-core.c
===================================================================
--- g26.orig/drivers/i2c/i2c-core.c	2006-12-18 13:26:25.000000000 -0800
+++ g26/drivers/i2c/i2c-core.c	2006-12-18 13:26:27.000000000 -0800
@@ -45,12 +45,45 @@ static DEFINE_IDR(i2c_adapter_idr);
 
 static int i2c_device_match(struct device *dev, struct device_driver *drv)
 {
-	/* (for now) bypass driver model probing entirely; drivers
-	 * scan each i2c adapter/bus themselves.
+	struct i2c_client	*client = to_i2c_client(dev);
+	struct i2c_driver	*driver = to_i2c_driver(drv);
+
+	/* legacy i2c drivers bypass driver model probing entirely;
+	 * drivers scan each i2c adapter/bus themselves.
 	 */
+	if (!driver->probe)
+		return 0;
+
+	/* new style drivers use the same driver matching policy as
+	 * platform devices or SPI:  compare device and driver names.
+	 */
+	return strcmp(client->name, drv->name) == 0;
+}
+
+#ifdef	CONFIG_HOTPLUG
+
+/* uevent helps with hotplug: modprobe -q $(MODALIAS) */
+static int i2c_device_uevent(struct device *dev, char **envp, int num_envp,
+		      char *buffer, int buffer_size)
+{
+	struct i2c_client	*client = to_i2c_client(dev);
+	int			i = 0, length = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
+			"MODALIAS=%s", client->name))
+		return -ENOMEM;
+	envp[i] = NULL;
 	return 0;
 }
 
+#else
+#define i2c_device_uevent	NULL
+#endif	/* CONFIG_HOTPLUG */
+
+
 static int i2c_device_suspend(struct device * dev, pm_message_t mesg)
 {
 	struct i2c_driver	*driver;
@@ -77,7 +110,16 @@ static int i2c_device_resume(struct devi
 
 static int i2c_device_probe(struct device *dev)
 {
-	return -ENODEV;
+	struct i2c_client	*client = to_i2c_client(dev);
+	struct i2c_driver	*driver;
+
+	if (!dev->driver)
+		return -ENODEV;
+	driver = to_i2c_driver(dev->driver);
+	if (!driver->probe)
+		return -ENODEV;
+	client->driver = driver;
+	return driver->probe(client);
 }
 
 static int i2c_device_remove(struct device *dev)
@@ -99,6 +141,7 @@ static void i2c_device_shutdown(struct d
 struct bus_type i2c_bus_type = {
 	.name =		"i2c",
 	.match =	i2c_device_match,
+	.uevent =	i2c_device_uevent,
 	.probe =	i2c_device_probe,
 	.remove =	i2c_device_remove,
 	.shutdown =	i2c_device_shutdown,
@@ -157,6 +200,20 @@ static ssize_t show_client_name(struct d
 static struct device_attribute dev_attr_client_name =
 	__ATTR(name, S_IRUGO, &show_client_name, NULL);
 
+/* modalias helps with coldplug:  modprobe $(cat /sys/devices/.../modalias)
+ * it's a convention across all Linux devices.
+ */
+DEVICE_ATTR(modalias, S_IRUGO, show_client_name, NULL);
+
+static /*const*/ struct attribute *i2c_dev_attrs[] = {
+	&dev_attr_client_name.attr,
+	&dev_attr_modalias.attr,
+	NULL,
+};
+
+static const struct attribute_group i2c_dev_attr_group = {
+	.attrs = i2c_dev_attrs,
+};
 
 /* ---------------------------------------------------
  * registering functions
@@ -316,33 +373,47 @@ int i2c_del_adapter(struct i2c_adapter *
 }
 
 
-/* -----
- * What follows is the "upwards" interface: commands for talking to clients,
- * which implement the functions to access the physical information of the
- * chips.
+/* ------------------------------------------------------------------------- */
+
+/*
+ * An i2c_driver is used with one or more i2c_client nodes to access i2c
+ * slave chips, on a bus associated some i2c_adapter.
  */
 
 int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
 {
-	struct list_head   *item;
-	struct i2c_adapter *adapter;
 	int res;
 
+	/* new style driver methods can't mix with legacy ones */
+	if (driver->probe) {
+		if (driver->attach_adapter || driver->detach_adapter
+				|| driver->detach_client) {
+			pr_debug("i2c-core: driver [%s] is confused\n",
+					driver->driver.name);
+			return -EINVAL;
+		}
+	}
+
 	/* add the driver to the list of i2c drivers in the driver core */
 	driver->driver.owner = owner;
 	driver->driver.bus = &i2c_bus_type;
 
 	res = driver_register(&driver->driver);
+	pr_debug("i2c-core: register driver [%s] --> %d\n",
+			driver->driver.name, res);
+
 	if (res)
 		return res;
 
 	mutex_lock(&core_lists);
 
 	list_add_tail(&driver->list,&drivers);
-	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
 
-	/* now look for instances of driver on our adapters */
+	/* legacy drivers scan i2c busses directly */
 	if (driver->attach_adapter) {
+		struct list_head   *item;
+		struct i2c_adapter *adapter;
+
 		list_for_each(item,&adapters) {
 			adapter = list_entry(item, struct i2c_adapter, list);
 			driver->attach_adapter(adapter);
@@ -408,6 +479,8 @@ int i2c_del_driver(struct i2c_driver *dr
 	return 0;
 }
 
+/* ------------------------------------------------------------------------- */
+
 static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr)
 {
 	struct list_head   *item;
@@ -459,7 +532,7 @@ int i2c_attach_client(struct i2c_client 
 	res = device_register(&client->dev);
 	if (res)
 		goto out_list;
-	res = device_create_file(&client->dev, &dev_attr_client_name);
+	res = sysfs_create_group(&client->dev.kobj, &i2c_dev_attr_group);
 	if (res)
 		goto out_unregister;
 	mutex_unlock(&adapter->clist_lock);
@@ -513,7 +586,7 @@ int i2c_detach_client(struct i2c_client 
 	mutex_lock(&adapter->clist_lock);
 	list_del(&client->list);
 	init_completion(&client->released);
-	device_remove_file(&client->dev, &dev_attr_client_name);
+	sysfs_remove_group(&client->dev.kobj, &i2c_dev_attr_group);
 	device_unregister(&client->dev);
 	mutex_unlock(&adapter->clist_lock);
 	wait_for_completion(&client->released);



More information about the i2c mailing list