当前所在位置: 首页 > 数码科技 > 正文

linux驱动编程--设备模型

2023-10-15 本站作者 【 字体:

在前面学习了 kobject和 kset之后,就迫不及待的想开始“研究”设备模型了。经过这几天的学习,感觉受益匪浅。所以就将自己的理解整理了下来

想要完成一个设备的驱动,就要涉及三部分: Bus, device, driver。当然这些“新”节点都是最终继承于kobject。

一.Bus

这里先整理一下BUS,总线负责在设备与驱动间建立连接,包括 I2C, PCI, 串口,platform等。其中platform是虚拟总线。

1.1 结构体

信息结构体是 bus_type.

struct bus_type {

const char *name; //the name of bus

struct bus_attribute *bus_attrs;

//attribute for bus, contain attribute file and some operate function.

// this is a interface between kernel space and user space.

struct device_attribute *dev_attrs; //attribute for device,

struct driver_attribute *drv_attrs; //attribute for deriver

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 (*suspend)(struct device *dev, pm_message_t state);

int (*suspend_late)(struct device *dev, pm_message_t state);

int (*resume_early)(struct device *dev);

int (*resume)(struct device *dev);

struct dev_pm_ops *pm; //power manage

struct bus_type_private *p;

//private data for bus. In fact, it is core of this structure

};

在其中 bus_attrs, dev_attrs 和 drv_attrs记录了该总线的一些属性信息,而最重要的被用来构建该总线的逻辑结构的信息都记录在了bus_type_private中。对应这个总线私有数据结构体的解析如下。

struct bus_type_private {

struct kset subsys;

//there are two points:

//1).this is a set. It is contain some devices and derivers about this bus.

//2). it's parent is @bus_kset, which is the root of all other bus.@bus_kset have many subset, this is just one of them.

//

struct kset *drivers_kset;

//all drivers about this bus will belong to this set.

struct kset *devices_kset;

//all devices of this bus will belong to this set.

struct klist klist_devices;

struct klist klist_drivers;

//they are two lists , for mount all corresponding nodes.

struct blocking_notifier_head bus_notifier;

unsigned int drivers_autoprobe:1;

//is this bus automaticly run when a new devices arrvied.

//sometime, we can see some attribute files in user space.(for example:@drivers_autoprobe).

//it is interface that kernel leave user to modify this argument.

struct bus_type *bus;

//just a port for return this bus.

};

其中的klist_devices, klist_drivers 链表会用来挂载该总线的设备与驱动。当需要找东西的时候就会去俩面翻。而上面的两个kset 分别是它们所属的集合。不同的集合对应于不同的操作特性。这是一种很给力的组织结构。就拿这里来说,我们用kobject来组织了一个二维链表(或其他什么数据结构),每个kobject在这个链表中充当了一个节点。但又想让其中指定的一些kobject节点具有一些属性。kset相当于kobject的属性。它包含了进行事件通知需要的一些数据信息。每当kobject有需要时,就会去找到自己所属的kset,或者上级kobject的kset来用。

1.2 重要函数分析

对于总线的注册需要使用到如下函数,通过分析它的行为对于理解bus_type的逻辑结构是很有帮助。

/**

* bus_register - register a bus with the system.

* @bus: bus.

*

* Once we have that, we registered the bus with the kobject

* infrastructure, then register the children subsystems it has:

* the devices and drivers that belong to the bus.

*/

int bus_register(struct bus_type *bus)

{

int retval;

struct bus_type_private *priv;

//alloc a private data package for @bus. It is the core of this structure,

//include device list, deriver list and so on.

priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);

if (!priv)

return -ENOMEM;

priv->bus = bus;

bus->p = priv;

BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

// before we keep on, what we should to know is that this bus is one of members of the great building,

//so it must be inherit form @kobject.

//and @(priv->subsys.kobj) is it's kobject.

retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

if (retval)

goto out;

priv->subsys.kobj.kset = bus_kset; //1).@bus_kset is the root of all buses. 2). this structure is the type of bus.

priv->subsys.kobj.ktype = &bus_ktype; //corresponding operation function for bus

priv->drivers_autoprobe = 1; //automaticly probe when new device arrived.

retval = kset_register(&priv->subsys);

if (retval)

goto out;

retval = bus_create_file(bus, &bus_attr_uevent); //create attribute file for bus,it is a interface between user space and kernel space.

if (retval)

goto bus_uevent_fail;

//给该总线创建一个设备子集,it is the set of all devices about this bus.

//在文件系统中的表现就是在该总线的目录下多了一个名字叫"devices"的子目录

