前言

Linux将所有的设备统一抽象为struct device结构, 同时将所有的驱动统一抽象为struct device_driver结构。这样设计之后就方便驱动开发工程师编写驱动,只需要将具体的设备包含struct device结构,具体的驱动包含struct device_driver结构。最终会调用device_register和driver_register将驱动和设备注册到系统,表现出来就是在sys目录的device和driver目录下。本小节先分析device结构,以及相关API,以及如何注册到系统中,以及提供给上层的sys接口。

数据结构

Linux将所有的设备统一抽象为struct device结构。定义在<linux/device.h>
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 thisdevice */void     *platform_data; /* Platform specific data, devicecore doesn't touch it */void      *driver_data;   /* Driver data, set and get withdev_set/get_drvdata */struct dev_pm_info    power;struct dev_pm_domain  *pm_domain;#ifdef CONFIG_PINCTRLstruct dev_pin_info *pins;
#endif#ifdef CONFIG_NUMAint     numa_node;  /* NUMA node this device is close to */
#endifu64       *dma_mask;  /* dma mask (if dma'able device) */u64     coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */unsigned long  dma_pfn_offset;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 memoverride */
#ifdef CONFIG_DMA_CMAstruct cma *cma_area;      /* contiguous memory area for dmaallocations */
#endif/* arch specific additions */struct dev_archdata  archdata;struct device_node *of_node; /* associated device tree node */struct acpi_dev_node acpi_node; /* associated ACPI device node */dev_t           devt;   /* dev_t, creates the sysfs "dev" */u32           id; /* device instance */spinlock_t     devres_lock;struct list_head    devres_head;struct klist_node   knode_class;struct class        *class;const struct attribute_group **groups;   /* optional groups */void   (*release)(struct device *dev);struct iommu_group   *iommu_group;bool           offline_disabled:1;bool         offline:1;
};

device结构体比较复杂,同时其中还包含了dma, numa,  pintrl, pm相关的知识,本小节不做过多谈论。不过该结构体的注释是相当的详细,可以参考。

parent:       代表设备的parent节点,通常parent是bus或者controller。 如果此parent为NULL,则此设备就是顶层设备。
p:                代表device的私有数据的指针,指针类型为device_private。
kobj:            kobject结构,用于层级关系。
init_name:   设备对象的名称,出现在sys目录下。
type:            指向device_type结构,代表了设备的特殊的信息。
mutex:          同步操作。
bus:              设备所属的总线。
driver:          设备所对应的驱动。
platfrorm_data:  设备所对应的平台数据。
driver_data:     保存设备所对于驱动的数据,一般使用get/set函数。
power/pm_domain:  电源管理相关的内容。
of_node:        该设备对应的设备树结构。
devt:               设备号,由主设备号和次设备号组成。
id:                   设备索引号。
devres_head: 设备资源管理的链表。用于将设备使用的资源用链表管理。
class:             设备所属的class。
group:            设备默认的属性。

设备相关函数

  • devices_init
此函数主要是初始化devices_kset,以及初始化所有的dev_kset
int __init devices_init(void)
{devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);   if (!devices_kset)return -ENOMEM;dev_kobj = kobject_create_and_add("dev", NULL);if (!dev_kobj)goto dev_kobj_err;sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);if (!sysfs_dev_block_kobj)goto block_kobj_err;sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);if (!sysfs_dev_char_kobj)goto char_kobj_err;return 0;char_kobj_err:kobject_put(sysfs_dev_block_kobj);block_kobj_err:kobject_put(dev_kobj);dev_kobj_err:kset_unregister(devices_kset);return -ENOMEM;
}

内核中每一个设备都是struct device结构,同时内核将所有的设备使用devices_kset管理。同时为了统一方便管理又将设备分为block和char设备,生成的内核对象分别为sysfs_dev_block_kobj和sysfs_dev_char_kobj。当然这两个内核模块都是在dev内核模块之下的。

这个函数的操作实现最后表现到sys文件目录下,分别为/sys/devices,  /sys/dev,  /sys/dev/char, /sys/dev/block。
  • device_initialize(用于初始化一个device)
void device_initialize(struct device *dev)
{dev->kobj.kset = devices_kset;kobject_init(&dev->kobj, &device_ktype);INIT_LIST_HEAD(&dev->dma_pools);mutex_init(&dev->mutex);lockdep_set_novalidate_class(&dev->mutex);spin_lock_init(&dev->devres_lock);INIT_LIST_HEAD(&dev->devres_head);device_pm_init(dev);set_dev_node(dev, -1);
}

