2 I2C子系统
2.1 LinuxI2C子系统架构
在内核中已经提供I2C子系统,所以在做I2C驱动之前,就必须要熟悉该子系统。
2.2 三大组成部分
1、I2C核心(i2c-core)
I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(algorithm)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
2、I2C总线驱动(I2Cadapter/Algo driver)
I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力。
I2C总线驱动由i2c_adapter和i2c_algorithm来描述
3、I2C客户驱动程序(I2Cclient driver)
I2C客户驱动是对I2C从设备的软件实现,一个具体的I2C客户驱动包括两个部分:一部分是i2c_driver,用于将设备挂接于i2c总线;另一部分是设备本身的驱动。
I2C客户驱动程序由i2c_driver和i2c_client来描述
2.3 所有的I2C驱动代码位于drivers/i2c目录下
I2c-core.c 实现I2C核心的功能
I2c-dev.c 通用的从设备驱动
Chips 特定的I2C设备驱动
Busses I2C适配器的驱动
Algos 实现了一些I2C总线适配器的algorithm
2.4 I2C驱动编写的两种方法
从上面的图我们可以看到两种编写驱动方法,一种是利用系统提供的i2c-dev.c来实现一个i2c适配器的设备文件,然后通过在应用层操作I2C适配器来控制I2C设备;另一种是为I2C从设备独立编写一个设备驱动,不需要i2c-dev.c文件。
2.5 重要的数据结构
每次分析子系统免不了分析它的数据结构,OK我们先来分析一下。
I2c_adapter结构体代表I2C总线控制器
-
struct i2c_adapter {
-
struct module *owner;
-
unsigned int class;
-
const struct i2c_algorithm*algo;
-
void *algo_data;
-
-
int timeout;
-
int retries;
-
struct device dev;
-
-
int nr;
-
char name[48];
-
struct completion dev_released;
-
};
struct i2c_adapter {
struct module *owner;
unsigned int class; /*classes to allow probing for */
const struct i2c_algorithm*algo; /* 总线上数据传输的算法*/
void *algo_data; /* algorithm 数据 */
int timeout; /* injiffies */
int retries; /* 重试次数 */
struct device dev; /* the adapter device */
int nr;
char name[48]; /* 适配器名字 */
struct completion dev_released; /* 用于同步 */
};
I2c_algorithm对应一套通信方法
-
struct i2c_algorithm {
-
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum);
-
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
-
unsigned short flags, charread_write,
-
u8 command, int size, unioni2c_smbus_data *data);
-
u32 (*functionality) (structi2c_adapter *);
-
};
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, charread_write,
u8 command, int size, unioni2c_smbus_data *data);
u32 (*functionality) (structi2c_adapter *);
};
Functionality 函数用于返回algorithm所支持的通信协议,比如I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR等。
Master_xfer 函数实现总线上数据传输,与具体的适配器有关
Master_xfer函数实现模板
-
static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
-
{
-
......
-
for (i = 0; i < num; i++) {
-
i2c_adapter_xxx_start();
-
if (msgs[i]->flags & I2C_M_RD) {
-
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);
-
i2c_adapter_xxx_wait_ack();
-
i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);
-
} else {
-
i2c_adapter_xxx_setaddr(msg->addr << 1);
-
i2c_adapter_xxx_wait_ack();
-
i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);
-
}
-
}
-
i2c_adapter_xxx_stop();
-
}
static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
{
......
for (i = 0; i < num; i++) {
i2c_adapter_xxx_start(); /*产生起始位*/
if (msgs[i]->flags & I2C_M_RD) { /*读取*/
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); /*发送从设备地址*/
i2c_adapter_xxx_wait_ack(); /*获得从设备的ACK*/
i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len); /*读取len长度的数据到buf中*/
} else {
i2c_adapter_xxx_setaddr(msg->addr << 1);
i2c_adapter_xxx_wait_ack();
i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);
}
}
i2c_adapter_xxx_stop(); /*产生停止位*/
}
上面调用的函数用于完成适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的数据手册来实现。在内核源码中,针对不同的I2C适配器都有master_xfer的实现,风格与模板不尽相同,但是可以用该模板作为参考来看源代码,受益匪浅。
I2c_driver代表I2C从设备驱动
-
struct i2c_driver {
-
unsignedint class;
-
-
int(*attach_adapter)(struct i2c_adapter *) __deprecated;
-
int(*detach_adapter)(struct i2c_adapter *) __deprecated;
-
-
int (*probe)(struct i2c_client*, const struct i2c_device_id *);
-
int (*remove)(struct i2c_client*);
-
-
int(*suspend)(struct i2c_client *, pm_message_t mesg);
-
int(*resume)(struct i2c_client *);
-
void(*alert)(struct i2c_client *, unsigned int data);
-
int(*command)(struct i2c_client *client, unsigned int cmd, void *arg);
-
-
struct device_driver driver;
-
const struct i2c_device_id*id_table;
-
-
-
int(*detect)(struct i2c_client *, struct i2c_board_info *);
-
constunsigned short *address_list;
-
structlist_head clients;
-
};
struct i2c_driver {
unsignedint class;
int(*attach_adapter)(struct i2c_adapter *) __deprecated; /*依附i2c适配器函数指针*/
int(*detach_adapter)(struct i2c_adapter *) __deprecated;/*脱离i2c适配器函数指针*/
int (*probe)(struct i2c_client*, const struct i2c_device_id *);
int (*remove)(struct i2c_client*);
int(*suspend)(struct i2c_client *, pm_message_t mesg);
int(*resume)(struct i2c_client *);
void(*alert)(struct i2c_client *, unsigned int data);
int(*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id*id_table; /* 该驱动所支持的设备ID表 */
/*Device detection callback for automatic device creation */
int(*detect)(struct i2c_client *, struct i2c_board_info *);
constunsigned short *address_list;
structlist_head clients;
};
在新内核中,attach_adapter和detach_adapter已经被probe和remove取代
Id_table用于i2c_driver和i2c_client的匹配
I2c_client代表I2C从设备
-
struct i2c_client {
-
unsigned short flags;
-
unsignedshort addr;
-
charname[I2C_NAME_SIZE];
-
struct i2c_adapter *adapter;
-
struct i2c_driver *driver;
-
structdevice dev;
-
intirq;
-
structlist_head detected;
-
};
struct i2c_client {
unsigned short flags; /*I2C_CLIENT_TEN:使用10位从地址,I2C_CLIENT_PEC:使用SMBus包错误检测*/
unsignedshort addr; /* chipaddress - NOTE: 7bit */
charname[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* 依附的i2c_adapter */
struct i2c_driver *driver; /* 依附的i2c_driver*/
structdevice dev; /* the devicestructure */
intirq; /* irq issuedby device */
structlist_head detected;
};
2.6 核心层提供的接口函数
1、 增加/删除I2C适配器
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_del_adapter(struct i2c_adapter *adap)
-
-
static int i2c_register_adapter(struct i2c_adapter *adap)
-
{
-
res = device_register(&adap->dev);
-
-
if (adap->nr <__i2c_first_dynamic_bus_num)
-
i2c_scan_static_board_info(adap);
-
-
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
-
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
res = device_register(&adap->dev);
if (adap->nr <__i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
}
Device_register(&adap->dev) 向I2C总线注册一个adapter设备
i2c_scan_static_board_info(adap) 注册所有已知的i2c_client
2、 增加/删除I2C从设备驱动
-
int i2c_add_driver(struct i2c_driver *driver)
-
void i2c_del_driver(struct i2c_driver *driver)
-
-
inti2c_register_driver(struct module *owner, struct i2c_driver *driver)
-
{
-
-
-
driver->driver.owner = owner;
-
driver->driver.bus = &i2c_bus_type;
-
-
res = driver_register(&driver->driver);
-
-
-
i2c_for_each_dev(driver, __process_new_driver);
-
}
int i2c_add_driver(struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
inti2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
/* add the driver to the list of i2c drivers inthe driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
res = driver_register(&driver->driver);
/* Walk the adapters that are already present*/
i2c_for_each_dev(driver, __process_new_driver);
}
driver_register(&driver->driver) 向I2C总线注册一个i2c_driver
3、 i2c传输,发送和接收
-
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num)
-
int i2c_master_send(const struct i2c_client *client, constchar *buf, int count)
-
int i2c_master_recv(const struct i2c_client *client, char*buf, int count)
-
-
int i2c_transfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
-
{
-
if (adap->algo->master_xfer) {
-
for (ret = 0, try = 0; try <=adap->retries; try++) {
-
ret = adap->algo->master_xfer(adap, msgs,num);
-
}
-
}
-
}
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num)
int i2c_master_send(const struct i2c_client *client, constchar *buf, int count)
int i2c_master_recv(const struct i2c_client *client, char*buf, int count)
int i2c_transfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
{
if (adap->algo->master_xfer) {
for (ret = 0, try = 0; try <=adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs,num);
}
}
}
最终会调用到适配器实现的master_xfer函数来完成数据传输工作
参考资料:《essential Linux device drivers》
《linux设备驱动开发详解》