//还需要提醒的一点就是:设备模型中的层次结构关系都是由kobject对象来指定,所以凡是属于这个设备模型的节点必须要继承kobject.

priv->devices_kset = kset_create_and_add("devices", NULL,

&priv->subsys.kobj);

if (!priv->devices_kset) {

retval = -ENOMEM;

goto bus_devices_fail;

}

//create a deriver set for this bus

priv->drivers_kset = kset_create_and_add("drivers", NULL,

&priv->subsys.kobj);

if (!priv->drivers_kset) {

retval = -ENOMEM;

goto bus_drivers_fail;

}

//thoes two list is used to mount some nodes. device-node or deriver-node.

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

klist_init(&priv->klist_drivers, NULL, NULL);

//create attribute file for this structure

retval = add_probe_files(bus);

if (retval)

goto bus_probe_files_fail;

retval = bus_add_attrs(bus); //create attribute file for @bus_attr

if (retval)

goto bus_attrs_fail;

pr_debug("bus: '%s': registeredn", bus->name);

return 0;

bus_attrs_fail:

remove_probe_files(bus);

bus_probe_files_fail:

kset_unregister(bus->p->drivers_kset);

bus_drivers_fail:

kset_unregister(bus->p->devices_kset);

bus_devices_fail:

bus_remove_file(bus, &bus_attr_uevent);

bus_uevent_fail:

kset_unregister(&bus->p->subsys);

kfree(bus->p);

out:

bus->p = NULL;

return retval;

}

在函数体中已经对行为进行了较详细的分析。

1.3 device_bind_driver

那么device到底又是怎么和一个driver进行绑定的呢?让它们在需要时能找到彼此

//bind a driver to one device.

int device_bind_driver(struct device *dev)

{

int ret;

ret = driver_sysfs_add(dev);

if (!ret)

driver_bound(dev);

return ret;

}

//这个函数是在设备已经绑定驱动之后使用,

static void driver_bound(struct device *dev)

{

if (klist_node_attached(&dev->p->knode_driver)) {

printk(KERN_WARNING "%s: device %s already boundn",

__func__, kobject_name(&dev->kobj));

return;

}

pr_debug("driver: '%s': %s: bound to device '%s'n", dev_name(dev),

__func__, dev->driver->name);

if (dev->bus)

blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

BUS_NOTIFY_BOUND_DRIVER, dev);

//把设备节点加入驱动的节点链表中

//?既然设备已经能找到自己的驱动了,为什么还需要加入驱动的链表

//?一个驱动可能支持一组设备,对于那些逻辑操作相同的设备,所有驱动有一个设备链表

klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

}

二. 设备信息

2.1 信息结构体

在总线的其中一端挂载的是设备。其用于记录数据和操作的结构体是device.

struct device {

struct device *parent; //parent device

struct device_private *p; //private data for device, 包含了其拓扑结构信息

struct kobject kobj;

//@device is inherit from @kobject. so it is belong to @devices_kset, which is created by kernel for devices and contain of some operation functions for devices.

//devices_kset

const char *init_name; /* initial name of the device */

struct device_type *type;

struct semaphore sem; /* semaphore 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 */

void *driver_data; /* data private to the driver */

void *platform_data; /* Platform specific data, device

core doesn't touch it */

struct dev_pm_info power;

#ifdef CONFIG_NUMA

int numa_node; /* NUMA node this device is close to */

#endif

u64 *dma_mask; /* dma mask (if dma'able device) */

u64 coherent_dma_mask;/* Like dma_mask, but for

alloc_coherent mappings as

not all hardware supports

64 bit addresses for consistent

allocations such descriptors. */

struct device_dma_parameters *dma_parms;

struct list_head dma_pools; /* dma pools (if dma'ble) */

struct dma_coherent_mem *dma_mem; /* internal for coherent mem

override */

/* arch specific additions */

struct dev_archdata archdata;

dev_t devt; /* dev_t, creates the sysfs "dev" */

spinlock_t devres_lock;

struct list_head devres_head;

struct klist_node knode_class;

struct class *class;

struct attribute_group **groups; /* optional groups */

void (*release)(struct device *dev);

};

而这个节点的拓扑结构信息则保存在 device_private 中

struct device_private {

struct klist klist_children;

//klist containing all children of this device

struct klist_node knode_parent;

//node in sibling list

struct klist_node knode_driver;

//node in driver list, @device is mount on the list of driver by this member.

//In linux system, one device have only one driver.

//But it is not same as to driver, one driver may have a lot of devices, if only those device have a same logical operation.

struct klist_node knode_bus;

//apparently, this device is need to mount on the list of bus, too.

struct device *device;

//point to this device

};

