#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/config.h>
#include <linux/i2c.h>
/* ----- compatibility stuff ----------------------------------------------- */
#include <linux/version.h>
#include <linux/init.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
#endif
#include <asm/uaccess.h>
/* ----- global defines ---------------------------------------------------- */
/* exclusive access to the bus */
#define I2C_LOCK(adap) down(&adap->lock)
#define I2C_UNLOCK(adap) up(&adap->lock)
#define ADAP_LOCK() down(&adap_lock)
#define ADAP_UNLOCK() up(&adap_lock)
#define DRV_LOCK() down(&driver_lock)
#define DRV_UNLOCK() up(&driver_lock)
#define DEB(x) if (i2c_debug>=1) x;
#define DEB2(x) if (i2c_debug>=2) x;
/* ----- global variables -------------------------------------------------- */
/**** lock for writing to global variables: the adapter & driver list */
struct semaphore adap_lock;
struct semaphore driver_lock;
/**** adapter list */
static struct i2c_adapter *adapters[I2C_ADAP_MAX];
static int adap_count;
/**** drivers list */
static struct i2c_driver *drivers[I2C_DRIVER_MAX];
static int driver_count;
/**** debug level */
static int i2c_debug = 0;
/* ---------------------------------------------------
* /proc entry declarations
*----------------------------------------------------
*/
#ifdef CONFIG_PROC_FS
static int i2cproc_init(void);
static void i2cproc_cleanup(void);
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27))
static void monitor_bus_i2c(struct inode *inode, int fill);
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count,
loff_t *ppos);
static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
int *eof , void *private);
/* To implement the dynamic /proc/bus/i2c-? files, we need our own
implementation of the read hook */
static struct file_operations i2cproc_operations = {
read: i2cproc_bus_read,
};
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,48))
static struct inode_operations i2cproc_inode_operations = {
&i2cproc_operations
};
#endif
static int i2cproc_initialized = 0;
#else /* undef CONFIG_PROC_FS */
#define i2cproc_init() 0
#define i2cproc_cleanup() 0
#endif /* CONFIG_PROC_FS */
/* ---------------------------------------------------
* registering functions
* ---------------------------------------------------
*/
/* -----
* i2c_add_adapter is called from within the algorithm layer,
* when a new hw adapter registers. A new device is register to be
* available for clients.
*/
int i2c_add_adapter(struct i2c_adapter *adap)
{
int i,j,res;
ADAP_LOCK();
for (i = 0; i < I2C_ADAP_MAX; i++)
if (NULL == adapters[i])
break;
if (I2C_ADAP_MAX == i) {
printk(KERN_WARNING
" i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n",
adap->name);
res = -ENOMEM;
goto ERROR0;
}
adapters[i] = adap;
adap_count++;
ADAP_UNLOCK();
/* init data types */
init_MUTEX(&adap->lock);
#ifdef CONFIG_PROC_FS
if (i2cproc_initialized) {
char name[8];
struct proc_dir_entry *proc_entry;
sprintf(name,"i2c-%d", i);
proc_entry = create_proc_entry(name,0,proc_bus);
if (! proc_entry) {
printk("i2c-core.o: Could not create /proc/bus/%s\n",
name);
res = -ENOENT;
goto ERROR1;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,48))
proc_entry->proc_fops = &i2cproc_operations;
#else
proc_entry->ops = &i2cproc_inode_operations;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27))
proc_entry->owner = THIS_MODULE;
#else
proc_entry->fill_inode = &monitor_bus_i2c;
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
adap->inode = proc_entry->low_ino;
}
#endif /* def CONFIG_PROC_FS */
/* 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|I2C_DF_DUMMY)))
/* We ignore the return code; if it fails, too bad */
drivers[j]->attach_adapter(adap);
DRV_UNLOCK();
DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n",
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,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",
adap->name);
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 "
"unregistered, because client at "
"address %02x can't be detached. ",
adap->name, client->addr);
goto ERROR0;
}
}
#ifdef CONFIG_PROC_FS
if (i2cproc_initialized) {
char name[8];
sprintf(name,"i2c-%d", i);
remove_proc_entry(name,proc_bus);
}
#endif /* def CONFIG_PROC_FS */
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;
}
/* -----
* What follows is the "upwards" interface: commands for talking to clients,
* which implement the functions to access the physical information of the
* chips.
*/
int i2c_add_driver(struct i2c_driver *driver)
{
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",
driver->name);
DRV_UNLOCK();
return -ENOMEM;
}
drivers[i] = driver;
driver_count++;
DRV_UNLOCK(); /* driver was successfully added */
DEB(printk("i2c-core.o: driver %s registered.\n",driver->name));
ADAP_LOCK();
/* now look for instances of driver on our adapters
*/
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();
return 0;
}
int i2c_del_driver(struct i2c_driver *driver)
{
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",
driver->name);
DRV_UNLOCK();
return -ENODEV;
}
/* Have a look at each adapter, if clients of this driver are still
* a