主要是设置设备所属的kset,设置设备所属kset的ktype,初始化设备资源管理的链表,以及设备电源管理初始化。

  • device_add(添加一个设备到系统中)
int device_add(struct device *dev)
{struct device *parent = NULL;struct kobject *kobj;struct class_interface *class_intf;int error = -EINVAL;dev = get_device(dev);                                  //增加此设备的引用计数if (!dev)goto done;if (!dev->p) {error = device_private_init(dev);            ---------------Aif (error)goto done;}/** for statically allocated devices, which should all be converted* some day, we need to initialize the name. We prevent reading back* the name, and force the use of dev_name()*/if (dev->init_name) {dev_set_name(dev, "%s", dev->init_name);dev->init_name = NULL;}/* subsystems can specify simple device enumeration */if (!dev_name(dev) && dev->bus && dev->bus->dev_name)dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);if (!dev_name(dev)) {                                                  //设置device的name域,如果设置失败退出。error = -EINVAL;                                                   goto name_error;}                                              pr_debug("device: '%s': %s\n", dev_name(dev), __func__);parent = get_device(dev->parent);                                      //增加设备parent的引用计数kobj = get_device_parent(dev, parent);                                 //设置设备parent的内核对象,建立设备在sys下的层次结构。if (kobj)dev->kobj.parent = kobj;/* use parent numa_node */if (parent)set_dev_node(dev, dev_to_node(parent));/* first, register with generic layer. *//* we require the name to be set before, and pass NULL */error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);             //调用kobject_add将设备添加到sys中if (error)goto Error;/* notify platform of device entry */if (platform_notify)                                                 platform_notify(dev); error = device_create_file(dev, &dev_attr_uevent);                 //创建设备的uevent属性if (error)goto attrError;if (MAJOR(dev->devt)) {error = device_create_file(dev, &dev_attr_dev);             //如果主设备号存在,创建dev属性if (error)goto ueventattrError;error = device_create_sys_dev_entry(dev);if (error)goto devtattrError;devtmpfs_create_node(dev);                                  //调用devtmpfs,自动创建设备节点}error = device_add_class_symlinks(dev);                             //创建链接文件if (error)goto SymlinkError;error = device_add_attrs(dev);                                      //添加设备的属性if (error)goto AttrsError;error = bus_add_device(dev);                                        //将此设备添加到一条总线上if (error)goto BusError;error = dpm_sysfs_add(dev);if (error)goto DPMError;device_pm_add(dev);                                                  //将设备添加到pm core链表中,会在suspend中使用到/* Notify clients of device addition.  This call must come* after dpm_sysfs_add() and before kobject_uevent().*/if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,    //使用通知链同时有新设备添加到bus中BUS_NOTIFY_ADD_DEVICE, dev);kobject_uevent(&dev->kobj, KOBJ_ADD);                     //使用uevent通知上层,这时候就会使用到device_uevent_ops中的函数bus_probe_device(dev);                                    //匹配bus下的设备与驱动if (parent)klist_add_tail(&dev->p->knode_parent,             //如果parent存在,将dev添加到parent链表中&parent->p->klist_children);if (dev->class) {                                         //如果class存在,则将dev添加到class链表中mutex_lock(&dev->class->p->mutex);/* tie the class to the device */klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);/* notify any interfaces that the device is here */list_for_each_entry(class_intf,&dev->class->p->interfaces, node)if (class_intf->add_dev)class_intf->add_dev(dev, class_intf);mutex_unlock(&dev->class->p->mutex);}
}

A:  如果设备的device_private不存在,就重现分配一个

int device_private_init(struct device *dev)
{dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);                         //分配一个device_private结构if (!dev->p)return -ENOMEM;dev->p->device = dev;                                                  //设置device_private的device结构klist_init(&dev->p->klist_children, klist_children_get,         klist_children_put);                                       //初始化设备下所有children链表INIT_LIST_HEAD(&dev->p->deferred_probe);return 0;
}

B:  主要分析下如何将设备添加到一条总线上,这是核心。

int bus_add_device(struct device *dev)
{struct bus_type *bus = bus_get(dev->bus);int error = 0;if (bus) {pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));error = device_add_attrs(bus, dev);if (error)goto out_put;error = device_add_groups(dev, bus->dev_groups);if (error)goto out_groups;error = sysfs_create_link(&bus->p->devices_kset->kobj,&dev->kobj, dev_name(dev));if (error)goto out_id;error = sysfs_create_link(&dev->kobj,&dev->bus->p->subsys.kobj, "subsystem");if (error)goto out_subsys;klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);}return 0;
}

