summaryrefslogtreecommitdiffstats
path: root/drivers/i2c/i2c-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/i2c-core.c')
-rw-r--r--drivers/i2c/i2c-core.c336
1 files changed, 245 insertions, 91 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 0bcb9eddc..7deb12bcb 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -20,7 +20,7 @@
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> */
-/* $Id: i2c-core.c,v 1.52 2000/02/27 10:43:29 frodo Exp $ */
+/* $Id: i2c-core.c,v 1.56 2000/07/09 15:13:05 frodo Exp $ */
#include <linux/module.h>
#include <linux/kernel.h>
@@ -73,8 +73,6 @@ static int driver_count;
/**** debug level */
static int i2c_debug=1;
-static void i2c_dummy_adapter(struct i2c_adapter *adapter);
-static void i2c_dummy_client(struct i2c_client *client);
/* ---------------------------------------------------
* /proc entry declarations
@@ -111,8 +109,8 @@ static int i2cproc_initialized = 0;
#else /* undef CONFIG_PROC_FS */
-#define i2cproc_init()
-#define i2cproc_cleanup()
+#define i2cproc_init() 0
+#define i2cproc_cleanup() 0
#endif /* CONFIG_PROC_FS */
@@ -129,7 +127,7 @@ static int i2cproc_initialized = 0;
*/
int i2c_add_adapter(struct i2c_adapter *adap)
{
- int i,j;
+ int i,j,res;
ADAP_LOCK();
for (i = 0; i < I2C_ADAP_MAX; i++)
@@ -139,8 +137,8 @@ int i2c_add_adapter(struct i2c_adapter *adap)
printk(KERN_WARNING
" i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n",
adap->name);
- ADAP_UNLOCK();
- return -ENOMEM;
+ res = -ENOMEM;
+ goto ERROR0;
}
adapters[i] = adap;
@@ -150,7 +148,6 @@ int i2c_add_adapter(struct i2c_adapter *adap)
/* init data types */
init_MUTEX(&adap->lock);
- i2c_dummy_adapter(adap); /* actually i2c_dummy->add_adapter */
#ifdef CONFIG_PROC_FS
if (i2cproc_initialized) {
@@ -163,12 +160,12 @@ int i2c_add_adapter(struct i2c_adapter *adap)
if (! proc_entry) {
printk("i2c-core.o: Could not create /proc/bus/%s\n",
name);
- return -ENOENT;
+ res = -ENOENT;
+ goto ERROR1;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,48))
proc_entry->proc_fops = &i2cproc_operations;
- proc_entry->owner = THIS_MODULE;
#else
proc_entry->ops = &i2cproc_inode_operations;
#endif
@@ -185,7 +182,9 @@ int i2c_add_adapter(struct i2c_adapter *adap)
/* inform drivers of new adapters */
DRV_LOCK();
for (j=0;j<I2C_DRIVER_MAX;j++)
- if (drivers[j]!=NULL && drivers[j]->flags&I2C_DF_NOTIFY)
+ if (drivers[j]!=NULL &&
+ (drivers[j]->flags&(I2C_DF_NOTIFY|I2C_DF_DUMMY)))
+ /* We ignore the return code; if it fails, too bad */
drivers[j]->attach_adapter(adap);
DRV_UNLOCK();
@@ -193,24 +192,68 @@ int i2c_add_adapter(struct i2c_adapter *adap)
adap->name,i));
return 0;
+
+
+ERROR1:
+ ADAP_LOCK();
+ adapters[i] = NULL;
+ adap_count--;
+ERROR0:
+ ADAP_UNLOCK();
+ return res;
}
int i2c_del_adapter(struct i2c_adapter *adap)
{
- int i,j;
+ int i,j,res;
+
ADAP_LOCK();
+
for (i = 0; i < I2C_ADAP_MAX; i++)
if (adap == adapters[i])
break;
if (I2C_ADAP_MAX == i) {
- printk( " i2c-core.o: unregister_adapter adap [%s] not found.\n",
+ printk( "i2c-core.o: unregister_adapter adap [%s] not found.\n",
adap->name);
- ADAP_UNLOCK();
- return -ENODEV;
+ res = -ENODEV;
+ goto ERROR0;
+ }
+
+ /* DUMMY drivers do not register their clients, so we have to
+ * use a trick here: we call driver->attach_adapter to
+ * *detach* it! Of course, each dummy driver should know about
+ * this or hell will break loose...
+ */
+ DRV_LOCK();
+ for (j = 0; j < I2C_DRIVER_MAX; j++)
+ if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY))
+ if ((res = drivers[j]->attach_adapter(adap))) {
+ printk("i2c-core.o: can't detach adapter %s "
+ "while detaching driver %s: driver not "
+ "detached!",adap->name,drivers[j]->name);
+ goto ERROR1;
+ }
+ DRV_UNLOCK();
+
+
+ /* detach any active clients. This must be done first, because
+ * it can fail; in which case we give upp. */
+ for (j=0;j<I2C_CLIENT_MAX;j++) {
+ struct i2c_client *client = adap->clients[j];
+ if (client!=NULL)
+ /* detaching devices is unconditional of the set notify
+ * flag, as _all_ clients that reside on the adapter
+ * must be deleted, as this would cause invalid states.
+ */
+ if ((res=client->driver->detach_client(client))) {
+ printk("i2c-core.o: adapter %s not "
+ "unregisted, because client at "
+ "address %02x can't be detached. ",
+ adap->name, client->addr);
+ goto ERROR0;
+ }
}
-
- i2c_dummy_adapter(adap); /* actually i2c_dummy->del_adapter */
#ifdef CONFIG_PROC_FS
if (i2cproc_initialized) {
char name[8];
@@ -219,25 +262,19 @@ int i2c_del_adapter(struct i2c_adapter *adap)
}
#endif /* def CONFIG_PROC_FS */
- /* detach any active clients */
- for (j=0;j<I2C_CLIENT_MAX;j++) {
- struct i2c_client *client = adap->clients[j];
- if ( (client!=NULL)
- /* && (client->driver->flags & I2C_DF_NOTIFY) */ )
- /* detaching devices is unconditional of the set notify
- * flag, as _all_ clients that reside on the adapter
- * must be deleted, as this would cause invalid states.
- */
- client->driver->detach_client(client);
- /* i2c_detach_client(client); --- frodo */
- }
- /* all done, now unregister */
adapters[i] = NULL;
adap_count--;
ADAP_UNLOCK();
DEB(printk("i2c-core.o: adapter unregistered: %s\n",adap->name));
return 0;
+
+ERROR0:
+ ADAP_UNLOCK();
+ return res;
+ERROR1:
+ DRV_UNLOCK();
+ return res;
}
@@ -249,14 +286,15 @@ int i2c_del_adapter(struct i2c_adapter *adap)
int i2c_add_driver(struct i2c_driver *driver)
{
- int i,j;
+ int i;
DRV_LOCK();
for (i = 0; i < I2C_DRIVER_MAX; i++)
if (NULL == drivers[i])
break;
if (I2C_DRIVER_MAX == i) {
printk(KERN_WARNING
- " i2c-core.o: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n",
+ " i2c-core.o: register_driver(%s) "
+ "- enlarge I2C_DRIVER_MAX.\n",
driver->name);
DRV_UNLOCK();
return -ENOMEM;
@@ -269,27 +307,14 @@ int i2c_add_driver(struct i2c_driver *driver)
DEB(printk("i2c-core.o: driver %s registered.\n",driver->name));
- /* Notify all existing adapters and clients to dummy driver */
ADAP_LOCK();
- if (driver->flags&I2C_DF_DUMMY) {
- for (i=0; i<I2C_ADAP_MAX; i++) {
- if (adapters[i]) {
- driver->attach_adapter(adapters[i]);
- for (j=0; j<I2C_CLIENT_MAX; j++)
- if (adapters[i]->clients[j])
- driver->detach_client(
- adapters[i]->clients[j]);
- }
- }
- ADAP_UNLOCK();
- return 0;
- }
/* now look for instances of driver on our adapters
*/
- if ( driver->flags&I2C_DF_NOTIFY ) {
+ if (driver->flags& (I2C_DF_NOTIFY|I2C_DF_DUMMY)) {
for (i=0;i<I2C_ADAP_MAX;i++)
if (adapters[i]!=NULL)
+ /* Ignore errors */
driver->attach_adapter(adapters[i]);
}
ADAP_UNLOCK();
@@ -298,14 +323,15 @@ int i2c_add_driver(struct i2c_driver *driver)
int i2c_del_driver(struct i2c_driver *driver)
{
- int i,j,k;
+ int i,j,k,res;
DRV_LOCK();
for (i = 0; i < I2C_DRIVER_MAX; i++)
if (driver == drivers[i])
break;
if (I2C_DRIVER_MAX == i) {
- printk(KERN_WARNING " i2c-core.o: unregister_driver: [%s] not found\n",
+ printk(KERN_WARNING " i2c-core.o: unregister_driver: "
+ "[%s] not found\n",
driver->name);
DRV_UNLOCK();
return -ENODEV;
@@ -320,19 +346,52 @@ int i2c_del_driver(struct i2c_driver *driver)
* pointers.
*/
ADAP_LOCK(); /* should be moved inside the if statement... */
- if ((driver->flags&I2C_DF_DUMMY)==0)
for (k=0;k<I2C_ADAP_MAX;k++) {
struct i2c_adapter *adap = adapters[k];
if (adap == NULL) /* skip empty entries. */
continue;
- DEB2(printk("i2c-core.o: examining adapter %s:\n",adap->name));
- for (j=0;j<I2C_CLIENT_MAX;j++) {
- struct i2c_client *client = adap->clients[j];
- if (client != NULL && client->driver == driver) {
- DEB2(printk("i2c-core.o: detaching client %s:\n",
- client->name));
- /*i2c_detach_client(client);*/
- driver->detach_client(client);
+ DEB2(printk("i2c-core.o: examining adapter %s:\n",
+ adap->name));
+ if (driver->flags & I2C_DF_DUMMY) {
+ /* DUMMY drivers do not register their clients, so we have to
+ * use a trick here: we call driver->attach_adapter to
+ * *detach* it! Of course, each dummy driver should know about
+ * this or hell will break loose...
+ */
+ if ((res = driver->attach_adapter(adap))) {
+ printk("i2c-core.o: while unregistering "
+ "dummy driver %s, adapter %s could "
+ "not be detached properly; driver "
+ "not unloaded!",driver->name,
+ adap->name);
+ ADAP_UNLOCK();
+ return res;
+ }
+ } else {
+ for (j=0;j<I2C_CLIENT_MAX;j++) {
+ struct i2c_client *client = adap->clients[j];
+ if (client != NULL &&
+ client->driver == driver) {
+ DEB2(printk("i2c-core.o: "
+ "detaching client %s:\n",
+ client->name));
+ if ((res = driver->
+ detach_client(client)))
+ {
+ printk("i2c-core.o: while "
+ "unregistering driver "
+ "`%s', the client at "
+ "address %02x of
+ adapter `%s' could not
+ be detached; driver
+ not unloaded!",
+ driver->name,
+ client->addr,
+ adap->name);
+ ADAP_UNLOCK();
+ return res;
+ }
+ }
}
}
}
@@ -374,12 +433,18 @@ int i2c_attach_client(struct i2c_client *client)
adapter->clients[i] = client;
adapter->client_count++;
- i2c_dummy_client(client);
- if (adapter->client_register != NULL)
- adapter->client_register(client);
+ if (adapter->client_register)
+ if (adapter->client_register(client))
+ printk("i2c-core.o: warning: client_register seems "
+ "to have failed for client %02x at adapter %s\n",
+ client->addr,adapter->name);
DEB(printk("i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n",
client->name, adapter->name,i));
+
+ if(client->flags & I2C_CLIENT_ALLOW_USE)
+ client->usage_count = 0;
+
return 0;
}
@@ -387,24 +452,31 @@ int i2c_attach_client(struct i2c_client *client)
int i2c_detach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
- int i;
+ int i,res;
for (i = 0; i < I2C_CLIENT_MAX; i++)
if (client == adapter->clients[i])
break;
if (I2C_CLIENT_MAX == i) {
- printk(KERN_WARNING " i2c-core.o: unregister_client [%s] not found\n",
+ printk(KERN_WARNING " i2c-core.o: unregister_client "
+ "[%s] not found\n",
client->name);
return -ENODEV;
}
-
+
+ if( (client->flags & I2C_CLIENT_ALLOW_USE) &&
+ (client->usage_count>0))
+ return -EBUSY;
+
if (adapter->client_unregister != NULL)
- adapter->client_unregister(client);
- /* client->driver->detach_client(client);*/
+ if ((res = adapter->client_unregister(client))) {
+ printk("i2c-core.o: client_unregister [%s] failed, "
+ "client not detached",client->name);
+ return res;
+ }
adapter->clients[i] = NULL;
adapter->client_count--;
- i2c_dummy_client(client);
DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name));
return 0;
@@ -422,7 +494,7 @@ void i2c_inc_use_client(struct i2c_client *client)
void i2c_dec_use_client(struct i2c_client *client)
{
-
+
if (client->driver->dec_use != NULL)
client->driver->dec_use(client);
@@ -430,6 +502,107 @@ void i2c_dec_use_client(struct i2c_client *client)
client->adapter->dec_use(client->adapter);
}
+struct i2c_client *i2c_get_client(int driver_id, int adapter_id,
+ struct i2c_client *prev)
+{
+ int i,j;
+
+ /* Will iterate through the list of clients in each adapter of adapters-list
+ in search for a client that matches the search criteria. driver_id or
+ adapter_id are ignored if set to 0. If both are ignored this returns
+ first client found. */
+
+ i = j = 0;
+
+ /* set starting point */
+ if(prev)
+ {
+ if(!(prev->adapter))
+ return (struct i2c_client *) -EINVAL;
+
+ for(j=0; j < I2C_ADAP_MAX; j++)
+ if(prev->adapter == adapters[j])
+ break;
+
+ /* invalid starting point? */
+ if (I2C_ADAP_MAX == j) {
+ printk(KERN_WARNING " i2c-core.o: get_client adapter for client:[%s] not found\n",
+ prev->name);
+ return (struct i2c_client *) -ENODEV;
+ }
+
+ for(i=0; i < I2C_CLIENT_MAX; i++)
+ if(prev == adapters[j]->clients[i])
+ break;
+
+ /* invalid starting point? */
+ if (I2C_CLIENT_MAX == i) {
+ printk(KERN_WARNING " i2c-core.o: get_client client:[%s] not found\n",
+ prev->name);
+ return (struct i2c_client *) -ENODEV;
+ }
+
+ i++; /* start from one after prev */
+ }
+
+ for(; j < I2C_ADAP_MAX; j++)
+ {
+ if(!adapters[j])
+ continue;
+
+ if(adapter_id && (adapters[j]->id != adapter_id))
+ continue;
+
+ for(; i < I2C_CLIENT_MAX; i++)
+ {
+ if(!adapters[j]->clients[i])
+ continue;
+
+ if(driver_id && (adapters[j]->clients[i]->driver->id != driver_id))
+ continue;
+ if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE)
+ return adapters[j]->clients[i];
+ }
+ }
+
+ return 0;
+}
+
+int i2c_use_client(struct i2c_client *client)
+{
+ if(client->flags & I2C_CLIENT_ALLOW_USE) {
+ if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE)
+ client->usage_count++;
+ else {
+ if(client->usage_count > 0)
+ return -EBUSY;
+ else
+ client->usage_count++;
+ }
+ }
+
+ i2c_inc_use_client(client);
+
+ return 0;
+}
+
+int i2c_release_client(struct i2c_client *client)
+{
+ if(client->flags & I2C_CLIENT_ALLOW_USE) {
+ if(client->usage_count>0)
+ client->usage_count--;
+ else
+ {
+ printk(KERN_WARNING " i2c-core.o: dec_use_client used one too many times\n");
+ return -EPERM;
+ }
+ }
+
+ i2c_dec_use_client(client);
+
+ return 0;
+}
+
/* ----------------------------------------------------
* The /proc functions
* ----------------------------------------------------
@@ -579,28 +752,6 @@ int i2cproc_cleanup(void)
#endif /* def CONFIG_PROC_FS */
-/* ---------------------------------------------------
- * dummy driver notification
- * ---------------------------------------------------
- */
-
-static void i2c_dummy_adapter(struct i2c_adapter *adap)
-{
- int i;
- for (i=0; i<I2C_DRIVER_MAX; i++)
- if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY))
- drivers[i]->attach_adapter(adap);
-}
-
-static void i2c_dummy_client(struct i2c_client *client)
-{
- int i;
- for (i=0; i<I2C_DRIVER_MAX; i++)
- if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY))
- drivers[i]->detach_client(client);
-}
-
-
/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
@@ -1187,6 +1338,9 @@ EXPORT_SYMBOL(i2c_attach_client);
EXPORT_SYMBOL(i2c_detach_client);
EXPORT_SYMBOL(i2c_inc_use_client);
EXPORT_SYMBOL(i2c_dec_use_client);
+EXPORT_SYMBOL(i2c_get_client);
+EXPORT_SYMBOL(i2c_use_client);
+EXPORT_SYMBOL(i2c_release_client);
EXPORT_SYMBOL(i2c_check_addr);