理解I2C设备驱动框架,主要围绕四个结构体去分析就容易了。
struct i2c_algorithm:提供I2C协议的实现的操作,如:master_xfer实现数据收发的最基本方法。
struct i2c_adapter:每一个i2c_adapter都代表一个I2C物理接口,一个cpu可以有多个I2C接口(i2c_adapter),i2c_algorithm就是为i2c_adapter提供I2C协议的实现。每增加一个i2c接口,即是向i2c_core.c注册一个i2c_adapter
struct i2c_driver:代表着一类I2C从机设备的驱动,比如:at24cxx的驱动,不同类型的I2C从机需要注册不同的i2c_driver,如:ssd1306的驱动不同于at24cxx的驱动。每增加一个类型的I2C从机设备,都要向i2c_core.c注册一个i2c_driver
struct i2c_client:代表具体的某一个I2C从机设备,如:at24cxx系列的设备,有at24c01,at24c02等,每增加一个at24cxx设备,都要注册一个i2c_client。只有I2C从机设备被探测到,i2c_client才会被注册。
这四者的关系可以分为:i2c_algorithm和i2c_adapter一起驱动I2C总线,i2c_driver和i2c_client一起实现设备驱动。
注:linux目前只支持I2C主机模式。本文引用内核源码中i2c-algo-bit.c和i2c-gpio.c文件来讲解, i2c_driver由驱动开发者根据特定的设备提供,这里引用作者提供的ssd1306.c。i2c-algo-bit.c和i2c-gpio.c共同实现IO模拟I2C。
i2c-algo-bit.c提供了一个i2c_algorithm,i2c-gpio.c提供了一个i2c_adapter。
i2c-algo-bit.c通过以下代码绑定到i2c-gpio.c
i2c-algo-bit.c
static const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};
static int i2c_bit_prepare_bus(struct i2c_adapter *adap)
{
... ...
adap->algo = &i2c_bit_algo;
... ...
return 0;
}
int i2c_bit_add_bus(struct i2c_adapter *adap)
{
... ...
err = i2c_bit_prepare_bus(adap);
... ...
return i2c_add_adapter(adap);
}
i2c-gpio.c
static int __init i2c_gpio_probe(struct platform_device *pdev)
{
struct i2c_gpio_platform_data *pdata;
struct i2c_algo_bit_data *bit_data;
struct i2c_adapter *adap;
... ...
pdata = pdev->dev.platform_data;
... ...
i2c_bit_add_bus(adap);
... ...
}
这里就注册了一个i2c_adapter。
要驱动ssd1306,因此对应地要提供一个i2c_driver,与i2c_adapter建立关系。
ssd1306.c
static struct i2c_driver ssd1306_driver = {
.driver = {
.name = "ssd1306",
},
.id = I2C_DRIVERID_I2CDEV,
.attach_adapter = ssd1306_attach_adapter,
.detach_client = ssd1306_detach_client,
};
static int ssd1306_module_init(void)
{
i2c_add_driver(&ssd1306_driver);
return 0;
}
i2c_driver和i2c_adapter是怎样建立关系的呢?
I2c_core.c负责桥接i2c_driver和i2c_adapter建立关系,在i2c_driver和i2c_adapter注册的时候,两者都会调用driver->attach_adapter(adapter)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
... ...
driver->attach_adapter(adapter);
... ...
return 0;
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
... ...
driver->attach_adapter(adap);
... ...
}
driver->attach_adapter(adapter)实际上调用
1 static int ssd1306_attach_adapter(struct i2c_adapter *adapter) 2 { 3 return i2c_probe(adapter, &addr_data, ssd1306_detect); 4 }
I2c_probe()函数的作用就是,探测是否存在ssd1306这个设备,是怎样探测的呢?就是通过发送从机地址到ssd1306,如果ssd1306返回应答信号,就认为探测到了。
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
... ...
i2c_probe_address(adapter,
address_data->probe[i + 1],
-1, found_proc);
... ...
}
代码太多,简化函数调用关系如下: