在linux中iic是以子系统的方式存在,我们查看2.6.32.2的内核源代码结构:
在/drivers/iic目录下有algos busses chips文件夹,有i2c-core.c i2c-dev.c i2c-boardinfo.c 源文件,那么这些分别代表什么呢?
Algos目录下主要存放I2C总线适配器的algorithm。也就是I2C通信的一些算法,比如多少频率等等。Busses目录具体控制器的I2C总线驱动,其中就有s3c24xx的驱动。Chips目录主要存放特定设备的IIC驱动。比如EEPROM等。
那么i2c-core.c是整个I2C驱动的核心代码。是连接应用程序和具体驱动程序的枢纽。
I2C-dev.c存放着应用程序的接口,和rtc-dev.c的功能是类似的。i2c-boardinfo.c存放一些板级包的信息,对于我们并不是很重要。
结合上文所说,Linux的IIC体系可以用以下图解释。
其中Driver对应的是i2c_driver结构
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this if you can, it will probably
* be removed in a near future.
*/
/*当调用i2c_add_driver向内核添加i2c驱动设备时被调用*/
int (*attach_adapter)(struct i2c_adapter *);
/*当调用i2c_del_driver向内核删除i2c驱动设备时被调用*/
int (*detach_adapter)(struct i2c_adapter *);
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(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;
/*如果一个驱动支持多个设备,idtable包含设备的ID*/
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
const struct i2c_client_address_data *address_data;
struct list_head clients;
};
i2c_client对应一个具体的设备:当我们使用open write系统调用就是通过这个结构传递信息:
struct i2c_client {
unsigned short flags; /* div., see below */
/*芯片低七位地址*/
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
i2c_algorithm对应I2C的传输算法控制:
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 */
/*I2C传输函数指针*/
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
/*SMBus传输之后走*/
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
/*返回适配器支持的功能*/
u32 (*functionality) (struct i2c_adapter *);
};
i2c_algorithm
分析以上结构,我们知道
I2c_adapter对应一套iic适配器,什么是适配器呢,就是iic控制器,在2440中就是iic控制单元,在该结构中有i2c_algorithm结构,一个iic控制器需要这个结构提供iic总线的访问周期等,其中在i2c_algorithm的master_xfer函数指针中,有i2c_msg结构
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
我们就是以这个结构来传输数据的。
Iic_driver是对应一套驱动方法,iic_client对应的是一个具体的物理设备,里面有物理地址,访问标志等,在iic_client结构中,有Iic_driver和iic_adapter结构,即一个具体的设备应该有相应的iic适配器(iic控制器)和对应的驱动。Iic_client依附于iic_adapter。
下面介绍一个具体的i2c驱动在内核是如何注册的:拿s3c2440为例子
如果内核支持了iic驱动,内核会首先注册一个iic-core.c中的i2c_init函数,在i2c_init中调用i2c-core.c 的i2c_register_driver.
static int __init i2c_init(void)
{
static int res;
static int first = 1;
if (!first)
return res;
first = 0;
/* Setup and enable the DATA and CLK pins */
res = crisv32_io_get_name(&cris_i2c_data,
CONFIG_ETRAX_V32_I2C_DATA_PORT);
if (res < 0)
return res;
res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_V32_I2C_CLK_PORT);
crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out);
return res;
}
static struct i2c_driver dummy_driver = {
.driver.name = "dummy",
.probe = dummy_probe,
.remove = dummy_remove,
.id_table = dummy_id,
};
在执行i2c_add_driver向内核添加i2c驱动时,会调用i2c_register_driver,i2c_register_driver就调用dummy_driver中的dummy_probe函数。
那么为什么要先执行一个i2c_init呢。内核中的解释是:
大概意思就是因为一些子系统在subsys_initcall中注册i2c driver(例如i2c-s3c2410),而这些子系统是跟这个有关系的
然后会注册具体平台的iic驱动,比如i2c-s3c2410.c
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
其中subsys_initcall在内存中的位置比module_init的位置更优先执行,所以先执行i2c_adap_s3c_init
在之前rtc已经降到,在mach_mini2440中,在mini2440_machine_init()函数中会添加各种platform device,其中就有iic设备:
在执行platform_driver_register时,
通过这个代码我们知道,platform_driver中的device_driver结构的probe以及remove shutdown这些函数指针就和platform_driver的probe remove shutdown一样了,其中platform_bus_type这个总线结构中有个match函数指针
这样我们可以知道,如果platform_driver结构中存在id_table域先测试platform_driver这个结构中的id_table和platform_device的name域是否一致,然后测试platform_driver和platform_device结构是否一致。如果一致就会执行platform_driver的probe函数。
接下来执行s3c24xx_i2c_probe函数
在该函数中主要有以下工作
启动IIC时钟
1 相应寄存器映射
2 申请IIC中断
3 调用s3c24xx_i2c_init初始化IIC控制器
4调用i2c_add_numbered_adapter添加i2c_adapter结构
下面主要讲解第四点:
调用i2c_add_numbered_adapter
函数最终调用i2c_register_adapter注册一个adapter结构
在i2c_register_adapter函数中
该函数的意思是遍历整个IIC总线,当匹配值执行i2c_do_add_adapter函数,
这样就会执行i2c_driver结构中的attacg_adapter函数。但是这时候的driver->attach_adapter是空的,所以不会执行。
下面就执行i2c-dev.c中的i2c_dev_init函数,
该函数主要做了以下事情:
1 注册cdev结构,主设备号为89
2 向i2c子系统添加i2c_driver
对于第二点,这里就会调用i2c-core.c的i2c_register_driver,i2c_register_driver再调用i2c-dev.c的i2cdev_attach_adapter。
从上分析:I2C驱动时很复杂的,那么我们如果要重新写一个I2C驱动,具体要怎么做呢?
(1)I2c-dev.c是应用程序系统调用的接口,例如open对于的就是i2cdev_open,read对应的就是i2cdev_read,(2)那么通过i2cdev_xxx在调用i2c-core.c的rtc核心代码,比如i2c_master_recv,i2c_master_recv再调用i2c_transfer,(3)i2c_transfer再通过i2c_adapter结构调用具体平台的函数。
(1)(2)内核已经写好了,我门要做的就是(3),要定义一个i2c_algorithm通信方法,并将通信函数写好。我们可以参照i2c-s3c2410,定义一个s3c24xx_i2c结构。
其中结构中包括传递信息的msg,适配器结构 i2c_adapter, i2c_algorithm就放在i2c_adapter中
其中结构中包括传递信息的msg,适配器结构 i2c_adapter, i2c_algorithm就放在i2c_adapter中。