Linux中IIC驱动简介及其简单编写流程
Linux中IIC驱动简介及其简单编写流程1、Linux I2C 驱动框架简介1.1、I2C动框架下的几个重要成员1.1.1、I2C总线1.1.2、I2C驱动1.1.3、I2C设备1.1.4、I2C适配器1.1.5、小结2、IIC驱动简单编写流程2.1、例程框架1、Linux I2C 驱动框架简介Linux内核将 I2C 驱动分为两部分:①、 I2C 总线驱动, I2C 总线驱动就是 SOC 的
Linux中IIC驱动简介及其简单编写流程
1、Linux I2C 驱动框架简介
Linux内核将 I2C 驱动分为两部分:
①、 I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
②、 I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。
I2C总线协议详解请参考:I2C总线协议
1.1、I2C动框架下的几个重要成员
1.1.1、I2C总线
I2C总线结构体在drivers\i2c\i2c-core.c中定义如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备与I2C驱动的匹配,删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动的probe函数。
形如:
i2c_device_match会管理I2C设备和I2C总线匹配规则,这将和如何编写I2C驱动程序息息相关
1.1.2、I2C驱动
i2c_driver 类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容, i2c_driver 结构体定义在 include/linux/i2c.h 文件中,内容如下:
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared. You should avoid
* using this, it will be removed in a near future.
*/
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* 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 *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* 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;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
重点成员如下:
成员 | 描述 |
---|---|
int (*probe)(struct i2c_client *, const struct i2c_device_id *) | 当 I2C 设备和驱动匹配成功以后 probe 函数就会执行 |
struct device_driver driver | device_driver 驱动结构体,如果使用设备树的话,需要设置 device_driver 的of_match_table 成员变量,也就是驱动的兼容(compatible)属性。 |
const struct i2c_device_id *id_table | id_table 是传统的、未使用设备树的设备匹配 ID表 |
1.1.3、I2C设备
I2C设备结构体i2c_client 结构体定义在 include/linux/i2c.h 文件中,内容如下:
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 device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
重点成员如下:
成员 | 描述 |
---|---|
flags | 标志 |
addr | 芯片地址, 7 位,存在低 7 位 |
flagsname[I2C_NAME_SIZE] | 名字 |
adapter | 对应的 I2C 适配器 |
dev | 设备结构体 |
irq | 中断 |
一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
1.1.4、I2C适配器
经过上面的介绍,知道有I2C驱动和I2C设备,我们需要通过I2C驱动去和I2C设备通讯,这其中就需要一个I2C设配器,I2C设配器对应的就是SOC上的I2C控制器。
Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter, i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,结构体内容如下:
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus *//* 总线访问算法 */
void *algo_data;
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
重点成员如下:
成员 | 描述 |
---|---|
const struct i2c_algorithm *algo | I2C 适配器与 IIC 设备进行通信的方法。 |
i2c_algorithm 结构体定义在 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);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
重点成员如下:
成员 | 描述 |
---|---|
master_xfer | I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。 |
smbus_xfer | SMBUS 总线的传输函数 |
I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter。
这两个函数的区别在于 i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号
1.1.5、小结
I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器
- I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
- I2C驱动:对应的就是I2C设备的驱动程序
- I2C设备:是具体硬件设备的一个抽象
- I2C适配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象
Linux I2C总线的运行机制
- 1、注册I2C驱动
- 2、将I2C驱动添加到I2C总线的驱动链表中
- 3、遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配调用i2c_device_probe函数
- 4、i2c_device_probe函数会调用I2C驱动的probe函数
2、IIC驱动简单编写流程
一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,这个不需要用户去编写。因此 I2C 总线驱动对我们这些 SOC 使用者来说是被屏蔽掉的,我们只要专注于 I2C 设备驱动即可。除非你是在半导体公司上班,工作内容就是写 I2C 适配器驱动。
i2c_driver 类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容,i2c_driver 在上面已经介绍了其结构体的具体内容。
对于我们 I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver,构建完成以后需要向Linux 内核注册这个 i2c_driver。
那么如何注册呢?
使用下面的这个函数:
int i2c_register_driver(struct module *owner,struct i2c_driver *driver)
函数参数和返回值含义如下:
owner: 一般为 THIS_MODULE。
driver:要注册的 i2c_driver。
返回值: 0,成功;负值,失败。
另外 i2c_add_driver 也常常用于注册 i2c_driver, i2c_add_driver 是一个宏,定义如下:
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。
注销 I2C 设备驱动的时候需要将前面注册的 i2c_driver 从 Linux 内核中注销掉,需要用到i2c_del_driver 函数,此函数原型如下:
void i2c_del_driver(struct i2c_driver *driver)
函数参数和返回值含义如下:
driver:要注销的 i2c_driver。
返回值: 无。
2.1、例程框架
/* i2c 驱动的 probe 函数 */
static int xxx_probe(struct i2c_client *client,
{
/* 函数具体程序 */
return 0;
}
/* i2c 驱动的 remove 函数 */
static int xxx_remove(struct i2c_client *client)
{
/* 函数具体程序 */
return 0;
}
/* 传统匹配方式 ID 列表 */
static const struct i2c_device_id xxx_id[] = {
{"xxx", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx" },
{ /* Sentinel */ }
};
/* i2c 驱动结构体 */
static struct i2c_driver xxx_driver = {
.probe = xxx_probe,
.remove = xxx_remove,
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = xxx_of_match,
},
.id_table = xxx_id,
};
/* 驱动入口函数 */
static int __init xxx_init(void)
{
int ret = 0;
ret = i2c_add_driver(&xxx_driver);
return ret;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
i2c_del_driver(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样, probe 函数里面基本就是标准的字符设备驱动那一套了。
更多推荐
所有评论(0)