linux I2C设备驱动主要包括两部分:设备注册、驱动程序
1、设备注册主要涉及到一个结构体和一个注册函数:
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned shortflags;
unsigned shortaddr;
void *platform_data;
struct dev_archdata*archdata;
int irq;
};
使用例子:
static struct i2c_board_info i2c_info[] = {
{
I2C_BOARD_INFO("tlv320aic3x", 0x18),//I2C地址值貌似不是很重要,及时错误,也能成功注册设备,和找到相应的驱动程序。但我估计后面的I2C的读写操作收到影响
},
{
I2C_BOARD_INFO("s35390a", 0x30),
},
}
注册函数:
i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info));//表示将上面tlv320aic3x、s35390a挂载到适配器编号为‘1’的I2C总线上,一般一个设备上只有一个I2C适配器,不过还有些设备是提供了两个I2C总线接口,所以适配器应该要有两个才对,具体适配器编号,可以通过如下命令查看红色部分:
ls -l /dev
.................
drwxr-xr-x 2 root root 60 Aug 8 00:12 i2c
lrwxrwxrwx 1 root root 5 Aug 8 00:12 i2c-1 -> i2c/1
...............
通过上面方法,就可以完成将I2C设备挂载在I2C总线上了。
2、下面我我们讲解I2C设备对应的驱动程序:
主要结构体:
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *);
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 *);
.........................
}
这个结构体中,基本可以分为两部分,这两部分可以在不同的情况下独立使用于I2C设备驱动程序中:
第一种:
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
使用例子:
static const struct i2c_device_id s35390a_id[] = {
{ "s35390a", 0 },
{ }
};
static struct i2c_driver s35390a_driver = {
.driver = {
.name = "rtc-s35390a",
},
.probe = s35390a_probe,
.remove = s35390a_remove,
.id_table = s35390a_id,
};
static int __init s35390a_rtc_init(void)
{
return i2c_add_driver(&s35390a_driver);
}
在这种情况下,如果想要s35390a_probe函数被调用,则需要向系统注册I2C设备到I2C总线上,方法就是上面讲到的第一点内容。
第二种:
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct i2c_adapter *);
使用实例:
static struct i2c_driver thermostat_driver = {
.driver = {
.name = "therm_adt746x",
},
.attach_adapter= attach_thermostat,
.detrach_client = client_thermostat,
};
i2c_add_driver(&thermostat_driver);
在这种模式中,系统加载i2c_add_driver函数后,attach_adapter指向的函数会得到立即调用,完成相关I2C设备的初始化等工作。这个相比第一种方式,就可以省去了I2C设备注册的步骤了。
其实这些东西使用很灵活,这里我在介绍另外一种使用情况:
static const struct i2c_device_id therm_adt746x_id[] = {
{ "therm_adt746x", 0 },
{ }
};
static struct i2c_driver thermostat_driver = {
.driver = {
.name = "therm_adt746x",
},
.attach_adapter= attach_thermostat,
.probe = probe_thermostat,
.remove = remove_thermostat,
.id_table = therm_adt746x_id,
};
i2c_add_driver(&thermostat_driver);
这种情况下,一般是在attach_adapter指向的函数被调用中,完成设备的注册工作,设备被注册到I2C总线上后,probe 指向的函数被立即调用,完成后续初始化工作。
好吧,I2C驱动先暂时介绍到这里。后续有补充再添加。