linux设备模型之bus,device,driver
Linux的总线设备驱动模型
Linux的总线设备驱动模型裸机编写驱动⽐较⾃由,按照⼿册实现其功能即可,每个⼈写出来都有很⼤不同;⽽Linux中还需要按照Linux的驱动模型来编写,也就是需要按照“模板”来写,写出来的驱动就⽐较统⼀。
⼀、 Linux采⽤总线设备驱动模型。
主要包含总线、设备、驱动三个部分。
总线:最先注册,有⼏个重要的回调函数,例如match函数⽤于匹配device和driver。
设备:设备的物理信息,例如设备名、物理地址、中断号等;驱动:设备的驱动程序和设备名等信息,例如初始化函数、波特率设置函数、启动停⽌函数、中断函数等。
现实中,很多设备都是接在总线上的,例如SPI Flash接在SPI总线上,EEPROM接在I2C总线上。
但也有很多芯⽚内部设备没有挂在总线上,例如UART、SPI控制器。
为了统⼀使⽤Linux总线设备驱动模型,内核中定义了⼀个虚拟总线platform_bus_type,将这些设备(叫做平台设备)注册到该虚拟总线上统⼀管理。
⼆、Linux驱动注册顺序(总线、设备、驱动三者关系)1、注册总线 xxx_bus_type:在系统初始化阶段,会⾸先向内核注册各种常⽤的总线类型,⽐如pci, usb, spi, i2c, platform等等。
有两个重要的链表挂在bus上,⼀个是设备device链表,⼀个是驱动driver链表。
它包含的最关键的函数:match()⽤于匹配device和driver。
//例⼦:static int __init spi_init(void){int status;printk("@@ spi_init :spi_bus_type\n");buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);if (!buf) {status = -ENOMEM;goto err0;}status = bus_register(&spi_bus_type);if (status < 0)goto err1;status = class_register(&spi_master_class);if (status < 0)goto err2;return 0;err2:bus_unregister(&spi_bus_type);err1:kfree(buf);buf = NULL;err0:return status;}2、注册设备:将系统设备注册进内核的对应总线上,⼤多是调⽤xxx_device_regisger注册。
linux的bus、device、driver介绍
linux的bus、device、driver介绍 linux 通过device和driver分别管理系统中的设备和驱动,⽤bus将设备和驱动关联起来,bus可以看成是设备和驱动的媒介,可以匹配设备和驱动。
这样设备和驱动可以独⽴加载,互不影响。
sysfs是⼀个基于内存的⽂件系统,它的作⽤是将内核信息以⽂件的⽅式提供给⽤户程序使⽤。
我们都知道设备和对应的驱动都是由内核管理的,这些对于⽤户空间是不可见的。
现在通过sysfs,可以在⽤户空间直观的了解设备驱动的层次结构。
⼀、bus注册过程bus_type结构体代表⼀条总线,如下所⽰: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); //device和driver的匹配函数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;};struct subsys_private {struct kset subsys; //对应/sys/bus⽬录struct kset *devices_kset; //对应/sys/bus/devices⽬录struct list_head interfaces;struct mutex mutex;struct kset *drivers_kset; //对应/sys/bus/drivers⽬录struct klist klist_devices; //该bus下的所有devicestruct klist klist_drivers; //该bus下的所有driverstruct blocking_notifier_head bus_notifier;unsigned int drivers_autoprobe:1;struct bus_type *bus;struct kset glue_dirs;struct class *class;};向系统添加⼀条bus_type总线时,改总线会⾃动添加到/sys/bus⽬录下,bus⽬录是系统⾃动创建的,这个bus⽬录为static struct kset *bus_kset,定义在kernel/drivers/base/bus.c中。
linux内核部件分析-设备驱动模型之driver
上节我们分析设备驱动模型中的device,主要是drivers/base/core.c,可以说是代码量最大的一个文件。
本节要分析的驱动driver,就要相对简单很多。
原因也很简单,对于driver,我们能定义的公共部分实在不多,能再sysfs中表达的也很少。
本节的分析将围绕drivers/base/driver.c,但头文件仍然是include/linux/device.h和drivers/base/base.h。
先让我们来看看driver的结构。
[cpp]view plaincopyprint?1.struct device_driver {2.const char *name;3.struct bus_type *bus;4.5.struct module *owner;6.const char *mod_name; /* used for built-in modules */7.8.bool suppress_bind_attrs; /* disables bind/unbind via sysfs */9.10.int (*probe) (struct device *dev);11.int (*remove) (struct device *dev);12.void (*shutdown) (struct device *dev);13.int (*suspend) (struct device *dev, pm_message_t state);14.int (*resume) (struct device *dev);15.const struct attribute_group **groups;16.17.const struct dev_pm_ops *pm;18.19.struct driver_private *p;20.};struct device_driver就是模型定义的通用驱动结构。
linux驱动模型
我们将详细的介绍Linux的设备驱动模型。
Linux设备驱动模型是一个相当复杂的系统,对于初学者来说真有些无从入手。
而且更加困难的是,随着新的Linux Kernel的release,Linux 的设备驱动模型总会有或大或小的变化,我们将尽量展现Linux Kernel 的这种变化。
早期的Linux内核(版本2.4之前)并没有实现一个统一的设备模型,设备节点的创建一般是mknod命令手动创建或利用devfs文件系统创建。
早期的Linux发行版一般会采用手动创建的方式预先把通常用到的节点都创建出来,而嵌入式系统则会采用devfs的方式。
起初Linux 2.6 内核还支持devfs,但从2.6.18开始,内核完全移除了devfs系统而采用的udev的方式动态的创建设备节点。
因此,新的Linux发行版都采用udev的方式管理设备节点文件。
Linux2.6设备驱动模型的基本元素是Class、Bus、Device、Driver,下面我们分别介绍各个部分。
Class 和Class Device驱动模型最基本的概念是设备及其类别,Linux中使用struct class 和struct class_device来管理不同类别的设备。
由于设备驱动模型是一个复杂的系统,我们还是从一个简单的例子开始介绍,然后在逐步展开。
其实实现设备节点的动态创建是一个很简单的事情,并不需要太多的代码。
我们修改我们的驱动初始化函数如下:#include <linux/device.h>#define DEVNAME "hello"static dev_t dev;static struct class *hello_c lass;static struct cdev *hello_cdev;static int __init hello_init(void){int error;error = alloc_chrdev_region(&dev, 0, 2, "hello");if (error){printk("hello: alloc_chardev_region failed! ");goto out;}hello_cdev = cdev_alloc();if (hello_cdev == NULL){printk("hello: alloc cdev failed! ");error = -ENOMEM;goto out_chrdev;}hello_cdev->ops = &hello_fops;hello_cdev->owner = THIS_MODULE;error = cdev_add(hello_cdev, dev, 1);if (error){printk("hello: cdev_add failed! ");goto out_cdev;}hello_class = class_create(THIS_MODULE, DEVNAME);if (IS_ERR(hello_class)){error = PTR_ERR(hello_class);goto out_chrdev;}class_device_create(hello_class, NULL, dev, NULL, DEVNAME);memset (hello_buf, 0, sizeof(hello_buf));memcpy(hello_buf, DEFAULT_MSG, sizeof(DEFAULT_MSG));printk("hello: Hello World! ");return 0;out_cdev:cdev_del(hello_cdev);out_chrdev:unregister_chrdev_region(hello_cdev->dev, 2);out:return error;}static void __exit hello_exit(void){class_device_destroy(hello_class, dev);class_destroy(hello_class);unregister_chrdev_region(hello_cdev->dev, 2);cdev_del(hello_cdev);printk("hello: Goodbye World ");}重新编译这个驱动程序,当加载这个驱动到内核中时,系统(一般是hotplug和udev系统)就会自动的创建我们指定的设备名字:/dev/hello,同时,你也可以发现在sysfs系统中添加了新的文件:/sys/class/hello/hello/。
Linux设备驱动模型与sysfs---platform总线设备驱动
Linux在2.6版本引入了设备驱动模型,设备驱动模型负责统一实现和维护一些特性,诸如:热插拔、对象生命周期、用户空间和驱动空间的交互等基础设施1.设备驱动模型基本概念设备驱动模型主要包含:类(class)、总线(bus)、设备(device)、驱动(driver),它们的本质都是内核中的几种数据结构的“实例”∙类的本质是class结构体类型,各种不同的类其实就是class的各种实例∙总线的本质是bus_type结构体类型,各种不同的总线其实就是bus_type的各种实例∙设备的本质是device结构体类型,各种不同的设备其实就是device的各种实例∙驱动的本质是device_driver结构体类型,各种不同的驱动其实就是device_driver的各种实例2.sysfs基本概念sysfs其实就是/sys目录,其主要作用就是:展示设备驱动模型中各组件的层次关系,并将各组件的本体——内核中的数据结构以文件形式呈现,方便用户层查看及操作3./sys目录结构与设备驱动模型∙/sys目录结构很好的展示了驱动设备模型,如图:∙注意:同一个设备可能在/sys中存在多个设备文件,比如一颗led的设备文件可能在/sys/bus/platform/devices/led1,同时还有一个在/sys/class/leds/led1。
虽然他们都是同一颗led的设备文件,但是他们的来源、机制、原理都是不同的,不能混为一谈4.各组件的特性与联系∙kobject:设备驱动模型各实例的最基本单元,提供一些公用型服务如:提供该实例在sysfs中的操作方法(show和store);提供在sysfs中以文件形式存在的属性,其实就是应用接口;提供各个实例的层次架构,让sysfs中弄出目录结构。
设备驱动模型中每个实例内部都会包含一个kobject∙总线、设备、驱动,这三者有着密切的联系。
在内核中,设备和驱动是分开注册的,注册设备的时候,并不需要驱动已经存在,而驱动被注册的时候,也不需要对应的设备已经被注册。
Linuxkernel驱动相关抽象概念及其实现之“bus,device,driver”
Linuxkernel驱动相关抽象概念及其实现之“bus,device,driver”bus,device,driver三个很重要的概念贯穿Linux内核驱动架构,特转载⼀篇博⽂:内核的开发者将总线,设备,驱动这三者⽤软件思想抽象了出来,巧妙的建⽴了其间的关系,使之更形象化。
结合前⾯所学的知识,总的来说其三者间的关系为bus有两条链表,分别⽤于挂接设备和驱动,指定了其⾃⾝bus的device或者driver最后都会分别连接到对应bus的这两条链表上,⽽总线⼜有其始端,为bus_kset,⼀个driver可以对应于⼏个设备,因此driver同样有其设备链表,⽤于挂接可以操作的设备,其⾃⾝也有bus挂接点,⽤于将⾃⾝挂接到对应bus(每个driver只属于⼀条总线),⽽对于device,⼀个设备只属于⼀条总线,只能有⼀个driver与其对应,因此对于device,都是单⼀的,⼀个driver挂接点,⼀个bus挂接点,device与bus相同的是都有始端,device为devices_kset,因此device的注册同时会出现在对应的bus⽬录和device总⽬录下。
好了,下⾯就以源码为例分别分析⼀下bus,device,driver的注册过程。
⼀、bus的注册bus的注册⽐较简单,⾸先来看⼀下bus的结构:1struct bus_type {2const char *name; //名字3struct bus_attribute *bus_attrs; //bus属性集4struct device_attribute *dev_attrs; //device属性集5struct driver_attribute *drv_attrs; //driver属性集6int (*match)(struct device *dev, struct device_driver *drv);7int (*uevent)(struct device *dev, struct kobj_uevent_env *env);8int (*probe)(struct device *dev);9int (*remove)(struct device *dev);10void (*shutdown)(struct device *dev);11int (*suspend)(struct device *dev, pm_message_t state);12int (*resume)(struct device *dev);13const struct dev_pm_ops *pm;14struct bus_type_private *p; //bus的私有成员15 };16//其中重点看⼀下私有成员结构体:17struct bus_type_private {18struct kset subsys; //bus内嵌的kset,代表其⾃⾝19struct kset *drivers_kset;20struct kset *devices_kset;21struct klist klist_devices; //包含devices链表及其操作函数22struct klist klist_drivers; //driver链表及其操作函数23struct blocking_notifier_head bus_notifier;24 unsigned int drivers_autoprobe:1; //匹配成功⾃动初始化标志25struct bus_type *bus;26 };⽆论是bus,driver,还是device其本⾝特征都放在私有成员⾥,其注册时,都会申请并填充这个结构体,下⾯具体分析⼀下bus的注册流程,从bus_register开始:1int bus_register(struct bus_type *bus)2 {3int retval;4struct bus_type_private *priv;5 priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); //进⼊时bus_type->bus_type_private为NULL6if (!priv) //该函数主要是对其的设置7return -ENOMEM;8 priv->bus = bus; //私有成员的bus回指该bus9 bus->p = priv; //初始化bus->p,即其私有属性10 BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);11 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //设置该bus的名字,bus是kset的封装12if (retval)13goto out;14//bus_kset即为所有bus的总起始端点15//围绕bus内嵌的kset初始化,和kset的初始化时围绕16 priv->subsys.kobj.kset = bus_kset; //kobj相似,没有parent时,就会⽤kset的kobj,此处即是17 priv->subsys.kobj.ktype = &bus_ktype; //属性操作级别统⼀为bus_ktype18 priv->drivers_autoprobe = 1; //设置该标志,当有driver注册时,会⾃动匹配devices19//上的设备并⽤probe初始化,20//当有device注册时也同样找到 driver并会初始化21 retval = kset_register(&priv->subsys); //注册kset,创建⽬录结构,以及层次关系22if (retval)23goto out;24 retval = bus_create_file(bus, &bus_attr_uevent); //当前bus⽬录下⽣成bus_attr_uevent属性⽂件25if (retval)26goto bus_uevent_fail;27 priv->devices_kset = kset_create_and_add("devices", NULL, //初始化bus⽬录下的devices⽬录,⾥⾯级联了该bus下设备,28 &priv->subsys.kobj); //仍然以kset为原型29if (!priv->devices_kset) {30 retval = -ENOMEM;31goto bus_devices_fail;32 }33 priv->drivers_kset = kset_create_and_add("drivers", NULL, //初始化bus⽬录下的drivers⽬录,⾥⾯级联了该bus下设备的driver34 &priv->subsys.kobj);35if (!priv->drivers_kset) {36 retval = -ENOMEM;37goto bus_drivers_fail;38 }39 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //初始化klist_devices⾥的操作函数成员40 klist_init(&priv->klist_drivers, NULL, NULL); //klist_drivers⾥的操作函数置空41 retval = add_probe_files(bus); //增加bus_attr_drivers_probe和bus_attr_drivers_autoprobe42if (retval) //属性⽂件43goto bus_probe_files_fail;44 retval = bus_add_attrs(bus); //增加默认的属性⽂件45if (retval)46goto bus_attrs_fail;47 pr_debug("bus: '%s': registered/n", bus->name);48return0;49 bus_attrs_fail: //以下为错误处理50 remove_probe_files(bus);51 bus_probe_files_fail:52 kset_unregister(bus->p->drivers_kset);53 bus_drivers_fail:54 kset_unregister(bus->p->devices_kset);55 bus_devices_fail:56 bus_remove_file(bus, &bus_attr_uevent);57 bus_uevent_fail:58 kset_unregister(&bus->p->subsys);59out:60 kfree(bus->p);61 bus->p = NULL;62return retval;63 }由此可见,bus⼜是kset的封装,bus_register主要完成了其私有成员bus_type_private的初始化,并初始化了其下的两个⽬录devices和drivers,及其属性⽂件,bus有个⾃⼰的根⽬录也就是bus有个起始端点,是bus_kset,经过此番的注册,bus⽬录下将会出现我们注册的bus,并且其下会有device和driver两个⼦⽬录,代表它下⾯的driver和device链表。
linux内核部件分析(九)——设备驱动模型之device
linux内核部件分析(九)——设备驱动模型之device前面我们分析了device、driver、bus三种类型,主要是三者的注册与注销,在sysfs中的目录与属性文件创建等内容。
本节就来详细分析下,在设备注册到总线上时,总线是如何为其寻找对应的驱动的;在驱动注册到总线上时,总线又是如何为其寻找对应的设备的。
本节的实现代码集中在drivers/base/bus.c和drivers/base/dd.c中。
先来回忆下,在device_register()->device_add()中,先是调用bus_add_device()添加device与bus间的联系,并添加bus 为device定义的属性,然后会调用bus_probe_device()。
bus_probe_device()会试图为已挂在总线上的该设备寻找对应的驱动。
我们的故事就从这里开始。
[cpp] view plaincopyprint?/** * bus_probe_device - probe drivers for a new device * @dev: device to probe * * - Automatically probe for a driver if the bus allows it. */ void bus_probe_device(struct device *dev) { struct bus_type *bus = dev->bus; int ret; if (bus&& bus->p->drivers_autoprobe){ ret = device_attach(dev);WARN_ON(ret < 0); } } bus_probe_device()为总线上的设备寻找驱动。
它先是检查bus->p->drivers_autoprobe,看是否允许自动探测。
Linux设备驱动模型
简介作者:hjlin内核版本:2.6.29设备驱动模型框架是linux驱动编程的基础。
它通过kobject,kset,ktype等底层数据结构将bus_type, device, device_driver 等高层数据结构组织起来,形成一个层次、分类清晰的驱动模型。
优点如下:1.代码重用。
将对象抽象为总线、驱动、设备三种,各司其职。
同一总线的多个驱动使用相同的总线对象。
同一驱动可以关联驱动多个设备。
2.通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。
同时sysfs文件系统还提供了方便的同用户控件交互的接口。
框架数据结构KobjectKobject是代表驱动模型中的一个对象。
总线、驱动、设备都继承了它。
(在结构体中包含kobject)。
每个kobject在sysfs中表现为一个目录。
每个kobject都有一个parent kobject和所属的kset。
Kset就是kobject所属的kset,通过kset 的链表可以找到所有属于它的kobject。
这些kobject进行uevent操作时,都会调用所属的kset 的uevent_ops方法。
父kobj,用于表示kobject之间或者kobject和kset,kset之间的在sysfs 中的目录结构关系。
如果父kobj不存在,并且所属的kset存在的话,则父kobj就是设置为所属的kset的内嵌kobj。
因此,注册设备、驱动或者总线的时候如果不指定parent kobj的话,父kobj则会设置为所属的kset的kobj。
(todo:最好画图表示关系)Kest通过kset可以将kobject组织成一颗层次树。
kobj_typebus_type代表一个总线。
对应/sys/bus下的一个目录。
管理相应总线下的所有驱动和设备。
struct bus_type {const char *name; //总线名称struct bus_attribute *bus_attrs; //该总线目录下的属性文件以及相应的访问方法struct device_attribute *dev_attrs; //该总线设备子目录下的属性文件以及相应的访问方法struct driver_attribute *drv_attrs; //该总线驱动子目录下的属性文件以及相应的访问方法int (*match)(struct device *dev, struct device_driver *drv); //驱动模型进行驱动和设备的匹配时调用int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //uevent方法int (*probe)(struct device *dev); //match成功之后会调用。
总线设备驱动
linux内核学习---总线,设备,驱动Linux 设备模型中三个很重要的概念就是总线,设备,驱动.即 bus,device,driver,而实际上内核中也定义了这么一些数据结构,他们是 struct bus_type,struct device,struct device_driver,这三个重要的数据结构都来自一个地方,include/linux/device.h.我们知道总线有很多种,pci总线,scsi 总线,usb 总线,所以我们会看到Linux 内核代码中出现pci_bus_type,scsi_bus_type,usb_bus_type,他们都是 struct bus_type 类型的变量.而struct bus_type 结构中两个非常重要的成员就是 struct kset drivers 和 struct kset devices。
kset 和另一个叫做 kobject 正是 Linux Kernel 2.6 中设备模型的基本元素。
这里我们只需要知道,drivers 和 devices 的存在,让struct bus_type 与两个链表联系了起来,一个是devices 的链表,一个是 drivers 的链表,也就是说,知道一条总线所对应的数据结构,就可以找到这条总线所关联的设备有哪些,又有哪些支持这类设备的驱动程序.而要实现这些,就要求每次出现一个设备就要向总线汇报,或者说注册,每次出现一个驱动,也要向总线汇报,或者说注册.比如系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立起一个 struct device 的变量,每一次有一个驱动程序,就要准备一个 struct device_driver 结构的变量.把这些变量统统加入相应的链表,device 插入 devices 链表,driver 插入 drivers 链表. 这样通过总线就能找到每一个设备,每一个驱动。
struct bus_type 中为 devices 和 drivers 准备了两个链表,而代表 device 的结构体 struct device 中又有两个成员,struct bus_type *bus 和 struct device_driver *driver。
Linux设备驱动模型介绍
文库资料 ©2017 Guangzhou ZHIYUAN Electronics Stock Co., Ltd.第1章 Linux 设备驱动模型介绍设备驱动模型,对系统的所有设备和驱动进行了抽象,形成了复杂的设备树型结构,采用面向对象的方法,抽象出了device 设备、driver 驱动、bus 总线和class 类等概念,所有已经注册的设备和驱动都挂在总线上,总线来完成设备和驱动之间的匹配。
总线、设备、驱动以及类之间的关系错综复杂,在Linux 内核中通过kobject 、kset 和subsys 来进行管理,驱动编写可以忽略这些管理机制的具体实现。
设备驱动模型的内部结构还在不停的发生改变,如device 、driver 、bus 等数据结构在不同版本都有差异,但是基于设备驱动模型编程的结构基本还是统一的。
Linux 设备驱动模型是Linux 驱动编程的高级内容,这一节只对device 、driver 等这些基本概念作介绍,便于阅读和理解内核中的代码。
实际上,具体驱动也不会孤立的使用这些概念,这些概念都融合在更高层的驱动子系统中。
对于大多数读者可以忽略这一节内容。
1.1.1 设备在Linux 设备驱动模型中,底层用device 结构来描述所管理的设备。
device 结构在文件<linux/device.h>中定义,如程序清单1.1所示。
程序清单1.1 device 数据结构定义struct device {struct device *parent; /* 父设备*/ struct device_private *p; /* 设备的私有数据 */ struct kobjectkobj; /* 设备的kobject 对象 */ const char *init_name; /*设备的初始名字 */ struct device_type *type;/* 设备类型*/ struct mutex mutex; /*同步驱动的互斥信号量 */ struct bus_type *bus; /*设备所在的总线类型 */ struct device_driver *driver; /*管理该设备的驱动程序*/ void*platform_data;/*平台相关的数据 */ struct dev_pm_infopower;/* 电源管理*/#ifdef CONFIG_NUMA int numa_node; /*设备接近的非一致性存储结构*/ #endifu64 *dma_mask; /* DMA 掩码*/ u64 coherent_dma_mask; /*设备一致性的DMA 掩码*/struct device_dma_parameters *dma_parms; /* DMA 参数*/ struct list_headdma_pools; /* DMA 缓冲池*/ struct dma_coherent_mem*dma_mem; /* DMA 一致性内存 */ /*体系结构相关的附加项*/ struct dev_archdata archdata;/* 体系结构相关的数据*/ #ifdef CONFIG_OFstruct device_node*of_node;文库资料 ©2017 Guangzhou ZHIYUAN Electronics Stock Co., Ltd.注册和注销device 的函数分别是device_register()和device_unregister(),函数原型如下:int __must_check device_register(struct device *dev); void device_unregister(struct device *dev);大多数不会在驱动中单独使用device 结构,而是将device 结构嵌入到更高层的描述结构中。
linux设备驱动(2)device详解
linux设备驱动(2)device详解Linux设备驱动的模型,是建⽴在sysfs设备⽂件系统和kobject上的,由总线(bus)、设备(device)、驱动(driver)和类(class)所组成的关系结构,在底层,Linux系统中的每个设备都有⼀个device结构体的实例。
struct device已在上⼀博⽂中介绍,下⾯按顺序详细介绍device的主要API。
1.device_createsource位于:drivers\base\Core.c。
向sysfs注册⼀个device,提供了强⼤的格式化注册接⼝。
1/**2 * device_create - creates a device and registers it with sysfs3 * @class: pointer to the struct class that this device should be registered to4 * @parent: pointer to the parent struct device of this new device, if any5 * @devt: the dev_t for the char device to be added6 * @drvdata: the data to be added to the device for callbacks7 * @fmt: string for the device's name8 *9 * This function can be used by char device classes. A struct device10 * will be created in sysfs, registered to the specified class.11 *12 * A "dev" file will be created, showing the dev_t for the device, if13 * the dev_t is not 0,0.14 * If a pointer to a parent struct device is passed in, the newly created15 * struct device will be a child of that device in sysfs.16 * The pointer to the struct device will be returned from the call.17 * Any further sysfs files that might be required can be created using this18 * pointer.19 *20 * Returns &struct device pointer on success, or ERR_PTR() on error.21 *22 * Note: the struct class passed to this function must have previously23 * been created with a call to class_create().24*/25struct device *device_create(struct class *class, struct device *parent,26 dev_t devt, void *drvdata, const char *fmt, ...)27 {28 va_list vargs;29struct device *dev;3031 va_start(vargs, fmt);32 dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);33 va_end(vargs);34return dev;35 }1.1device_create_vargs上⾯API只是为了提供完善的接⼝,下⾯API这个才是真正⼯作的。
Linux设备模型
Linux设备模型1.kobject, kobj_type, ksetkobject是Linux设备模型的基础,像bus, device, driver都是典型的kobject容器,这些容器通过kobject相互连接,匹配,形成了树状结构.所以kset是把这些东西串联起来的节点.61struct kobject {62const char *name;63struct list_headentry;64struct kobject *parent;65struct kset *kset;66struct kobj_type *ktype;67struct kernfs_node *sd;68struct krefkref;69 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE70struct delayed_workrelease;71 #endif72 unsigned int state_initialized:1;73 unsigned int state_in_sysfs:1;74 unsigned int state_add_uevent_sent:1;75 unsigned int state_remove_uevent_sent:1;76 unsigned int uevent_suppress:1;77 };kobj_type release方法用于释放kobject占用的资源,sysfs_ops则是提供了读写attr的show, store 方法,attribute是kobject的默认的属性. 在sysfs中,kobjet对应目录,attribute对应文件kset是一组特定类型的kobject的集合,维护了一个kobject链表. 虽然理论上讲,这些kobject 的类型可以不一样,但是总的来说kset存在的意义,在于把这些kobject集合到一起,用统一的方式操作它们.147/**148 * structkset - a set of kobjects of a specific type, belonging to a specific subsystem.149 *150 * A kset defines a group of kobjects. They can be individually151 * different "types" but overall these kobjects all want to be grouped 152 * together and operated on in the same manner.ksets are used to153 * define the attribute callbacks and other common events that happen to154 * a kobject.155 *156 * @list: the list of all kobjects for this kset157 * @list_lock: a lock for iterating over the kobjects158 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)159 * @uevent_ops: the set of uevent operations for this kset. These are 160 * called whenever a kobject has something happen to it so that the kset161 * can add new environment variables, or filter out the uevents if so 162 * desired.163 */164struct kset {165struct list_headlist;166spinlock_t list_lock;167struct kobjectkobj;168conststruct kset_uevent_ops *uevent_ops;169 };2.bus, device, driverbus Linux系统设备模型的主框架,各种不同类型的bus,像I2C,SPI, USB甚至抽象出来的platform 等构成了Linux设备模型的主干。
Linux设备模型之总线 设备 和驱动
Linux设备模型之总线设备和驱动《Linux内核修炼之道》读书笔记1、设备模型的上层建筑由总线(bus) 、设备(device)、驱动(device_drive r)这3个数据结构构成,设备模型表示了它们之间的连接关系。
在设备模型中,所有的设备都通过总线连接。
总线可以是物理存在的,也可以是虚拟的。
比如内部的platform总线。
设备是连接到某条物理或虚拟总线上的对象。
可能是真正的物理对象,也可能的是虚拟对象。
驱动是用来和设备通信的软件程序。
驱动可以从设备获得数据,也可以把相应数据发给设备进行处理。
2、数据结构(1)、总线struct bus_type {const char *name;总线类型的名称struct bus_attribute*bus_attrs;struct device_attribute*dev_attrs;struct driver_attribute*drv_attrs;int (*match)(struct device *dev, struct device_driver *drv);设备和驱动能否对应,就是有该总线的match方式决定。
不同总线的match 方式不一样。
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 pm_ext_ops *pm;struct bus_type_private *p;};现在如上数据结构和书中讲的有所不同,只不过有包装了一层数据结构struct bus_type_private *p,源码如下:/*** struct bus_type_private - structure to hold the private to thedriver core portions of the bus_type structure.** @subsys - the struct kset that defines this bus. This is the m ain kobjectsubsys描述该总线的子系统,它连接到全局量kset bus_subsys中。
Linux设备模型:Bus
Linux设备模型:Bus1. 概述在Linux设备模型中,Bus(总线)是一类特殊的设备,它是连接处理器和其它设备之间的通道(channel)。
为了方便设备模型的实现,内核规定,系统中的每个设备都要连接在一个Bus上,这个Bus可以是一个内部Bus、虚拟Bus或者Platform Bus。
内核通过struct bus_type结构,抽象Bus,它是在include/linux/device.h中定义的。
本文会围绕该结构,描述Linux内核中Bus的功能,以及相关的实现逻辑。
最后,会简单的介绍一些标准的Bus(如Platform),介绍它们的用途、它们的使用场景。
2. 功能说明按照老传统,描述功能前,先介绍一下该模块的一些核心数据结构,对bus模块而言,核心数据结构就是struct bus_type,另外,还有一个sub system相关的结构,会一并说明。
2.1 struct bus_type1: /* inlcude/linux/device.h, line 93 */2: struct bus_type {3: const char *name;4: const char *dev_name;5: struct device *dev_root;6: struct bus_attribute *bus_attrs;7: struct device_attribute *dev_attrs;8: struct driver_attribute *drv_attrs;9:10: int (*match)(struct device *dev, struct device_driver *drv);11: int (*uevent)(struct device *dev, struct kobj_uevent_env *env);12: int (*probe)(struct device *dev);13: int (*remove)(struct device *dev);14: void (*shutdown)(struct device *dev);15:16: int (*suspend)(struct device *dev, pm_message_t state);17: int (*resume)(struct device *dev);。
你知道Linux设备驱动模型是怎么样构成的?
你知道Linux设备驱动模型是怎么样构成的?尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要。
Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。
换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。
在正式进入设备驱动模型的学习之前,有必要把documentation/filesystems/sysfs.txt读一遍(不能偷懒)。
sysfs.txt主要描述/sys目录的创建及其属性,sys目录描述了设备驱动模型的层次关系,我们可以简略看一下/sys目录,block:所有块设备devices:系统所有设备(块设备特殊),对应struct device的层次结构bus:系统中所有总线类型(指总线类型而不是总线设备,总线设备在devices下),bus的每个子目录都包含--devices:包含到devices目录中设备的软链接--drivers:与bus类型匹配的驱动程序class:系统中设备类型(如声卡、网卡、显卡等)fs:一些文件系统,具体可参考filesystems /fuse.txt中例子dev:包含2个子目录--char:字符设备链接,链接到devices目录,以:命名--block:块设备链接Linux设备模型学习分为:Linux设备底层模型,描述设备的底层层次实现(kobject);Linux 上层容器,包括总线类型(bus_type)、设备(device)和驱动(device_driver)。
==== Linux设备底层模型====。
linux下bus,device,driver三者关系
linux下bus,device,driver三者关系1.bus:总线作为主机和外设的连接通道,有些总线是比较规范的,形成了很多协议。
如PCI,USB,1394,IIC等。
任何设备都可以选择合适的总线连接到主机。
当然主机也可能就是CPU本身。
内存也是通过BUS连接到主机的,可内存使用的总线不是外设总线,因此和内存使用相同类型的总线的设备,谈及BUS比较含糊。
一块网卡在嵌入式开发中,直接通过内存总线接入到CPU。
我们在写网卡驱动时,要给该BUS定义就麻烦了。
2.driver:驱动程序是在CPU运行时,提供操作的软件接口。
所有的设备必须有与之配套驱动程序才能正常工作。
一个驱动程序可以驱动多个类似或者完全不同的设备。
3.device:设备就是连接在总线上的物理实体。
设备是有功能之分的。
具有相同功能的设备被归到一个类(CLASS中)。
如音频设备(和声音相关的都算),输入设备(鼠标,键盘,游戏杆等)。
从宏观考虑,任何设备必须要连接到主机才能发挥其作用。
一个鼠标离开了电脑主机就不再是鼠标了。
提到了连接就必然出现总线BUS。
任何设备要正常运行必须有软件支持,所有的设备必须有DRIVER。
设备的目的就是完成功能,根据功能的分类,该设备必然属于某个CLASS。
1.三者的定义在 include/Linux/device.h里。
2.总线中的那两条链表是怎么形成:这要求每次出现一个设备就要向总线汇报,或者说注册,每次出现一个驱动,也要向总线汇报,或者说注册。
比如系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立起一个struct device的变量,每一次有一个驱动程序,就要准备一个struct device_driver结构的变量。
把这些变量统统加入相应的链表,device 插入devices 链表,driver插入drivers链表。
这样通过总线就能找到每一个设备,每一个驱动.然而,假如计算机里只有设备却没有对应的驱动,那么设备无法工作。
Linux总线、设备、驱动模型与设备树
Linux总线、设备、驱动模型与设备树
1.总线、设备、驱动模型
本着⾼内聚、低耦合的原则,Linux 把设备驱动模型分为了总线、设备和驱动三个实体,这三个实体在内核⾥的职责分别如下:
设备和驱动向总线进⾏注册,总线负责把设备和对应的驱动绑定起来。
驱动通过总线 API 接⼝platform_get_resource()取得板级设备信息,这样驱动和设备之间就实现了⾼内聚、低耦合的设计,
⽆论设备怎么换,驱动都可以岿然不动。
代码架构如下图所⽰:
2、设备树
引⼊设备树之前,关于硬件设备的描述信息⼀般放在⼀个个类似 arch/xxx/mach-xxx/board-xxx.c 的⽂件中,
这些代码中除了描述的设备信息不同,其代码逻辑都是⼀样的。
我们有理由,把这些设备端的信息,⽤⼀个⾮ C 的脚本语⾔来描述,这个脚本⽂件,就是 Device Tree(设备树)。
设备树是⼀种 dts ⽂件,它⽤简单的语法描述每个板⼦上的所有设备,以及这些设备的连接信息。
设备树⽂件存储在⽬录 arch/xxx/boot/dts 中,每⼀个 board 对应⼀个 dts ⽂件。
引⼊设备树之后,⼤量重复的 C 代码(arch/xxx/mach-xxx/board-xxx.c)被去除——驱动的归于驱动 C 代码,设备的归于设备树脚本⽂件。
arch/arm/mach-xxx/board-a.c 这样的⽂件永远地进⼊了历史的故纸堆,换个板⼦,只要换个 Device Tree ⽂件就好。
代码架构如下图所⽰:。
LinuxI2C子系统之bus_device_driver
LinuxI2C子系统之bus_device_driverLinux I2C Register ProcedureBus RegisterCODE PATH: ~/kernel/drivers/i2c/i2c-core.cFunction:bus_register()~/kernel/drivers/base/bus.c863/**864 * bus_register - register a driver-core subsystem865 * @bus: bus to register866 *867 * Once we have that, we register the bus with the kobject 868 * infrastructure, then register the children subsystems it has:869 * the devices and drivers that belong to the subsystem.870 */871int bus_register(struct bus_type *bus)872 {873intretval;874struct subsys_private *priv;875struct lock_class_key *key = &bus->lock_key;876/*给subsys_private结构体分配空间,这个结构体是用来管理bus上的device和driver */ 877priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);879 return -ENOMEM;880/*这两行代码是把 i2c_bus_type和它的subsys_private互相关联*/881priv->bus = bus;882bus->p = priv;883884BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);885/*把i2c bus对应的kobject name 设置成“i2c”*/886retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);887 if (retval)888goto out;889/*把i2c bus的kobj归到bus_kset下*/890priv->subsys.kobj.kset = bus_kset;891priv->subsys.kobj.ktype = &bus_ktype;892priv->drivers_autoprobe = 1;893/*注册i2c bus的kset,它会在/sys/bus/下创建一个i2c目录通过kobject_uevent(&k->kobj, KOBJ_ADD);向用户空间发送一个uevent*/894retval = kset_register(&priv->subsys);895 if (retval)896goto out;897/*在bus目录下创建一个uevent节点*/898retval = bus_create_file(bus, &bus_attr_uevent);900gotobus_uevent_fail;901/*先来分析下kset_create_and_add函数,900 * kset_create_and_add - create a structkset dynamically and add it to sysfs901 *902 * @name: the name for the kset903 * @uevent_ops: a structkset_uevent_ops for the kset904 * @parent_kobj: the parent kobject of this kset, if any.1.是会创建一个kset,名字是devices,当然之后会调用kset_register在bus/i2c/目录下生成一个目录“devices”.2.而且这个kset的parent是i2c bus3.drivers kset注册同理*/902priv->devices_kset = kset_create_and_add("devices", NULL,903&priv->subsys.kobj);904 if (!priv->devices_kset) {905retval = -ENOMEM;906gotobus_devices_fail;907 }908909priv->drivers_kset = kset_create_and_add("drivers", NULL,910&priv->subsys.kobj);911 if (!priv->drivers_kset) {912retval = -ENOMEM;913gotobus_drivers_fail;914 }915916INIT_LIST_HEAD(&priv->interfaces);917__mutex_init(&priv->mutex, "subsysmutex", key);/*初始化devices 和drivers两个链表*/918klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); 919klist_init(&priv->klist_drivers, NULL, NULL);920/*在bus/i2c/目录下再创建两个节点drivers_probe和drivers_autoprobe*/921retval = add_probe_files(bus);922 if (retval)923gotobus_probe_files_fail;924/*有待验证*/925retval = bus_add_groups(bus, bus->bus_groups);926 if (retval)927gotobus_groups_fail;928929pr_debug("bus: '%s': registered\n", bus->name);930 return 0;931932bus_groups_fail:933remove_probe_files(bus);934bus_probe_files_fail:935kset_unregister(bus->p->drivers_kset);936bus_drivers_fail:937kset_unregister(bus->p->devices_kset);938bus_devices_fail:939bus_remove_file(bus, &bus_attr_uevent);940bus_uevent_fail:941kset_unregister(&bus->p->subsys);942out:943kfree(bus->p);944bus->p = NULL;945 return retval;946 }/*至此,用bus_register来注册i2c bus的结果就很清晰了1.把i2c bus的kset的(其实是kset->kobj)parent设置成bus_kset,所谓bus_kset对应的就是在/sys目录下的bus目录2.在/sys/bus/目录下,创建i2c目录,并关联好他们之间的kobject,kset3.在/sys/bus/i2c/目录下创建节点(uevent, probe, aotuprobe), 目录(devices, drivers)*/Device Register。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
总线属性的删除, 使用:
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
(4)总线实例: 1:首先是要准备一个总线 bus_type.也就是定义一个 bus_type,然后 给它填上一些成员。定义如下:
struct bus_type my_bus_type = { .name = "my_bus", .match = my_match, };
(5)测试 将 bus.c 以动态加载的方式加载到内核, insmod bus.ko ,在 /sys/bus/ 目录下会有一个 my_bus 目录,这就是我们添加的总线。该目录下有
devices ,drivers 目录,因为该总线上没有挂载任何设备和驱动,所以这
两个目录都为空;同时,在/sy/devices 目录下,还可看到 my_bus0 设 备(总线本身也是一种设备)。 二:设备: 关于设备的一些常用结构体:device,
参数中的 attr,即为 bus_attr_name。 另外, 就是参数中的 show 方法,设置方法如下
static ssize_t show_bus_version(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", Version); }
1:struct device { struct device * parent; //父设备,一般一个 bus 也对应一个设备。 struct kobject kobj;//代表自身 char bus_id[BUS_ID_SIZE]; struct bus_type * bus; /* 所属的总线 */ struct device_driver *driver; /* 匹配的驱动*/ void *driver_data; /* data private to the driver 指向驱动 */
} struct device my_dev={ //创建设备属性 .bus = &my_bus_type,//定义总线类型 .parent = &my_bus,//定义 my_dev 的父设备。 .release = my_device_release, }; static ssize_t mydev_show(struct device *dev, char *buf) { return sprintf(buf, "%s\n", "This is my device!"); } static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL); static int __init my_device_init(void){ int ret; strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE); //初始化设备 ret = device_register(&my_dev); //注册设备 if (ret) printk("device register!\n"); device_create_file(&my_dev, &dev_attr_dev); //创建设备文件 return ret; } static void __exit my_device_exit(void) { device_unregister(&my_dev);//卸载设备 } module_init(my_device_init); module_exit(my_device_exit); MODULE_AUTHOR("Fany"); MODULE_LICENSE("GPL");
当加载驱动时,驱动就支总线上找到自己对应的设备。或者先把驱动 加载上,来了一个设备就去总线找驱动。 一:总线 总线是处理器与设备之间通道,在设备模型中,所有的设备都通 过总线相连。 关于总线的一些结构体:bus_type, (1)bus_type:
struct bus_type { const char * name;//设备名称 struct subsystem subsys;//代表自身 struct kset drivers; //当前总线的设备驱动集合 struct kset devices; //所有设备集合 struct klist klist_devices; struct klist klist_drivers; struct bus_attribute * bus_attrs;//总线属性
基本关系简要的概括 linux2.6 提供了新的设备模型:总线、 驱动、 设备。 如下: 驱动核心可以注册多种类型的总线。 每种总线下面可以挂载许多设备。(通过 kset devices) 每种总线下可以用很多设备驱动。(通过包含一个 kset drivers)} 每个驱动可以处理一组设备。按照我的理解就是所有的设备都挂载到总线上,
if (ret) printk("device_register failed!\n"); return ret; } static void __exit my_bus_exit(void) { bus_unregister(&my_bus_type);//删除总线属性 device_unregister(&my_bus);//删除总线设备 } module_init(my_bus_init); module_exit(my_bus_exit); MODULE_AUTHOR("Fany"); MODULE_LICENSE("GPL");
2:设备属性: sysfs 中的设备入口可有属性. 相关的结构是:
struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, char *buf); ssize_t (*store)(struct device *dev, const char *buf, size_t count); };
注册:int bus_register(struct bus_type * bus) 注销:void bus_unregister(struct bus_type *bus);
(3)总线属性 bus_attribute
struct bus_attribute { struct attribute attr; ssize_t (*show)(struct bus_type *bus, char *buf); ssize_t (*store)(struct bus_type *bus, const char *buf,size_t count); }; BUS_ATTR(name, mode, show, store);
linuform 总线的设备驱动时,总理不清三者之间的关系, 后来通过看国嵌的视频教程, 到 bus,device,driver,才对 linux 的 设备模型有了一个更深入的了解。于是,便在开发板上,测试了一下, 在此,把学过的东西,做下总结。
这个宏声明一个结构, 产生它的名子通过前缀字符串 bus_attr_ 到给定 的名子(bus_attr_name),然后利用 bus_create_file 来创建总线属性
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
static int my_match(struct device *dev, struct device_driver *driver) { return !strncmp(dev->bus_id, driver->name, strlen(driver->name)); } static int my_bus_release(struct device *dev) { return 0; } static ssize_t show_bus_version(struct bus_type *bus, char *buf) { return sprintf(buf, PAGE_SIZE, "%s\n", version); } static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
3:创建设备实例:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/device.h> #include <linux/string.h> extern struct device my_bus; //这里用到了总线设备中定义的结构体 extern struct bus_type my_bus_type; static int my_device_release() { return 0;
struct device_attribute * dev_attrs;//设备属性 struct driver_attribute * drv_attrs; int (*match)(struct device * dev, struct device_driver * drv);//设备驱动匹配函数 int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);//热拔插事件 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); };