如果此设备不属于任何总线,直接就返回,否则添加设备属性,创建链接文件,将此bus下的设备添加到klist_devices链表中。

C:  关于总线先设备与驱动的匹配是设备驱动模型的核心
void bus_probe_device(struct device *dev)
{struct bus_type *bus = dev->bus;struct subsys_interface *sif;int ret;if (!bus)                               //如果不存在总线,直接返回return;if (bus->p->drivers_autoprobe) {       //是否总线支持自动匹配ret = device_attach(dev);WARN_ON(ret < 0);}mutex_lock(&bus->p->mutex);           list_for_each_entry(sif, &bus->p->interfaces, node)if (sif->add_dev)sif->add_dev(dev, sif);            mutex_unlock(&bus->p->mutex);
}

一般总线都会支持自动匹配,当然可以修改driver_autoprobe的值,然后调用device_attach进行匹配。

int device_attach(struct device *dev)
{int ret = 0;device_lock(dev);                      //因为此操作只能运行一个dev进行,需要互斥保护。if (dev->driver) {                     //如果设备存在对应的driver,说明已经进行了匹配,只需要调用device_bind_driver在sys中建立关系。if (klist_node_attached(&dev->p->knode_driver)) {ret = 1;goto out_unlock;}ret = device_bind_driver(dev);if (ret == 0)ret = 1;else {dev->driver = NULL;ret = 0;}} else {ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);      //如果driver不存在,则需要遍历dev所在bus下的所有driverpm_request_idle(dev);}
out_unlock:device_unlock(dev);return ret;
}

当遍历driver下过程中,最终会调用到__device_attach函数。

static int __device_attach(struct device_driver *drv, void *data)
{struct device *dev = data;if (!driver_match_device(drv, dev))return 0;return driver_probe_device(drv, dev);
}

函数match最终使用的匹配是

static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

根据bus是否存在match函数,如果存在调用bus的match函数,如果不存在,直接返回1。如果匹配成功之后就会调用driver_probe_device进行绑定,最终会调用really_probe函数。

static int really_probe(struct device *dev, struct device_driver *drv)
{int ret = 0;int local_trigger_count = atomic_read(&deferred_trigger_count);atomic_inc(&probe_count);pr_debug("bus: '%s': %s: probing driver %s with device %s\n",drv->bus->name, __func__, drv->name, dev_name(dev));WARN_ON(!list_empty(&dev->devres_head));dev->driver = drv;                                                 //设置设备的driver变量/* If using pinctrl, bind pins now before probing */ret = pinctrl_bind_pins(dev);if (ret)goto probe_failed;if (driver_sysfs_add(dev)) {                                       printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}if (dev->bus->probe) {ret = dev->bus->probe(dev);                    //如果bus的probe存在,则调用bus的probe函数if (ret)goto probe_failed;} else if (drv->probe) {                               //否则调用驱动的probe,这下知道驱动的probe函数是如何调用的。ret = drv->probe(dev);if (ret)goto probe_failed;}driver_bound(dev);                                     //将设备所属的驱动加入到驱动链表中,使用通知链发出BOUND信息。ret = 1;pr_debug("bus: '%s': %s: bound device %s to driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);goto done;probe_failed:               //probe失败之后,就会清空设备资源,设备driver设置为NULL,从sys中移除dev建立的driver链接。devres_release_all(dev);driver_sysfs_remove(dev);dev->driver = NULL;dev_set_drvdata(dev, NULL);if (ret == -EPROBE_DEFER) {/* Driver requested deferred probing */dev_info(dev, "Driver %s requests probe deferral\n", drv->name);driver_deferred_probe_add(dev);/* Did a trigger occur while probing? Need to re-trigger if yes */if (local_trigger_count != atomic_read(&deferred_trigger_count))driver_deferred_probe_trigger();} else if (ret != -ENODEV && ret != -ENXIO) {/* driver matched but the probe failed */printk(KERN_WARNING"%s: probe of %s failed with error %d\n",drv->name, dev_name(dev), ret);} else {pr_debug("%s: probe of %s rejects match %d\n",drv->name, dev_name(dev), ret);}/** Ignore errors returned by ->probe so that the next driver can try* its luck.*/ret = 0;
}
  • device_register(注册一个设备到系统中是上述两个函数的结合体)
  • device_unregister(将一个设备从系统中注销)
