分类: LINUX
2008-12-18 13:56:40
1、 LINUX的I2C驱动架构
LINUX中I2C总线的驱动分为两个部分:总线驱动(BUS)和设备驱动(DEVICE)。
总线驱动的职责是:为系统中每个I2C总线增加相应的读写方法。但是总线驱动本身不进行任何的通讯,它等待设备驱动调用其函数。
设备驱动是挂在I2C总线上的具体的设备通讯的驱动。通过I2C总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其实现细节地与硬件设备进行通讯。
图1 LINUX下I2C驱动架构图
2、 总线驱动
系统启动后,首先装载的是I2C总线驱动。一个总线驱动用于支持一条特定的I2C总线的读写。一个总线驱动通常需要两个模块:struct i2c_adapter,struct i2c_algorithm。
linux-2.6.19.2_mx27/include/linux/i2c.h:
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class;
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* --- administration stuff. */
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
/* data fields that are valid for all devices */
struct mutex bus_lock;
struct mutex clist_lock;
int timeout;
int retries;
struct device dev; /* the adapter device */
struct class_device class_dev; /* the class device */
int nr;
struct list_head clients;
struct list_head list;
char name[I2C_NAME_SIZE];
struct completion dev_released;
struct completion class_dev_released;
};
这个模块没有提供读写函数,具体的读写方法由第二个模块:struct i2c_algorithm提供。
I2C Algorithm是为了I2C总线驱动和I2C总线之间能够对话。
I2C Algorithm的具体“算法”实现在driver/i2c/algos/i2c-algo-***.c中。
linux-2.6.19.2_mx27/include/linux/i2c.h:
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data);
/* --- ioctl like call to set div. parameters. */
int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
这个模块为总线驱动增加读写“算法”。通常情况下每个I2C总线驱动都定义了一个自己的读写算法,但是鉴于有些总线使用相同的算法,所以可以共用同一套读写算法。
然后通过:linux-2.6.19.2_mx27/drivers/i2c/i2c-core.c:
int i2c_add_adapter(struct i2c_adapter *adap);
具体的调用流程是:
linux-2.6.19.2_mx27/drivers/i2c/busses/i2c-ite.c: module_init(iic_ite_init);
linux-2.6.19.2_mx27/drivers/i2c/i2c-core.c: int i2c_add_adapter(struct i2c_adapter *adap)
3、 设备驱动
总线驱动只提供了对一条总线的读写机制,本身并不会去做通信。通信是由I2C设备驱动来做的,设备驱动通过I2C总线同具体的设备进行通信。
一个设备驱动有两个模块来描述:struct i2c_driver和struct i2c_client。
系统启动后,I2C总线驱动加载完成后,就可以加载设备驱动。
首先装入如下结构:
linux-2.6.19.2_mx27/include/linux/i2c.h:
struct i2c_driver {
int id;
unsigned int class;
/* Notifies the driver that a new bus has appeared. This routine
* 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.
*/
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct i2c_adapter *);
/* 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.
*/
int (*detach_client)(struct i2c_client *);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
struct device_driver driver;
struct list_head list;
};
这个i2c_driver一旦装入完成后,其中的attach_adapter函数会被调用。在其中可以遍历系统中的每个I2C总线驱动,探测想要访问的设备:
注意:探测可能会找到多个设备,因而不仅一个I2C总线可以挂多个不同类型的设备,一个设备驱动也可以同时为挂在多个不同I2C总线上的设备服务。
每当设备驱动探测到一个它能支持的设备,它就创建一个struct i2c_client来标识这个设备:
linux-2.6.19.2_mx27/include/linux/i2c.h:
struct i2c_client {
unsigned int flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
int usage_count; /* How many accesses currently */
/* to the client */
struct device dev; /* the device structure */
struct list_head list;
char name[I2C_NAME_SIZE];
struct completion released;
};
一个i2c_client代表着位于adapter总线上,地址为address,使用driver来驱动的一个设备。它将总线驱动和设备驱动,以及设备地址绑定在了一起。一个i2c_client就代表着一个I2C设备。
当得到I2C设备以后,就可以直接对此设备进行读写了:
这两个函数的具体实现是在:linux-2.6.19.2_mx27/drivers/i2c/i2c-core.c:中。
/*
* The master routines are the ones normally used to transmit data to devices
* on a bus (or read from them). Apart from two basic transfer functions to
* transmit one message at a time, a more complex version can be used to
* transmit an arbitrary number of messages without interruption.
*/
extern int i2c_master_send(struct i2c_client *,const char* ,int);
extern int i2c_master_recv(struct i2c_client *,char* ,int);