文章目录
上一节分析了 平台设备和驱动的匹配过程,即 probe 函数的自动调用过程,本节来分析 IIC 总线上设备和驱动的匹配过程。
1. 驱动端probe调用过程
1.1 i2c_driver 结构体
struct i2c_driver {
struct device_driver driver;
const struct i2c_device_id *id_table;
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
void (*shutdown)(struct i2c_client *);
void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
1.2 i2c_driver 的实现样例
struct i2c_device_id mpu6050_id[] = {
{.name = "mpu6050"},
{}
};
struct i2c_driver mpu6050_driver = {
.probe = mpu6050_probe, // probe 函数
.remove = mpu6050_remove, // remove 函数
.driver = {
.name = "my_i2cdrv", // 驱动的名字
.owner = THIS_MODULE,
},
.id_table = mpu6050_id, // 用于和设备匹配的 id_table
};
1.3 probe的调用过程
IIC驱动probe的调用过程和平台驱动类似,只是i2c_bus_type结构体中的变量不太一致,整体调用过程类似,详细的过程分析如下:
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver) (include\linux\i2c.h)
|-> driver->driver.bus = &i2c_bus_type; (drivers\i2c\i2c-core.c) // i2c总线类型
|-> i2c_bus_type.name = "i2c", // 名字是i2c
i2c_bus_type.match = i2c_device_match, // match 函数
i2c_bus_type.probe = i2c_device_probe, // probe 函数
i2c_bus_type.remove = i2c_device_remove, // remove函数
|-> driver_register(&driver->driver)
|-> bus_add_driver(struct device_driver *drv) (drivers\base\bus.c) // 添加驱动到总线上
|-> driver_attach(struct device_driver *drv) (drivers\base\dd.c)
|-> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) (drivers\base\bus.c)
|-> __driver_attach(struct device *dev, void *data) (drivers\base\dd.c)
|-> driver_match_device(struct device_driver *drv, struct device *dev) (drivers\base\base.h) // 驱动和设备匹配
|-> return drv->bus->match ? drv->bus->match(dev, drv) : 1
|-> if (of_driver_match_device(dev, drv)) // 设备树风格
return 1;
if (acpi_driver_match_device(dev, drv)) // ACPI风格
return 1;
driver = to_i2c_driver(drv);
if (driver->id_table) // 匹配ID表
return i2c_match_id(driver->id_table, client) != NULL;
|-> driver_probe_device(drv, dev) (drivers\base\dd.c) // 驱动和设备匹配成功后,执行probe函数
|-> really_probe(drv, dev) (drivers\base\dd.c) // 执行真正的 probe 函数
|-> if (dev->bus->probe) { // 没有定义
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) { // 执行 device_driver 里面的 probe 函数
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
|-> if (dev->bus->remove) // remove 函数
dev->bus->remove(dev);
else if (drv->remove) // 在这里调用真正的自己定义的remove函数
drv->remove(dev);
|-> platform_drv_probe(struct device *_dev) (drivers\base\platform.c)
|-> struct platform_driver *drv = to_platform_driver(_dev->driver) // 找到 platform_driver 结构体
|-> #define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver)) (include\linux\platform_device.h)
|-> struct platform_device *dev = to_platform_device(_dev)
|-> #define to_platform_device(x) container_of((x), struct platform_device, dev)
|-> if (drv->probe) {
ret = drv->probe(dev); // 执行真正的probe函数,也就是在 platform_driver 中自己定义的,并将 pdev 传入进去
if (ret)
dev_pm_domain_detach(_dev, true);
} else {
/* don't fail if just dev_pm_domain_attach failed */
ret = 0;
}
2. 设备端
2.1 i2c_client 结构体
设备端用 struct i2c_client 结构体描述,结构体定义如下:
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
};
2.2 IIC设备向内核的注册
IIC设备向内核的注册使用 i2c_register_board_info 函数,随着设备树的出现这种方法基本被淘汰,现在大多采用设备树的方法来对IIC设备进行描述。
关于设备树的解析将在下一节进行分析。
将i2c设备添加到 busnum 号i2c总线上
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
struct fwnode_handle *fwnode;
int irq;
};
int i2c_register_board_info(int busnum, struct i2c_board_info const * info, unsigned len) (drivers\i2c\i2c-boardinfo.c)