All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
To: Linux I2C <i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org>
Cc: David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
Subject: [PATCH 1/4] i2c: Introduce i2c listeners
Date: Wed, 4 Jun 2008 20:18:38 +0200	[thread overview]
Message-ID: <20080604201838.25807602@hyperion.delvare> (raw)
In-Reply-To: <20080604201334.19636f30-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>

Add an i2c listener mechanism to the i2c-subsystem. This mechanism
lets device drivers be notified of addition and removal of adapters,
and gives them a chance to detect devices they would support and to ask
i2c-core to instantiate them.

Signed-off-by: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org>
---
This patch depends on:
http://khali.linux-fr.org/devel/linux-2.6/jdelvare-i2c/i2c-use-class_for_each_device.patch

Implementation note: i2c_detect and i2c_detect_address are basically
clones of i2c_probe and i2c_probe_address but operating on a callback
with a slightly different prototype. This duplicated code accounts
for a large part of the patch, but once legacy drivers are gone,
i2c_probe and i2c_probe_address will be removed, so thus is no
long-term duplication.

 drivers/i2c/i2c-core.c |  235 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c.h    |   34 ++++++
 2 files changed, 269 insertions(+)

--- linux-2.6.26-rc4.orig/include/linux/i2c.h	2008-06-04 08:26:05.000000000 +0200
+++ linux-2.6.26-rc4/include/linux/i2c.h	2008-06-04 08:26:05.000000000 +0200
@@ -43,6 +43,7 @@ struct i2c_adapter;
 struct i2c_client;
 struct i2c_driver;
 union i2c_smbus_data;
+struct i2c_board_info;
 
 /*
  * The master routines are the ones normally used to transmit data to devices
@@ -92,6 +93,34 @@ extern s32 i2c_smbus_write_i2c_block_dat
 					  u8 command, u8 length,
 					  const u8 *values);
 
+/**
+ * struct i2c_listener - Be informed of adapter addition and removal
+ * @class: What kind of i2c device the listener may instantiate
+ * @attach_adapter: Called after adapter addition (unimplemented)
+ * @detach_adapter: Called before adapter removal (unimplemented)
+ * @address_data: The I2C addresses to probe, ignore or force
+ * @detect: Callback for device detection
+ * @clients: List of detected clients we created
+ *
+ * The @detect callback returns an i2c_board_info structure for device
+ * creation if the detection was successful, NULL otherwise.
+ *
+ * The @clients list is handled by i2c-core.
+ */
+struct i2c_listener {
+	unsigned int class;
+
+	int (*attach_adapter)(struct i2c_adapter *);
+	int (*detach_adapter)(struct i2c_adapter *);
+
+	const struct i2c_client_address_data *address_data;
+	int (*detect)(struct i2c_adapter *, int address, int kind,
+		      struct i2c_board_info *);
+	struct list_head clients;
+
+	struct list_head list;
+};
+
 /*
  * A driver is capable of handling one or more physical devices present on
  * I2C adapters. This information is used to inform the driver of adapter
@@ -155,6 +184,7 @@ struct i2c_driver {
  * @dev: Driver model device node for the slave.
  * @irq: indicates the IRQ generated by this device (if any)
  * @list: list of active/busy clients (DEPRECATED)
+ * @detected: member of i2c_listener.clients
  * @released: used to synchronize client releases & detaches and references
  *
  * An i2c_client identifies a single device (i.e. chip) connected to an
@@ -172,6 +202,7 @@ struct i2c_client {
 	struct device dev;		/* the device structure		*/
 	int irq;			/* irq issued by device (or -1) */
 	struct list_head list;		/* DEPRECATED */
+	struct list_head detected;
 	struct completion released;
 };
 #define to_i2c_client(d) container_of(d, struct i2c_client, dev)