2.2 重要函数

设备的注册函数为 device_register( ), 其会建立设备的拓扑关系,并将设备添加到对应总线的拓扑列表中。

int device_register(struct device *dev)

{

device_initialize(dev);

return device_add(dev);

}

其中的device_initialize( )在对设备进行初始化。初始化中使用的一个非常重要的变量是devices_kset ,是内核在启动时就建立的专门用来装载设备的集合。与之一起初始化的还有另外两个

void device_initialize(struct device *dev)

{

dev->kobj.kset = devices_kset;

//@devices_kset is created by kernel as the parent of all devices.

kobject_init(&dev->kobj, &device_ktype);

//@device_ktype 为kobj定义了两个操作函数

INIT_LIST_HEAD(&dev->dma_pools);

init_MUTEX(&dev->sem);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

device_init_wakeup(dev, 0);

device_pm_init(dev);

set_dev_node(dev, -1);

}

那三个重要设备集合的故事是从这开工的

int __init devices_init(void)

{

......

devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

dev_kobj = kobject_create_and_add("dev", NULL);

sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);

sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);

......

}

对于设备注册来说核心的操作在device_add()中完成,其简化版如下

int device_add(struct device *dev)

{

......

kobject_uevent(&dev->kobj, KOBJ_ADD);

//开始调用事件通知函数

bus_attach_device(dev);

//attach @device to bus

if (parent)

klist_add_tail(&dev->p->knode_parent,

&parent->p->klist_children);

......

}

在这个函数中是bus_attach_device()完成了最后的操作,将设备挂进总线的链表中,在加粗的部分可以很清楚的看到。

void bus_attach_device(struct device *dev)

{

struct bus_type *bus = dev->bus;

int ret = 0;

if (bus) {

if (bus->p->drivers_autoprobe)

ret = device_attach(dev);

// if autoprobe = 1,it will bind to driver

WARN_ON(ret < 0);

//add @device to the devices-list of @bus

if (ret >= 0)

klist_add_tail(&dev->p->knode_bus,

&bus->p->klist_devices);

//@device will be add to the list of the bus.

//@knode_bus is a mount point. think about @containerof()

}

}

三. 驱动信息

3.1 结构体

记录驱动信息的结构体是device_driver,它就是device, bus, driver这个三角模型中的最后一个了。

struct device_driver {

const char *name;

//it is used to match with the device.

struct bus_type *bus;

//corresponding bus for this driver

struct module *owner;

const char *mod_name; /* used for built-in modules */

int (*probe) (struct device *dev);

//this is a probe function, for ensure whether this driver is match with the device.

int (*remove) (struct device *dev);

//when this device is removed.

void (*shutdown) (struct device *dev);

int (*suspend) (struct device *dev, pm_message_t state);

int (*resume) (struct device *dev);

struct attribute_group **groups;

struct dev_pm_ops *pm;

struct driver_private *p;

//as the counterpart of @device and @bus, it is also contain of the hierarchy information.

};

当然,和两外两个一样,结构体中也制作了一个专门用来装该驱动节点拓扑信息的结构体,就是driver_private

struct driver_private {

struct kobject kobj;

//of course, @driver is also a @kobject.

//我们使用kobj为了将该节点增加到这个虚拟的层次结构中。

struct klist klist_devices;

//the list of devices. Thanks for device-model, we could use a driver serices for a serice of devices,

// only if they have a similar operation.

struct klist_node knode_bus;

//@driver will be mounted on the list of driver of the bus.

// Usually, there are two list for the bus.

struct module_kobject *mkobj;

struct device_driver *driver;

//point to container.

};

3.2 重要函数

下面来整理一下驱动注册函数 driver_register,因为它包含了这个结构体的最终解释权

int driver_register(struct device_driver *drv)

{

......

//add @driver to the list of drivers of bus.

ret = bus_add_driver(drv);

......

}

沿着核心部分继续向下找

int bus_add_driver(struct device_driver *drv)

{

......

priv = kzalloc(sizeof(*priv), GFP_KERNEL);

......

//该驱动对象属于对应总线的驱动集

priv->kobj.kset = bus->p->drivers_kset;

error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,

"%s", drv->name);

//假如开启了自动探测,则尝试加载设备

if (drv->bus->p->drivers_autoprobe) {

error = driver_attach(drv);

if (error)

goto out_unregister;

}

//add @driver to the list of drivers of the bus

klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

module_add_driver(drv->owner, drv);

//create attribute file for driver

error = driver_create_file(drv, &driver_attr_uevent);

