文章目錄
上一節分析了 平臺設備和驅動的匹配過程,即 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)