参考:正点原子。
一. Linux驱动分离:
设备驱动模型可以让设备驱动同时支持多个主机,并且只用一份驱动,有点用数组管理每个设备的感觉。。
但是实际当中设备驱动可能不止一个,于是产生了下面这种模型,总线充当中间人,给两边配对。
但是在 SOC 中有些外设是没有总线这个概念的,但是又要使用总线、驱动和设备模型该怎么办呢?为了解决此问题,Linux 提出了 platform 这个虚拟总线,相应的就有 platform_driver 和 platform_device。
总线代码我们不需要编写,linux内核提供给我们使用的。我们需要编写驱动和设备,当向总线注册驱动的时候,总线会从现有的所有设备中查找,看看哪个设备和此驱动匹配。同理,当向总线注册设备的时候总线也会在现有的驱动中查看与之匹配的驱动。
一. 驱动(driver),总线(bus),设备(device)
1.总线注册:
注册总线:使用bus_register()函数
卸载总线:bus_unregister()函数
总线数据类型为:bus_type。向内核注册总线使用bus_register。
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
在device.h下有结构体:bus_type {}
cd /bus/i2c 文件下有devices drivers,,通过match()函数来匹配。bus就像中间人,把设备和驱动匹配,联系起来。
-
2.总线设备注册:
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
……
};
- 向总线注册设备的时候,使用device_register。
- 调用函数顺序如下:
device_register()
-> device_add
-> bus_add_device
-> bus_probe_device
->device_attach
->bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
-> __device_attach
-> driver_match_device //匹配驱动
->bus->match
->driver_probe_device //执行此函数
-> really_probe
-> drv->probe(dev);
驱动与设备匹配以后驱动的probe函数就会执行,probe函数就是驱动编写人员去编写的!!!!
-
3.总线驱动注册:
驱动数据类型为device_driver,驱动程序向内核注册驱动采用driver_register。
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
驱动和设备匹配以后驱动里面的probe函数就会执行。
使用driver_register注册驱动。
driver_register
-> bus_add_driver
-> driver_attach //查找bus下所有设备,找预期匹配的。
->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
->__driver_attach //每个设备都调用此函数,
//查看每个设备是否与驱动匹配
-> driver_match_device //检查是否匹配。
-> driver_probe_device
->really_probe
-> drv->probe(dev); //执行driver的probe函数。
总结:向总线注册驱动的时候,会检查当前总线下的所有设备,有没有与此驱动匹配的设备,如果有的话就执行驱动里面的probe函数。
二. platformat模型驱动
然后不好用总线设备驱动模型的用的是platformat模型驱动:
在drivers/base/platform.c
-
1.注册平台总线:
platform_bus_init
-> bus_register
注册的内容就是:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
}
- platform_match负责设备和驱动匹配。
- 卸载总线:bus_unregister()函数
2. platform驱动:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
-> const struct of_device_id *of_match_table;
-> const char *name;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
3. platform设备:
- 无设备树的时候,使用platform_device_register函数注册设备。
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
- platform匹配过程:(无设备树)
通过bus->match()函数匹配。也就是platform_bus_type的platform_match();
--->platform_match_id () 根据platform_driver-> id_table
--->strcmp(pdev->name, drv->name) //最终的就是比较字符串,就是platform_device->name,和platform_driver->driver->name。无设备树情况下使用。