home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
lxapi32.zip
/
I2C
/
i2ccore.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-04-26
|
21KB
|
687 lines
/* $Id: i2ccore.c,v 1.2 2002/04/26 23:08:58 smilcke Exp $ */
/*
* i2ccore.c
* Autor: Stefan Milcke
* Erstellt am: 30.10.2001
* Letzte Aenderung am: 14.03.2002
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/i2c.h>
#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)
#ifdef USEPRINTK
#define DEB(x) if (i2c_debug>=1) x;
#define DEB2(x) if (i2c_debug>=2) x;
#else
#define DEB(x)
#define DEB2(x)
#endif
/**** 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;
//------------------------------ i2c_add_adapter -------------------------------
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)
{
CPK(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);
// 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;
ERROR0:
ADAP_UNLOCK();
return res;
}
//------------------------------ i2c_del_adapter -------------------------------
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)
{
CPK(printk("i2ccore: 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)))
{
CPK(printk("i2ccore: 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 up.
for(j=0;j<I2C_CLIENT_MAX;j++)
{
struct i2c_client *client=adap->clients[j];
if(client!=NULL)
if((res=client->driver->detach_client(client)))
{
CPK(printk("i2ccore: adapter %s not unregistered, because client at address %02x can't be detached."
,adap->name,client->addr));
goto ERROR0;
}
}
adapters[i]=NULL;
adap_count--;
ADAP_UNLOCK();
CPK(printk("i2ccore: adapter unregistered: %s\n",adap->name));
return 0;
ERROR0:
ADAP_UNLOCK();
return res;
ERROR1:
DRV_UNLOCK();
return res;
}
#ifdef TARGET_OS2
unsigned long OS2_i2c_add_driver(struct i2c_driver *driver);
unsigned long OS2_i2c_del_driver(struct i2c_driver *driver);
#endif
//------------------------------- i2c_add_driver -------------------------------
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)
{
CPK(printk(KERN_WARNING "i2ccore: 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("i2ccore: 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)
driver->attach_adapter(adapters[i]);
}
ADAP_UNLOCK();
#ifdef TARGET_OS2
OS2_i2c_add_driver(driver);
#endif
return 0;
}
//------------------------------- i2c_del_driver -------------------------------
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)
{
CPK(printk(KERN_WARNING "i2ccore: 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 attached.
// If so, detach them to be able to kill the driver afterwards.
DEB2(printk("i2ccore: unregister_driver - looking for clients.\n"));
// removing clients does not depend on the notify flag, else invalid operation
// might (will!) result, when using stale client pointers.
ADAP_LOCK();
for(k=0;k<I2C_ADAP_MAX;k++)
{
struct i2c_adapter *adap=adapters[k];
if(adap==NULL) // skip empty entries
continue;
DEB2(printk("i2ccore: 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)))
{
CPK(printk("i2ccore: 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("i2ccore: detaching client %s:\n",client->name));
if((res=driver->detach_client(client)))
{
CPK(printk("i2ccore: while unregistering driver %s the client at address %02x of adapters %s could not be detached; driver not unloaded!"
,driver->name,client->addr,adap->name));
ADAP_UNLOCK();
return res;
}
}
}
}
}
ADAP_UNLOCK();
drivers[i]=NULL;
driver_count--;
DRV_UNLOCK();
DEB(printk("i2ccore: driver unregistered: %s\n",driver->name));
#ifdef TARGET_OS2
OS2_i2c_del_driver(driver);
#endif
return 0;
}
//------------------------------- i2c_check_addr -------------------------------
int i2c_check_addr(struct i2c_adapter *adapter,int addr)
{
int i;
for(i=0;i<I2C_CLIENT_MAX;i++)
if(adapter->clients[i]&&(adapter->clients[i]->addr==addr))
return -EBUSY;
return 0;
}
//----------------------------- i2c_attach_client ------------------------------
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter=client->adapter;
int i;
if(i2c_check_addr(client->adapter,client->addr))
return -EBUSY;
for(i=0;i<I2C_CLIENT_MAX;i++)
if(NULL==adapter->clients[i])
break;
if(I2C_CLIENT_MAX==i)
{
CPK(printk(KERN_WARNING "i2ccore: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n"
,client->name));
return -ENOMEM;
}
adapter->clients[i]=client;
adapter->client_count++;
if(adapter->client_register)
if(adapter->client_register(client))
CPK(printk("i2ccore: warning: client_register seems to have failed for client %02x at adapter %s\n"
,client->addr,adapter->name));
DEB(printk("i2ccore: 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;
}
//----------------------------- i2c_detach_client ------------------------------
int i2c_detach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int i,res;
for(i = 0; i < I2C_CLIENT_MAX; i++)
if (client == adapter->clients[i])
break;
if(I2C_CLIENT_MAX == i)
{
CPK(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)
if ((res = adapter->client_unregister(client)))
{
CPK(printk("i2c-core.o: client_unregister [%s] failed, client not detached"
,client->name));
return res;
}
adapter->clients[i] = NULL;
adapter->client_count--;
DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name));
return 0;
}
//----------------------------- i2c_inc_use_client -----------------------------
void i2c_inc_use_client(struct i2c_client *client)
{
if(client->driver->inc_use != NULL)
client->driver->inc_use(client);
if(client->adapter->inc_use != NULL)
client->adapter->inc_use(client->adapter);
}
//----------------------------- i2c_dec_use_client -----------------------------
void i2c_dec_use_client(struct i2c_client *client)
{
if(client->driver->dec_use != NULL)
client->driver->dec_use(client);
if(client->adapter->dec_use != NULL)
client->adapter->dec_use(client->adapter);
}
//-------------------------------- i2c_transfer --------------------------------
int i2c_transfer(struct i2c_adapter *adap,struct i2c_msg msgs[],int num)
{
int ret;
if(adap->algo->master_xfer)
{
DEB2(printk("i2ccore: master_xfer: %s with %d msgs.\n"
,adap->name,num));
I2C_LOCK(adap);
ret=adap->algo->master_xfer(adap,msgs,num);
I2C_UNLOCK(adap);
return ret;
}
else
{
CPK(printk("i2ccore: I2C adapter %04x: I2C level transfers not supported\n"
,adap->id));
return -ENOSYS;
}
}
//------------------------------ i2c_master_send -------------------------------
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
if (client->adapter->algo->master_xfer)
{
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
(const char *)msg.buf = buf;
CPK(printk("i2ccore: master_send: writing %d bytes on %s.\n",
count,client->adapter->name));
I2C_LOCK(adap);
ret = adap->algo->master_xfer(adap,&msg,1);
I2C_UNLOCK(adap);
// if everything went ok (i.e. 1 msg transmitted), return #bytes
// transmitted, else error code.
return (ret == 1 )? count : ret;
}
else
{
CPK(printk("i2ccore: I2C adapter %04x: I2C level transfers not supported\n",
client->adapter->id));
return -ENOSYS;
}
}
//------------------------------ i2c_master_recv -------------------------------
int i2c_master_recv(struct i2c_client *client,char *buf,int count)
{
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
int ret;
if(client->adapter->algo->master_xfer)
{
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
CPK(printk("i2ccore: master_recv: reading %d bytes on %s.\n",
count,client->adapter->name));
I2C_LOCK(adap);
ret = adap->algo->master_xfer(adap,&msg,1);
I2C_UNLOCK(adap);
CPK(printk("i2ccore: master_recv: return:%d (count:%d, addr:0x%02x)\n",
ret, count, client->addr));
// if everything went ok (i.e. 1 msg transmitted), return #bytes
// transmitted, else error code.
return (ret == 1 )? count : ret;
}
else
{
CPK(printk("i2ccore: I2C adapter %04x: I2C level transfers not supported\n",
client->adapter->id));
return -ENOSYS;
}
}
//--------------------------------- i2c_probe ----------------------------------
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
i2c_client_found_addr_proc *found_proc)
{
int addr,i,found,err;
int adap_id = i2c_adapter_id(adapter);
// Forget it if we can't probe using SMBUS_QUICK
if(!i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK))
return -1;
for(addr = 0x00; addr <= 0x7f; addr++)
{ // Skip if already in use */
if(i2c_check_addr(adapter,addr))
continue;
// If it is in one of the force entries, we don't do any detection at all
found = 0;
for(i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3)
{
if(((adap_id == address_data->force[i]) ||
(address_data->force[i] == ANY_I2C_BUS)) &&
(addr == address_data->force[i+1]))
{
DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n"
,adap_id,addr));
if((err = found_proc(adapter,addr,0,0)))
return err;
found = 1;
}
}
if(found)
continue;
// If this address is in one of the ignores, we can forget about it right now
for (i = 0;!found && (address_data->ignore[i] != I2C_CLIENT_END); i += 2)
{
if(((adap_id == address_data->ignore[i]) ||
((address_data->ignore[i] == ANY_I2C_BUS))) &&
(addr == address_data->ignore[i+1]))
{
DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, addr %04x\n"
,adap_id ,addr));
found = 1;
}
}
for(i = 0;!found && (address_data->ignore_range[i] != I2C_CLIENT_END);i += 3)
{
if(((adap_id == address_data->ignore_range[i]) ||
((address_data->ignore_range[i]==ANY_I2C_BUS))) &&
(addr >= address_data->ignore_range[i+1]) &&
(addr <= address_data->ignore_range[i+2]))
{
DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, addr %04x\n"
, adap_id,addr));
found = 1;
}
}
if(found)
continue;
// Now, we will do a detection, but only if it is in the normal or probe entries
for(i = 0;!found && (address_data->normal_i2c[i] != I2C_CLIENT_END);i += 1)
{
if(addr == address_data->normal_i2c[i])
{
found = 1;
DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, addr %02x"
, adap_id,addr));
}
}
for(i = 0;!found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END);i+=2)
{
if((addr >= address_data->normal_i2c_range[i]) &&
(addr <= address_data->normal_i2c_range[i+1]))
{
found = 1;
DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, addr %04x\n"
, adap_id,addr));
}
}
for(i = 0;!found && (address_data->probe[i] != I2C_CLIENT_END);i += 2)
{
if(((adap_id == address_data->probe[i]) ||
((address_data->probe[i] == ANY_I2C_BUS))) &&
(addr == address_data->probe[i+1]))
{
found = 1;
DEB2(printk("i2c-core.o: found probe parameter for adapter %d, addr %04x\n"
, adap_id,addr));
}
}
for(i = 0;!found && (address_data->probe_range[i] != I2C_CLIENT_END);i += 3)
{
if(((adap_id == address_data->probe_range[i]) ||
(address_data->probe_range[i] == ANY_I2C_BUS)) &&
(addr >= address_data->probe_range[i+1]) &&
(addr <= address_data->probe_range[i+2]))
{
found = 1;
DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, addr %04x\n"
, adap_id,addr));
}
}
if(!found)
continue;
// OK, so we really should examine this address. First check whether there is some client here at all!
if(i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
if((err = found_proc(adapter,addr,0,-1)))
return err;
}
return 0;
}
//------------------------------- i2c_adapter_id -------------------------------
int i2c_adapter_id(struct i2c_adapter *adap)
{
int i;
for(i=0;i<I2C_ADAP_MAX;i++)
if(adap==adapters[i])
return i;
return -1;
}
//--------------------------- i2c_smbus_xfer_emulate ---------------------------
// Simulate a SMBus command using the i2c protocol No checking of parameters is done!
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
// So we need to generate a series of msgs. In the case of writing, we need to
// use only one message; when reading, we need two. We initialize most things
// with sane defaults, to keep the code below somewhat simpler.
unsigned char msgbuf0[34];
unsigned char msgbuf1[34];
int num = read_write == I2C_SMBUS_READ?2:1;
int i;
struct i2c_msg msg[2];
msg[0].addr=addr;
msg[0].flags=flags;
msg[0].len=1;
msg[0].buf=msgbuf0;
msg[1].addr=addr;
msg[1].flags=flags | I2C_M_RD;
msg[1].len=0;
msg[1].buf=msgbuf1;
msgbuf0[0] = command;
switch(size)
{
case I2C_SMBUS_QUICK:
msg[0].len = 0;
// Special case: The read/write field is used as data
msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;
num = 1;
break;
case I2C_SMBUS_BYTE:
if(read_write == I2C_SMBUS_READ)
{ // Special case: only a read!
msg[0].flags = I2C_M_RD | flags;
num = 1;
}
break;
case I2C_SMBUS_BYTE_DATA:
if(read_write == I2C_SMBUS_READ)
msg[1].len = 1;
else
{
msg[0].len = 2;
msgbuf0[1] = data->byte;
}
break;
case I2C_SMBUS_WORD_DATA:
if(read_write == I2C_SMBUS_READ)
msg[1].len = 2;
else
{
msg[0].len=3;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = (data->word >> 8) & 0xff;
}
break;
case I2C_SMBUS_PROC_CALL:
num = 2; /* Special case */
msg[0].len = 3;
msg[1].len = 2;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = (data->word >> 8) & 0xff;
break;
case I2C_SMBUS_BLOCK_DATA:
if(read_write == I2C_SMBUS_READ)
{
CPK(printk("i2c-core.o: Block read not supported under I2C emulation!\n"));
return -1;
}
else
{
msg[0].len = data->block[0] + 2;
if(msg[0].len > 34)
{
CPK(printk("i2c-core.o: smbus_access called with invalid block write size (%d)\n",
msg[0].len));
return -1;
}
for(i = 1; i <= msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
}
break;
default:
CPK(printk("i2c-core.o: smbus_access called with invalid size (%d)\n",size));
return -1;
}
if(i2c_transfer(adapter, msg, num) < 0)
return -1;
if(read_write == I2C_SMBUS_READ)
switch(size)
{
case I2C_SMBUS_BYTE:
data->byte = msgbuf0[0];
break;
case I2C_SMBUS_BYTE_DATA:
data->byte = msgbuf1[0];
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
break;
}
return 0;
}
//------------------------------- i2c_smbus_xfer -------------------------------
s32 i2c_smbus_xfer(struct i2c_adapter *adapter,u16 addr
,unsigned short flags
,char read_write,u8 command,int size
,union i2c_smbus_data *data)
{
s32 res;
flags=flags&I2C_M_TEN;
if(adapter->algo->smbus_xfer)
{
I2C_LOCK(adapter);
res=adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data);
I2C_UNLOCK(adapter);
}
else
res=i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);
return res;
}
//--------------------------- i2c_get_functionality ----------------------------
// You should always define 'functionality'; the 'else' is just for
// backward compatibility.
u32 i2c_get_functionality(struct i2c_adapter *adap)
{
if(adap->algo->functionality)
return adap->algo->functionality(adap);
else
return 0xffffffff;
}
//-------------------------- i2c_check_functionality ---------------------------
int i2c_check_functionality(struct i2c_adapter *adap,u32 func)
{
u32 adap_func=i2c_get_functionality(adap);
return (func & adap_func)==func;
}
//---------------------------------- i2c_init ----------------------------------
int __init i2c_init(void)
{
CPK(printk("i2ccore: i2c core module\n"));
memset(adapters,0,sizeof(adapters));
memset(drivers,0,sizeof(drivers));
adap_count=0;
driver_count=0;
init_MUTEX(&adap_lock);
init_MUTEX(&driver_lock);
return 0;
}