[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