@@ -382,6 +413,9 @@ extern int i2c_add_adapter(struct i2c_ad
 extern int i2c_del_adapter(struct i2c_adapter *);
 extern int i2c_add_numbered_adapter(struct i2c_adapter *);
 
+extern void i2c_add_listener(struct i2c_listener *);
+extern void i2c_del_listener(struct i2c_listener *);
+
 extern int i2c_register_driver(struct module *, struct i2c_driver *);
 extern void i2c_del_driver(struct i2c_driver *);
 
--- linux-2.6.26-rc4.orig/drivers/i2c/i2c-core.c	2008-06-04 08:26:05.000000000 +0200
+++ linux-2.6.26-rc4/drivers/i2c/i2c-core.c	2008-06-04 11:38:02.000000000 +0200
@@ -42,8 +42,16 @@
 static DEFINE_MUTEX(core_lock);
 static DEFINE_IDR(i2c_adapter_idr);
 
+/* listeners_lock protects the listeners list. If you need both core_lock
+   and listeners_lock, core_lock must be taken first. */
+static DEFINE_MUTEX(listeners_lock);
+static LIST_HEAD(listeners);
+
 #define is_newstyle_driver(d) ((d)->probe || (d)->remove)
 
+static int i2c_detect(struct i2c_adapter *adapter,
+		      struct i2c_listener *listener);
+
 /* ------------------------------------------------------------------------- */
 
 static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
@@ -418,6 +426,7 @@ static int i2c_do_add_adapter(struct dev
 
 static int i2c_register_adapter(struct i2c_adapter *adap)
 {
+	struct i2c_listener *listener;
 	int res = 0, dummy;
 
 	mutex_init(&adap->bus_lock);
@@ -448,6 +457,13 @@ static int i2c_register_adapter(struct i
 	if (adap->nr < __i2c_first_dynamic_bus_num)
 		i2c_scan_static_board_info(adap);
 
+	/* let listeners know that a bus has arrived */
+	mutex_lock(&listeners_lock);
+	list_for_each_entry(listener, &listeners, list) {
+		i2c_detect(adap, listener);
+	}
+	mutex_unlock(&listeners_lock);
+
 	/* let legacy drivers scan this bus for matching devices */
 	dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
 				 i2c_do_add_adapter);
@@ -576,6 +592,7 @@ static int i2c_do_del_adapter(struct dev
 int i2c_del_adapter(struct i2c_adapter *adap)
 {
 	struct i2c_client *client, *_n;
+	struct i2c_listener *listener;
 	int res = 0;
 
 	mutex_lock(&core_lock);
@@ -588,6 +605,21 @@ int i2c_del_adapter(struct i2c_adapter *
 		goto out_unlock;
 	}
 
+	/* Walk the listeners to remove the devices we created ourselves */
+	mutex_lock(&listeners_lock);
+	list_for_each_entry(listener, &listeners, list) {
+		list_for_each_entry_safe(client, _n, &listener->clients,
+					 detected) {
+			if (client->adapter == adap) {
+				dev_dbg(&adap->dev, "Removing %s at 0x%x\n",
+					client->name, client->addr);
+				list_del(&client->detected);
+				i2c_unregister_device(client);
+			}
+		}
+	}
+	mutex_unlock(&listeners_lock);
+
 	/* Tell drivers about this removal */
 	res = bus_for_each_drv(&i2c_bus_type, NULL, adap,
 			       i2c_do_del_adapter);
@@ -752,6 +784,53 @@ EXPORT_SYMBOL(i2c_del_driver);
 
 /* ------------------------------------------------------------------------- */
 
+static int __i2c_detect(struct device *dev, void *data)
+{
+	struct i2c_adapter *adapter = to_i2c_adapter(dev);
+	struct i2c_listener *listener = data;
+
+	i2c_detect(adapter, listener);
+
+	return 0;
+}
+
+void i2c_add_listener(struct i2c_listener *listener)
+{
+	mutex_lock(&core_lock);
+	mutex_lock(&listeners_lock);
+	INIT_LIST_HEAD(&listener->clients);
+	list_add_tail(&listener->list, &listeners);
+	pr_debug("i2c: Listener %p added\n", listener);
+
+	/* let this listener know about buses that are already there */
+	class_for_each_device(&i2c_adapter_class, listener, __i2c_detect);
+	mutex_unlock(&listeners_lock);
+	mutex_unlock(&core_lock);
+}
+EXPORT_SYMBOL_GPL(i2c_add_listener);
+
+void i2c_del_listener(struct i2c_listener *listener)
+{
+	struct i2c_client *client, *_n;
+
+	mutex_lock(&listeners_lock);
+	/* remove all clients that originate from this listener */
+	list_for_each_entry_safe(client, _n, &listener->clients, detected) {
+		dev_dbg(&client->adapter->dev, "Removing %s at 0x%x\n",
+			client->name, client->addr);
+		list_del(&client->detected);
+		i2c_unregister_device(client);
+	}
+
+	list_del(&listener->list);
+	pr_debug("i2c: Listener %p deleted\n", listener);
+	mutex_unlock(&listeners_lock);
+
+}
+EXPORT_SYMBOL_GPL(i2c_del_listener);
+
+/* ------------------------------------------------------------------------- */
+
 static int __i2c_check_addr(struct device *dev, void *addrp)
 {
 	struct i2c_client	*client = i2c_verify_client(dev);
@@ -1196,6 +1275,162 @@ int i2c_probe(struct i2c_adapter *adapte
 }
 EXPORT_SYMBOL(i2c_probe);
 
+/* Separate detection function for listeners */
+static int i2c_detect_address(struct i2c_adapter *adapter,
+			      int addr, int kind,
+			      struct i2c_listener *listener)
+{
+	struct i2c_board_info info;
+	struct i2c_client *client;
+	int err;
+
+	/* Make sure the address is valid */
+	if (addr < 0x03 || addr > 0x77) {
+		dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
+			 addr);
+		return -EINVAL;
+	}
+
+	/* Skip if already in use */
+	if (i2c_check_addr(adapter, addr))
+		return 0;
+
+	/* Make sure there is something at this address, unless forced */
+	if (kind < 0) {
+		if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
+				   I2C_SMBUS_QUICK, NULL) < 0)
+			return 0;
+
+		/* prevent 24RF08 corruption */
+		if ((addr & ~0x0f) == 0x50)
+			i2c_smbus_xfer(adapter, addr, 0, 0, 0,
+				       I2C_SMBUS_QUICK, NULL);
+	}
+
+	/* Finally call the custom detection function */
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	err = listener->detect(adapter, addr, kind, &info);
+	if (err) {
+		/* -ENODEV can be returned if there is a chip at the given address
+		   but it isn't supported by this chip driver. We catch it here as
+		   this isn't an error. */
+		return err == -ENODEV ? 0 : err;
+	}
+
+	/* Consistency check */
+	if (info.type[0] == '\0' || info.addr != addr) {
+		dev_err(&adapter->dev, "Detection function returned "
+			"inconsistent data for 0x%x\n", addr);
+	} else {
+		/* Detection succeeded, instantiate the device */
+		dev_dbg(&adapter->dev, "Creating %s at 0x%x\n",
+			info.type, info.addr);
+		client = i2c_new_device(adapter, &info);
+		list_add_tail(&client->detected, &listener->clients);
+	}
+	return 0;
+}
+
+static int i2c_detect(struct i2c_adapter *adapter,
+		      struct i2c_listener *listener)
+{
+	const struct i2c_client_address_data *address_data;
+	int i, err;
+	int adap_id = i2c_adapter_id(adapter);
+
+	address_data = listener->address_data;
+
+	/* Force entries are done first, and are not affected by ignore
+	   entries */
+	if (address_data->forces) {
+		const unsigned short * const *forces = address_data->forces;
+		int kind;
+
+		for (kind = 0; forces[kind]; kind++) {
+			for (i = 0; forces[kind][i] != I2C_CLIENT_END;
+			     i += 2) {
+				if (forces[kind][i] == adap_id
+				 || forces[kind][i] == ANY_I2C_BUS) {
+					dev_dbg(&adapter->dev, "found force "
+						"parameter for adapter %d, "
+						"addr 0x%02x, kind %d\n",
+						adap_id, forces[kind][i + 1],
+						kind);
+					err = i2c_detect_address(adapter,
+						forces[kind][i + 1],
+						kind, listener);
+					if (err)
+						return err;
+				}
+			}
+		}
+	}
+
+	/* Stop here if we can't use SMBUS_QUICK */
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
+		if (address_data->probe[0] == I2C_CLIENT_END
+		 && address_data->normal_i2c[0] == I2C_CLIENT_END)
+			return 0;
+
+		dev_warn(&adapter->dev, "SMBus Quick command not supported, "
+			 "can't probe for chips\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* Probe entries are done second, and are not affected by ignore
+	   entries either */
+	for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
+		if (address_data->probe[i] == adap_id
+		 || address_data->probe[i] == ANY_I2C_BUS) {
+			dev_dbg(&adapter->dev, "found probe parameter for "
+				"adapter %d, addr 0x%02x\n", adap_id,
+				address_data->probe[i + 1]);
+			err = i2c_detect_address(adapter,
+						 address_data->probe[i + 1],
+						 -1, listener);
+			if (err)
+				return err;
+		}
+	}
+
+	/* Stop here if the classes do not match */
+	if (!(adapter->class & listener->class))
+		return 0;
+
+	/* Normal entries are done last, unless shadowed by an ignore entry */
+	for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
+		int j, ignore;
+
+		ignore = 0;
+		for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
+		     j += 2) {
+			if ((address_data->ignore[j] == adap_id ||
+			     address_data->ignore[j] == ANY_I2C_BUS)
+			 && address_data->ignore[j + 1]
+			    == address_data->normal_i2c[i]) {
+				dev_dbg(&adapter->dev, "found ignore "
+					"parameter for adapter %d, "
+					"addr 0x%02x\n", adap_id,
+					address_data->ignore[j + 1]);
+				ignore = 1;
+				break;
+			}
+		}
+		if (ignore)
+			continue;
+
+		dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
+			"addr 0x%02x\n", adap_id,
+			address_data->normal_i2c[i]);
+		err = i2c_detect_address(adapter, address_data->normal_i2c[i],
+					 -1, listener);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 struct i2c_client *
 i2c_new_probed_device(struct i2c_adapter *adap,
 		      struct i2c_board_info *info,

-- 
Jean Delvare

_______________________________________________
i2c mailing list
i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org
http://lists.lm-sensors.org/mailman/listinfo/i2c

  parent reply	other threads:[~2008-06-04 18:18 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-06-04 18:13 [PATCH 0/4] i2c: Introduce i2c listeners Jean Delvare
     [not found] ` <20080604201334.19636f30-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-06-04 18:18   ` Jean Delvare [this message]
2008-06-04 18:31   ` [PATCH 2/4] i2c: Convert the lm90 driver to a new-style i2c driver Jean Delvare
2008-06-04 18:33   ` [PATCH 3/4] i2c: Use i2c_listener in driver f75375s Jean Delvare
     [not found]     ` <20080604203322.472f8653-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-06-05  8:33       ` Riku Voipio
     [not found]         ` <4847A4E2.9040406-WgUW+8SLYMv1KXRcyAk9cg@public.gmane.org>
2008-06-05  9:06           ` Jean Delvare
     [not found]             ` <20080605110659.3456fbe4-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-06-06 10:21               ` Riku Voipio
     [not found]                 ` <48490FA3.8020702-WgUW+8SLYMv1KXRcyAk9cg@public.gmane.org>
2008-06-06 11:38                   ` Jean Delvare
2008-06-04 18:35   ` [PATCH 4/4] i2c: Use i2c_listener in driver lm75 Jean Delvare
2008-06-04 18:55   ` [PATCH 0/4] i2c: Introduce i2c listeners Jon Smirl
     [not found]     ` <9e4733910806041155n7551ac74lf29c8a32163ec09a-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-06-04 19:28       ` Jon Smirl
     [not found]         ` <9e4733910806041228i330e145q439d3ee43494f4c4-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-06-04 21:33           ` Jean Delvare
     [not found]             ` <20080604233335.13459512-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-06-04 23:11               ` Jon Smirl
     [not found]                 ` <9e4733910806041611l41832e07p4f55424be0ef5ea0-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-06-05  0:10                   ` David Brownell
     [not found]                     ` <200806041710.59338.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-06-05  0:42                       ` Jon Smirl
     [not found]                         ` <9e4733910806041742va67401en608c8c4b8c4c11b9-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-06-05  2:21                           ` David Brownell
     [not found]                             ` <200806041921.26293.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-06-05  4:04                               ` Jon Smirl
     [not found]                                 ` <9e4733910806042104l70cf8a30sc6329b1c3016c879-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-06-05  8:18                                   ` David Brownell
     [not found]                                     ` <200806050118.23706.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-06-05 14:55                                       ` Jon Smirl
     [not found]                                         ` <9e4733910806050755n3835d20xfc4d018c2222d5d3-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-06-05 17:00                                           ` Jean Delvare
     [not found]                                             ` <20080605190034.16f06604-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-06-05 17:34                                               ` Jon Smirl
     [not found]                                                 ` <9e4733910806051034k2e40082focaaa03b124fcd4ad-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-06-05 18:29                                                   ` Jean Delvare
2008-06-05  8:49                                   ` Jean Delvare
     [not found]                                     ` <20080605104914.2dd622b2-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-06-05 14:30                                       ` Jon Smirl
2008-06-05  8:38                           ` Jean Delvare
2008-06-05  9:05                   ` Jean Delvare
     [not found]                     ` <20080605110502.76f0f606-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-06-05 14:59                       ` Jon Smirl
2008-06-05  0:03               ` Trent Piepho
     [not found]                 ` <Pine.LNX.4.58.0806041638080.10290-13q4cmjDBaTP3RPoUHIrnuTW4wlIGRCZ@public.gmane.org>
2008-06-05  0:27                   ` David Brownell
     [not found]                     ` <200806041727.51746.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-06-05  0:40                       ` Trent Piepho
     [not found]                         ` <Pine.LNX.4.58.0806041731250.10290-13q4cmjDBaTP3RPoUHIrnuTW4wlIGRCZ@public.gmane.org>
2008-06-05  2:14                           ` David Brownell
     [not found]                             ` <200806041914.27291.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-06-05  7:08                               ` Trent Piepho
     [not found]                                 ` <Pine.LNX.4.58.0806042349251.10290-13q4cmjDBaTP3RPoUHIrnuTW4wlIGRCZ@public.gmane.org>
2008-06-05  8:15                                   ` David Brownell
2008-06-05  8:16                           ` Jean Delvare
2008-06-05  0:45                       ` Jon Smirl
2008-06-04 21:12       ` Jean Delvare
2008-06-06  2:47   ` David Brownell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080604201838.25807602@hyperion.delvare \
    --to=khali-puyad+kwke1g9huczpvpmw@public.gmane.org \
    --cc=david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org \
    --cc=i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.