1、以前的驱动简单,IO,阻塞非阻塞,为提高代码重用以及跨平台性能。
目的:方便驱动的编写,提高软件的重用以及跨平台性能。
***控制器 <->设备***,这就是分离与分隔,控制器就是控制设备的程序,设备就是设备程序
以I2C为例,每个平台的I2C控制器都提供一个统一的接口(也叫主机驱动)需要按照Linux主机驱动框架编写,每个设备的话也只提供一个驱动程序(设备驱动),每个设备通过统一的I2C接口驱动来访问主机,
1、主机控制器驱动与设备驱动分离,主机控制器驱动一般是半导体厂商写的,在linux驱动框架下编写具体的设备驱动。
2、中间的联系就是核心层,主机控制器向外提供统一的API(由核心层规定的)
3、驱动的分层
目的是不同的层编写不同的内容,这样编写起来就方便了
*学Linux驱动就是学linux框架,按照框架去编写驱动,框架由核心层去定*
不同驱动的分离与分层框架不同,
驱动--总线--设备。
根据驱动的分离与分层衍生出了总线(bus)-驱动(driver) - 设备(device)驱动框架
总线代码由linux内核提供给我们,
我们需要编写驱动和设备,
当向总线注册驱动的时候,总线会从现有的所有设备中查找,看看哪个设备和此驱动匹配。
同理,当向总线注册设备的时候总线也会在现有的驱动中查找与之匹配的驱动。
驱动:是具体的设备驱动
设备:设备属性,包括地址范围、如果是I2C的话还有IIC器件地址、速度
中间的联系人是总线
2.总线
总线的数据类型为:bus_type。向内核注册总线使用bus_register。
卸载用bus_unregister函数;
总线的工作就是完成总线下的设备和驱动之间的匹配。
添加自定义的总线:向linux内核注册总线:bus_register函数; 注册核心就是bus_type类型结构体
例如在i2c-core.c有i2c_bus_type 里面有.name = i2c,核心层注册的,用户很少去注册总线
重点是驱动和设备,驱动数据类型为device-driver,驱动程序向内核注册驱动采用driver_register.
在device.h下有个结构体
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(drv);附加这个
->int driver_attach(struct device_driver *drv) //查找bus下所有设备,找预期匹配的
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);查找总线下
__driver_attach 每个设备都调用此函数,查看每个设备是否与驱动匹配,
->上面会调用driver_match_device函数//检查是否匹配
}
在设备驱动模型中,当有新的设备加入系统或新的驱动注册时,内核会遍历相关总线上的驱动列表,并调用这些驱动所属总线的match
函数来尝试匹配设备和驱动。这意味着match
函数的执行是由内核在运行时按需触发的
用这条宏来检测是否匹配;
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1; 这个回调函数
}
如果match存在,就执行match实现dev和drv的匹配,否则返回1,具体过程在内核层实现
匹配成功执行
->driver_probe_device(drv, dev)
->really_probe(dev, drv);
-》drv->probe(dev);//执行driver的probe函数,重点,当驱动和设备匹配成功后,驱动下的probe函数就会执行
向总线注册驱动的时候,会检查当前总线下的所有设备,有没有与此驱动匹配的设备,如果有的话就执行驱动里面的probe函数。
3.设备
向总线注册设备,使用device_register();
-》device_add(struct device *dev)
-》bus_add_device(dev)
-》bus_probe_device(struct device *dev)
-》device_attach(dev)
-》bus_for_each_drv(dev->bus, NULL, dev, __device_attach);查找bus下的每个驱动
-》__driver_attach
-》driver_match_device //匹配驱动
->bus->match
->driver_probe_device(drv,dev) //执行此函数
-》really_probe(dev, drv)
->drv->probe(dev); 执行这个,我们需要去实现
驱动与设备匹配后最终要执行probe函数,probe函数就是驱动人员要去编写的!!!非常重要
先安装设备就设备找驱动,先安装驱动就驱动找设备