error = driver_add_attrs(bus, drv);

error = add_bind_files(drv);

//notify a event, this will notify the user space.

kobject_uevent(&priv->kobj, KOBJ_ADD);

return 0;

......

}

看到了两件事,一是函数将 driver挂载到了对应总线的链表中,就是这句话 "klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers) ”,现在我又一次看到了containerof的恶行。自从containerof之后内核经常做一些让我这种新手看着不习惯的链表出来。二是该函数尝试着自动探测对应设备,调用了driver_attach 。这里又出现了一个有趣的东西

/**

* driver_attach - try to bind driver to devices.

* @drv: driver.

*

* Walk the list of devices that the bus has on it and try to

* match the driver with each one. If driver_probe_device()

* returns 0 and the @dev->driver is set, we've found a

* compatible pair.

*/

int driver_attach(struct device_driver *drv)

{

return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

}

driver_attach函数将会去遍历该总线的设备列表,并对每个遍历到的设备调用__driver_attach ,检查一下是否是自己要找的设备。

static int __driver_attach(struct device *dev, void *data)

{

/*

* Lock device and try to bind to it. We drop the error

* here and always return 0, because we need to keep trying

* to bind to devices and some drivers will return an error

* simply if it didn't support the device.

*

* driver_probe_device() will spit a warning if there

* is an error.

*/

//假如这个设备不是自己要找的,则马上退出

if (!driver_match_device(drv, dev))

return 0;

......

//并且这个设备还没有驱动,如果已经有了也是不行的

if (!dev->driver)

driver_probe_device(drv, dev);

......

return 0;

}

接下来就是 driver_probe_device了,这里将会调用探针函数(总线的,或我们的)去检查这个设备是否确实是对应这个驱动的。

int driver_probe_device(struct device_driver *drv, struct device *dev)

{

......

ret = really_probe(dev, drv);

......

}

static int really_probe(struct device *dev, struct device_driver *drv)

{

......

//假如总线没有定义探针函数,则调用用户填写的探测函数

//只要探针函数返回0值,则表示选中。

if (dev->bus->probe) {

ret = dev->bus->probe(dev);

if (ret)

goto probe_failed;

} else if (drv->probe) {

ret = drv->probe(dev);

if (ret)

goto probe_failed;

}

......

//驱动绑定

driver_bound(dev);

}

从上面已经看到了探针函数的使用过程,再来看最后一个部分。

static void driver_bound(struct device *dev)

{

......

//把设备节点加入驱动的节点链表中

//?既然设备已经能找到自己的驱动了,为什么还需要加入驱动的链表

//?一个驱动可能支持一组设备,对于那些逻辑操作相同的设备,所有驱动有一个设备链表

klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

......

}

这就是这个驱动的实际运作了。

四. 总结

这就是设备模型的三个基础部分。那么下一个问题就是,通过对这三个信息结构体填不同的参数就形成了多种多样的模型了吗?

阅读全文
相关推荐

太原市旅游攻略 太原最值得去的地方

太原市旅游攻略 太原最值得去的地方
太原经典1日游:太原第一站先游览山西省博物馆感受5000年的中华文明史,了解感受夏商踪迹、晋国霸业、佛风遗韵、明清晋商文化;然后可以去太原最大的的迎泽公园感受当地人民的生活气息,再去太原食品街寻找美食。

密云古北水镇旅游攻略 密云古北水镇一日游攻略

密云古北水镇旅游攻略 密云古北水镇一日游攻略
古北水镇位于密云东北侧,是一处在古村基础上改造重建的古镇景区。古镇依水而建,即有北方古镇的大气威严,也不失江南水乡的特色,可以泡温泉、品尝美食、爬司马台长城等,是京郊地区休闲度假不错的选择。古镇内不通车,非常适合步行游玩。

银川沙湖旅游攻略 银川沙湖几月份去最好

银川沙湖旅游攻略 银川沙湖几月份去最好
宁夏沙湖旅游景区地处贺兰山下、黄河金岸,20余平方公里沙漠与40余平方公里水域毗邻而居,拥有万亩水域、两千亩芦苇、千亩荷池、五千亩沙丘,这里栖居着白鹤、黑鹤、天鹅等十数种珍鸟奇禽,被誉为“世间少有”的文化旅游胜地。

黔东南旅游攻略 贵州黔东南旅游攻略自由行

