前面我们说了如何I2C用户模式驱动,这种驱动基于I2C子系统,但是他对于应用程序开发人员的要求较高,需要应用程序开发人员了解硬件的一些东西,比如时序,地址等等,而多数时候应用程序开发人员是按照操作文件的方法操作设备,所以我们更希望用一些更简单的接口去访问。也就是我们今天的内容——基于I2C子系统的字符驱动。
I2C子系统的代码分为三部分如图:
Host:主机控制器驱动
Device:设备驱动代码
Core: 核心代码,提供设备与控制器的接口
一、主机控制器驱动
Linux下主机控制器驱动,大多数是BSP提供的,这里不多说,简单说下它主要干的活。I2C主机控制器在内核里又叫适配器,用结构i2c_adapter描述。
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 */
……
};
struct i2c_algorithm 提供设备访问控制器的接口,定义如下:
struct i2c_algorithm {
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);
u32 (*functionality) (struct i2c_adapter *);
};
其中master_xfer就是我们给设备端提供的接口,这部分内容按照芯片手册中寄存器的操作实现数据的收发。
最终我们将i2c_adapter注册到系统中,使用如下函数:
int i2c_add_numbered_adapter(struct i2c_adapter *);
二、核心代码
这部分就不说了,刚才我们介绍的函数全部是核心代码提供,它主要是提供标准的统一的接口。
三、设备代码
基于I2C的字符驱动的编写首先我们需要了解几个特定的结构。
1、i2c_bus_type
i2c总线结构定义了一些总线相关的方法,这里我们关系的是i2c_driver和i2c_client的配备规则,为什么匹配呢,i2c_client携带硬件信息,而i2c_driver只负责操作设备而不管操作的是那个设备它需要的硬件信息有i2c_client提供,所以需要i2c_client和i2c_driver协同操作,而一个系统中i2c_driver和i2c_client都可能有多个,如何得到自己的另一半就是我所说的匹配规则,i2c_bus_type的匹配规则定义如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
我们发现i2c总线的匹配规则是id或name两种,id优先级高。
2、板级结构:
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //芯片类型,其实也就是名字,用来匹配
unsigned short flags; //标志位,一些特定的标志
I2C子系统的代码分为三部分如图:
Host:主机控制器驱动
Device:设备驱动代码
Core: 核心代码,提供设备与控制器的接口
一、主机控制器驱动
Linux下主机控制器驱动,大多数是BSP提供的,这里不多说,简单说下它主要干的活。I2C主机控制器在内核里又叫适配器,用结构i2c_adapter描述。
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 */
……
};
struct i2c_algorithm 提供设备访问控制器的接口,定义如下:
struct i2c_algorithm {
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);
u32 (*functionality) (struct i2c_adapter *);
};
其中master_xfer就是我们给设备端提供的接口,这部分内容按照芯片手册中寄存器的操作实现数据的收发。
最终我们将i2c_adapter注册到系统中,使用如下函数:
int i2c_add_numbered_adapter(struct i2c_adapter *);
二、核心代码
这部分就不说了,刚才我们介绍的函数全部是核心代码提供,它主要是提供标准的统一的接口。
三、设备代码
基于I2C的字符驱动的编写首先我们需要了解几个特定的结构。
1、i2c_bus_type
i2c总线结构定义了一些总线相关的方法,这里我们关系的是i2c_driver和i2c_client的配备规则,为什么匹配呢,i2c_client携带硬件信息,而i2c_driver只负责操作设备而不管操作的是那个设备它需要的硬件信息有i2c_client提供,所以需要i2c_client和i2c_driver协同操作,而一个系统中i2c_driver和i2c_client都可能有多个,如何得到自己的另一半就是我所说的匹配规则,i2c_bus_type的匹配规则定义如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
我们发现i2c总线的匹配规则是id或name两种,id优先级高。
2、板级结构:
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //芯片类型,其实也就是名字,用来匹配
unsigned short flags; //标志位,一些特定的标志