void device_unregister(struct device *dev)
{pr_debug("device: '%s': %s\n", dev_name(dev), __func__);device_del(dev);put_device(dev);
}

此函数分为两步走,第一步从系统中移除此设备,第二步将设备的引用计数减去1。主要的注销操作在device_del中实现,也就是register的反操作。

void device_del(struct device *dev)
{struct device *parent = dev->parent;struct class_interface *class_intf;/* Notify clients of device removal.  This call must come* before dpm_sysfs_remove().*/if (dev->bus)      //如果bus总线存在,调用通知链删除设备blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_DEL_DEVICE, dev);dpm_sysfs_remove(dev); //电源管理函数,关掉设备电源if (parent)            //如果有父设备,将当前设备从父设备所属链表删除klist_del(&dev->p->knode_parent);if (MAJOR(dev->devt)) {     //如果主设备号存在, 动态删除设备节点,移除设备的属性devtmpfs_delete_node(dev);device_remove_sys_dev_entry(dev);device_remove_file(dev, &dev_attr_dev);}if (dev->class) {   //如果设备所属的class存在,移除设备的链接,调用remove_dev做移除,从class链表中删除该设备device_remove_class_symlinks(dev);mutex_lock(&dev->class->p->mutex);/* notify any interfaces that the device is now gone */list_for_each_entry(class_intf,&dev->class->p->interfaces, node)if (class_intf->remove_dev)class_intf->remove_dev(dev, class_intf);/* remove the device from the class list */klist_del(&dev->knode_class);mutex_unlock(&dev->class->p->mutex);}device_remove_file(dev, &dev_attr_uevent);    //移除设备的uevent属性device_remove_attrs(dev);            bus_remove_device(dev);                       //从总线中移除设备,以及电源管理等。device_pm_remove(dev);driver_deferred_probe_del(dev);/* Notify the platform of the removal, in case they* need to do anything...*/if (platform_notify_remove)platform_notify_remove(dev);if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_REMOVED_DEVICE, dev);kobject_uevent(&dev->kobj, KOBJ_REMOVE);cleanup_device_parent(dev);kobject_del(&dev->kobj);              //删除设备的内核模块put_device(parent);
}

设备属性

linux中使用device_attribute结构体表示一个设备的属性
struct device_attribute {struct attribute  attr;ssize_t (*show)(struct device *dev, struct device_attribute *attr,char *buf);ssize_t (*store)(struct device *dev, struct device_attribute *attr,const char *buf, size_t count);
};

通常使用to_dev_attr宏定义得到device_attribute对象

#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)

同时为了方便定义设备的属性,内核提供了一系列相关的宏定义,用于初始化设备的属性。

#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define DEVICE_ATTR_RW(_name) \struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR_WO(_name) \struct device_attribute dev_attr_##_name = __ATTR_WO(_name)

关于设备属性的调用过程,最终会调用到设备的show和store函数中,具体的流程分析可见Linux设备驱动模型-Ktype

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,char *buf)
{struct device_attribute *dev_attr = to_dev_attr(attr);struct device *dev = kobj_to_dev(kobj);ssize_t ret = -EIO;if (dev_attr->show)ret = dev_attr->show(dev, dev_attr, buf);if (ret >= (ssize_t)PAGE_SIZE) {print_symbol("dev_attr_show: %s returned bad count\n",(unsigned long)dev_attr->show);}return ret;
}static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,const char *buf, size_t count)
{struct device_attribute *dev_attr = to_dev_attr(attr);struct device *dev = kobj_to_dev(kobj);ssize_t ret = -EIO;if (dev_attr->store)ret = dev_attr->store(dev, dev_attr, buf, count);return ret;
}static const struct sysfs_ops dev_sysfs_ops = {.show  = dev_attr_show,.store = dev_attr_store,
};

设备类型

在include/linux/device.h文件存在这样的结构体:
struct device_type {const char *name;const struct attribute_group **groups;int (*uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, umode_t *mode,kuid_t *uid, kgid_t *gid);void (*release)(struct device *dev);const struct dev_pm_ops *pm;
};

此结构代表设备类型。通常一个Bus下会存在各种设备的,比如:disks,  mouse,  event等。而此结构就表明此设备是何种类型的设备。

