[i2c] [patch 1/5] i2c stack can probe()

Jean Delvare khali at linux-fr.org
Fri Mar 2 17:28:05 CET 2007


Hi David,

On Thu, 15 Feb 2007 00:41:37 -0800, David Brownell wrote:
> One 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 those
> 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.
> 
> Some code is moved earlier in the source code, helping group more of the
> per-device infrastructure in one place and simplifying handling per-device
> attributes.
> 
> 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 mixing them won't even be registered.
> 
> Signed-off-by: David Brownell <dbrownell at users.sourceforge.net>
> 
> ---
>  drivers/i2c/i2c-core.c |  146 ++++++++++++++++++++++++++++++++-----------------
>  include/linux/i2c.h    |   10 ++-
>  2 files changed, 105 insertions(+), 51 deletions(-)
> 
> Index: i2c/include/linux/i2c.h
> ===================================================================
> --- i2c.orig/include/linux/i2c.h	2007-02-14 23:59:56.000000000 -0800
> +++ i2c/include/linux/i2c.h	2007-02-15 00:00:03.000000000 -0800
> @@ -113,7 +113,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 *);
> @@ -121,10 +121,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: i2c/drivers/i2c/i2c-core.c
> ===================================================================
> --- i2c.orig/drivers/i2c/i2c-core.c	2007-02-14 23:59:56.000000000 -0800
> +++ i2c/drivers/i2c/i2c-core.c	2007-02-15 00:00:03.000000000 -0800
> @@ -43,15 +43,58 @@ static DEFINE_IDR(i2c_adapter_idr);
>  
>  /* ------------------------------------------------------------------------- */
>  
> -/* match always succeeds, as we want the probe() to tell if we really accept this match */
>  static int i2c_device_match(struct device *dev, struct device_driver *drv)
>  {
> -	return 1;
> +	struct i2c_client	*client = to_i2c_client(dev);
> +	struct i2c_driver	*driver = to_i2c_driver(drv);
> +
> +	/* legacy i2c drivers bypass driver model probing entirely;
> +	 * such drivers scan each i2c adapter/bus themselves.
> +	 */
> +	if (!driver->probe)
> +		return 0;

Just to clarify things... This function will never actually be called
for legacy drivers, will it?

> +
> +	/* 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;

Can this realistically happen? I fail to see how.

> +
> +	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_probe(struct device *dev)
>  {
> -	return -ENODEV;
> +	struct i2c_client	*client = to_i2c_client(dev);
> +	struct i2c_driver	*driver;
> +
> +	if (!dev->driver)
> +		return -ENODEV;

Same question here, can this realistically happen?

> +	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)
> @@ -94,9 +137,30 @@ static int i2c_device_resume(struct devi
>  	return driver->resume(to_i2c_client(dev));
>  }
>  
> +static void i2c_client_release(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	complete(&client->released);
> +}
> +
> +static ssize_t show_client_name(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	return sprintf(buf, "%s\n", client->name);
> +}
> +
> +static /*const*/ struct device_attribute i2c_dev_attrs[] = {

What's this commented out "const"?

> +	__ATTR(name, S_IRUGO, show_client_name, NULL),
> +	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
> +	__ATTR(modalias, S_IRUGO, show_client_name, NULL),
> +	{ },
> +};

This means we will create the modalias file for both legacy and new
style i2c clients. Is it OK?

> +
>  struct bus_type i2c_bus_type = {
>  	.name		= "i2c",
> +	.dev_attrs	= i2c_dev_attrs,

Nice cleanup :)

>  	.match		= i2c_device_match,
> +	.uevent		= i2c_device_uevent,
>  	.probe		= i2c_device_probe,
>  	.remove		= i2c_device_remove,
>  	.shutdown	= i2c_device_shutdown,
> @@ -129,31 +193,6 @@ static ssize_t show_adapter_name(struct 
>  static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
>  
>  
> -static void i2c_client_release(struct device *dev)
> -{
> -	struct i2c_client *client = to_i2c_client(dev);
> -	complete(&client->released);
> -}
> -
> -static ssize_t show_client_name(struct device *dev, struct device_attribute *attr, char *buf)
> -{
> -	struct i2c_client *client = to_i2c_client(dev);
> -	return sprintf(buf, "%s\n", client->name);
> -}
> -
> -/*
> - * We can't use the DEVICE_ATTR() macro here, as we used the same name for
> - * an i2c adapter attribute (above).
> - */
> -static struct device_attribute dev_attr_client_name =
> -	__ATTR(name, S_IRUGO, &show_client_name, NULL);
> -
> -
> -/* ---------------------------------------------------
> - * registering functions
> - * ---------------------------------------------------
> - */
> -
>  static int i2c_do_add_adapter(struct device_driver *d, void *data)
>  {
>  	struct i2c_driver *driver = to_i2c_driver(d);
> @@ -219,7 +258,7 @@ int i2c_add_adapter(struct i2c_adapter *
>  
>  	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
>  
> -	/* let drivers scan this bus for matching devices */
> +	/* let legacy drivers scan this bus for matching devices */
>  	res = bus_for_each_drv(&i2c_bus_type, NULL, adap,
>  			       i2c_do_add_adapter);
>  
> @@ -312,39 +351,54 @@ 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 (device) nodes to access
> + * i2c slave chips, on a bus instance associated with some i2c_adapter.  There
> + * are two models for binding the driver to its device:  "new style" drivers
> + * follow the standard Linux driver model and just respond to probe() calls
> + * issued if the driver core sees they match(); "legacy" drivers create device
> + * nodes themselves.
>   */
>  
>  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;
> +		}
> +	}

Very good idea, I like it. But I believe this deserves at the very
least a KERN_NOTICE-level message, not just debugging. KERN_WARN or
KERN_ERR might even be more suitable.

> +
>  	/* 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);
> -
> -	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

I don't like this change. I understand that you want errors to be
reported, but having a single string for success and error is somewhat
confusing. I propose that you leave the current debug message in place
for the success case, and add a separate KERN_ERR-level message for the
error case. OK?

> -
> -	/* 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;
> +
> +		mutex_lock(&core_lists);
>  		list_for_each(item,&adapters) {
>  			adapter = list_entry(item, struct i2c_adapter, list);
>  			driver->attach_adapter(adapter);
>  		}
> +		mutex_unlock(&core_lists);
>  	}

I like this locking and local variables cleanup, however it really
doesn't belong to that patch. Can you please provide a separate patch
instead?

>  
> -	mutex_unlock(&core_lists);
>  	return 0;
>  }
>  EXPORT_SYMBOL(i2c_register_driver);
> @@ -399,6 +453,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;
> @@ -449,9 +505,6 @@ 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);
> -	if (res)
> -		goto out_unregister;
>  	mutex_unlock(&adapter->clist_lock);
>  
>  	if (adapter->client_register)  {
> @@ -464,10 +517,6 @@ int i2c_attach_client(struct i2c_client 
>  
>  	return 0;
>  
> -out_unregister:
> -	init_completion(&client->released); /* Needed? */
> -	device_unregister(&client->dev);
> -	wait_for_completion(&client->released);
>  out_list:
>  	list_del(&client->list);
>  	dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
> @@ -502,7 +551,6 @@ 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);
>  	device_unregister(&client->dev);
>  	mutex_unlock(&adapter->clist_lock);
>  	wait_for_completion(&client->released);

Looks good overall, thanks.

-- 
Jean Delvare



More information about the i2c mailing list