黔东南旅游攻略 贵州黔东南旅游攻略自由行
黔东南苗族侗族自治州位于贵州省东南部,地处云贵高原东南边缘,东邻湖南,南接广西,境内居住着苗、侗、汉、布依、水等20多个民族,民族风情非常浓郁。这里有世界上最大的苗寨和最大的侗寨,有独特的吊脚楼、风雨桥、鼓楼。秋季是到黔东南旅行的最好时间,雨水不会像夏季般频繁,气温不会像冬季时寒冷,温度适宜。

青海湖旅游住宿攻略 青海湖环湖住宿攻略

青海湖旅游住宿攻略 青海湖环湖住宿攻略
由于青海湖距离西宁较远,所以建议在青海湖周边过夜,以便欣赏日出日落等美景。目前青海湖周边住宿的选择地方主要有以下几个:一、二郎剑景区附近:这里是青海湖唯一的官方景区,也是观看日出最佳的地点之一。这里有多家酒店和民宿可供选择,价格根据季节和设施不同而不同,旺季每晚约200-500元,淡季每晚约100-300元。

丽江大理洱海旅游攻略 丽江大理攻略最佳旅游攻略

丽江大理洱海旅游攻略 丽江大理攻略最佳旅游攻略
丽江古城一日游攻略:今日主要游览丽江古城,漫步在古城小巷,尽情享受古城慵懒的氛围,还可以在街头巷尾寻觅各式丽江特色小吃来填补肚子。之后前往狮子山,登上狮子山的制高点—万古楼,俯瞰整个丽江古城,运气足够好的话向北可以远眺到玉龙雪山。下了狮子山顺路去木府,近距离欣赏古城内这座仿紫禁城的纳西宫廷式建筑。傍晚回到古城,在四方街感受古城的夜生活,还可以去酒吧消遣一番,说不定会碰上美好的艳遇。古城西侧的狮子山公园是观赏古城全景的最佳去处,除了山顶的万古楼,公园有很多点都可以看到古城和雪山。

长春旅游攻略景点必去 长春市区旅游攻略必去景点

长春旅游攻略景点必去 长春市区旅游攻略必去景点
1、吉林省博物院:吉林省博物院,原名吉林省博物馆。吉林省博物院是一座历史与艺术博物馆,现有文物藏品12万余件,其中一级文物295件、二级文物3379件、三级文物14280件、其它文物近10万件,始自远古,及至现代,精华荟萃,内涵丰富。2、伪满皇宫博物院:伪满洲皇宫博物院建立在伪满皇宫旧址上,是末代皇帝爱新觉罗•溥仪充当伪满洲国傀儡皇帝时居住的宫殿,可见到御用汽车等文物。皇宫可分为进行政治活动的外廷和日常生活的内廷两部分,现分别辟为伪满皇宫陈列馆和伪满帝宫陈列馆,有缉熙楼、中和门、怀远楼、同德殿等建筑景观。

康定新都桥旅游攻略 新都桥必去的几个景点

康定新都桥旅游攻略 新都桥必去的几个景点
新都桥又称东俄罗,位于318国道川藏南北线的分叉路口,号称“摄影家的天堂”。在这可以拍摄到无垠的草甸,曲折的溪流,金黄的秋叶,山坡上大片在觅食的牛羊,散落着的藏族村寨,在金秋来临之际不失为赏秋的好去处,随便一按快门都能得出一张美丽的风景照。此外,国家地理推荐最佳欣赏蜀山之王——贡嘎山的观景点正是在新都桥,天气好可拍摄日照金山之景。

普陀山自驾旅游攻略 普陀山旅游自驾游攻略

普陀山自驾旅游攻略 普陀山旅游自驾游攻略
普陀山在浙江省舟山市普陀区,是舟山群岛1390个岛屿中的一个小岛,形似苍龙卧海,面积近13平方公里,与舟山群岛的沈家门隔海相望,素有“海天佛国”、“南海圣境”之称,是首批国家重点风景名胜区。

南昌旅游攻略景点必去 南昌必看的旅游点

南昌旅游攻略景点必去 南昌必看的旅游点
1、滕王阁:滕王阁临赣江而立,因王勃的《滕王阁序》而闻名,是“江南三大名楼”之一,也是南昌的地标。景区主要由滕王阁主阁,和南北两面的小园子组成,登楼望远是游人来此的主要目的。主阁从外面看是三层带回廊的建筑,其实它里面还有三个暗层和一个设备层,加上两层底座,一共有九层。2、八一广场:位于江西省南昌市的心脏,是江西政治、经济、文化、休闲、娱乐等活动的重要场所。由中心广场、主席台、东西二块游园组成,是江西省最大的城市中心广场。广场中心伫立着由元帅题写的“八一南昌起义纪念塔”,很多游客会在此合影。
本文Tag