name:     代表设备的名称,在上报event的时候,会通过DEVTYPE设置设备的类型名称。
groups:   代码设备的属性,在添加设备的属性的时候,如果存在设备类型,也会添加设备类型的属性。
uevent:   在新增一个设备时,通过该函数上报设备类型的uevent。
devnode: 在创建一个设备节点的时候会使用到,获取设备的信息。
release:   在设备释放时候,如果存在设备类型会调用到。

Linux设备驱动模型-Device相关推荐

  1. linux设备驱动模型 - device/bus/driver

    在linux驱动模型中,为了便于管理各种设备,我们把不同设备分别挂在他们对应的总线上,设备对应的驱动程序也在总线上找,这样就提出了deivce-bus-driver的模型,硬件上有许多设备总线,那么我 ...

  2. Linux设备驱动模型1——简介和底层架构

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...

  3. linux设备驱动模型及其他,Linux设备驱动模型

    Linux设备驱动模型,主要函数分析 整个驱动模型中,最核心的三个函数分别是 __bus_register.driver_register.device_register int __bus_regi ...

  4. linux平台设备驱动模型是什么意思,Linux设备驱动模型之我理解

    点击(此处)折叠或打开 /* my_bus.c   */ #include #include #include #include #include #include "my_bus.h&qu ...

  5. Linux设备驱动模型之platform(平台)总线详解

    /********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...

  6. Linux设备驱动模型三 kset

    Linux设备驱动模型三 kset 1 kset数据结构 kset的定义在前文已有描述,我们再回顾一下: [cpp] view plain copy struct kset { /*与子kobject ...

  7. Linux设备驱动模型二 kobject

    Linux设备驱动模型二 kobject 1 kobject 1.1 kobject数据结构 kobject是sysfs文件系统的基础数据结构,它定义在include/linux/kobjec.h中 ...

  8. Linux设备驱动模型一 sysfs

    Linux设备驱动模型一 sysfs 1 Linux设备模型 Linux 2.5的内核引入了一种新的设备模型,目的是对计算机上的所有设备进行统一的管理. 它包含以下基础结构: 类型 说明 设备Devi ...

  9. 五.linux设备驱动模型

    站在设备驱动这个角度分析,设备驱动模型是如何构建出来,起到什么作用,认识它并在写驱动的时候去利用设备驱动模型 目录 一.linux 设备驱动模型简介 1.1. 什么是设备驱动模型 1.2. 为什么需要 ...

  10. 整理--linux设备驱动模型

    知识整理–linux设备驱动模型 以kobject为底层,组织类class.总线bus.设备device.驱动driver等高级数据结构,同时实现对象引用计数.维护对象链表.对象上锁.对用户空间的表示 ...

最新文章

  1. 重构职场竞争力之测试能力提升方法
  2. 二级计算机为让利消费者,计算机二级office题库训练题(2)
  3. 复盘无人业态的三点心得:起于共享单车,止于何?
  4. Android香露刀之SeekBar之双管齐下
  5. nagios 使用mysql_Nagios监控MySQL
  6. java.lang 源码剖析_java.lang.Void类源码解析
  7. qt调用import sys库_Python模块之 sys 模块
  8. mysql 文本备份_[MySQL]用mysqldump制作文本备份_MySQL
  9. [转载] python仿真入门_python基础-入门
  10. flux架构浅谈:什么数据才应该放store
  11. WPF在DLL中读取Resource的方法
  12. 神经网络结构可视化工具总结实践大全
  13. 【Unreal】关于实时编码(live coding)退出UE编辑器C++ Class消失的问题
  14. Word生成目录后,二级,三级目录页码处与一级目录不齐
  15. BUGKU writeup
  16. 360怎么修改域名服务器地址,怎样修改DNS地址
  17. search_web_resources
  18. 北京游玩之北海首都博物馆
  19. python倒数切片_python的切片操作
  20. 小学生html教程,一个在加华人妈妈整理的30个小学生学习网站

热门文章

  1. [转]Magento2开发教程 - 如何向数据库添加新表
  2. 第二次作业刘惠惠2.6,2.15
  3. 盗版牢骚? or 学而不思?
  4. 查看sqlserver信息
  5. swift. 扩展类添加属性_swift 扩展属性的方法
  6. Spring 的 BeanPostProcessor接口实现
  7. Apache Spark Meetup China 第1期 最全资料下载
  8. Redis五大数据类型以及操作---散列表
  9. 重构计算力 浪潮M5新一代服务器闪耀登场
  10. HDU 2883 kebab(最大流)