Heim > System-Tutorial > LINUX > Hauptteil

Linux-Gerätemodell (5)_Gerät und Gerätetreiber

WBOY
Freigeben: 2024-02-11 08:21:11
nach vorne
547 Leute haben es durchsucht

1. Einführung

Bei der Linux-Treiberentwicklung sind Gerät und Gerätetreiber Grundkonzepte. Die Kernidee des Kernels besteht darin, zwei Datenstrukturen, Gerät und Gerätetreiber, für das Gerät und seinen Treiber zu definieren. Dieser Artikel konzentriert sich auf diese beiden Datenstrukturen, um die Kernlogik des Linux-Gerätemodells vorzustellen, einschließlich:

Linux设备模型(5)_device和device driver

  • Abstraktion, Nutzung und Wartung von Geräten und Gerätetreibern im Kernel
  • Prinzipien der Registrierung, des Ladens und der Initialisierung von Geräten und Gerätetreibern
  • So verwenden Sie Gerätemodelle im eigentlichen Treiberentwicklungsprozess.

Es ist zu beachten, dass bei der Einführung von Gerät und Gerätetreiber möglicherweise viele andere Wissenspunkte beteiligt sind, z. B. Klasse, Bus, DMA, Energieverwaltung usw. Diese Wissenspunkte sind sehr komplex und jeder einzelne davon kann als separates Thema erläutert werden. Daher werden sie in diesem Artikel nicht eingehend analysiert, sondern in nachfolgenden Artikeln gezielt vorgestellt.

2. Strukturgerät und Strukturgerätetreiber

Beim Lesen des Linux-Kernel-Quellcodes können mehr als 60 % der Logik eines bestimmten Moduls über die Kerndatenstruktur verstanden werden, insbesondere über den Gerätemodellteil.

In include/linux/device.h definiert der Linux-Kernel die beiden wichtigsten Datenstrukturen im Gerätemodell, struct device und struct device_driver.

  • Strukturgerät
 1: /* include/linux/device.h, line 660 */
 2: struct device {
 3:     struct device       *parent;
 4:  
 5:     struct device_private   *p;
 6:  
 7:     struct kobject kobj;
 8:     const char *init_name; /* initial name of the device */
 9:     const struct device_type *type;
 10:  
 11:    struct mutex        mutex; /* mutex to synchronize calls to
 12:                             * its driver.
 13:                             */
 14:  
 15:    struct bus_type *bus; /* type of bus device is on */
 16:    struct device_driver *driver; /* which driver has allocated this
 17:                                 device */
 18:    void *platform_data; /* Platform specific data, device
 19:                         core doesn't touch it */
 20:    struct dev_pm_info  power;
 21:    struct dev_pm_domain    *pm_domain;
 22:  
 23: #ifdef CONFIG_PINCTRL
 24:    struct dev_pin_info *pins;
 25: #endif
 26:  
 27: #ifdef CONFIG_NUMA
 28:    int numa_node; /* NUMA node this device is close to */
 29: #endif
 30:    u64     *dma_mask; /* dma mask (if dma'able device) */
 31:    u64     coherent_dma_mask;/* Like dma_mask, but for
 32:                             alloc_coherent mappings as
 33:                             not all hardware supports
 34:                             64 bit addresses for consistent
 35:                             allocations such descriptors. */
 36:  
 37:    struct device_dma_parameters *dma_parms;
 38:  
 39:    struct list_head    dma_pools; /* dma pools (if dma'ble) */
 40:  
 41:    struct dma_coherent_mem *dma_mem; /* internal for coherent mem
 42:                            override */
 43: #ifdef CONFIG_CMA
 44:    struct cma *cma_area; /* contiguous memory area for dma
 45:                            allocations */
 46: #endif
 47:    /* arch specific additions */
 48:    struct dev_archdata archdata;
 49:  
 50:    struct device_node  *of_node; /* associated device tree node */
 51:    struct acpi_dev_node    acpi_node; /* associated ACPI device node */
 52:  
 53:    dev_t           devt; /* dev_t, creates the sysfs "dev" */
 54:    u32         id; /* device instance */
 55:  
 56:    spinlock_t      devres_lock;
 57:    struct list_head    devres_head;
 58:  
 59:    struct klist_node   knode_class;
 60:    struct class *class;
 61:    const struct attribute_group **groups; /* optional groups */
 62:  
 63:    void (*release)(struct device *dev);
 64:    struct iommu_group  *iommu_group;
 65: };
Nach dem Login kopieren

Die Gerätestruktur ist sehr komplex (aber die Qualität der Linux-Kernel-Entwickler ist sehr hoch und die Kommentare zu dieser Schnittstelle sind sehr detailliert. Interessierte Schüler können sich auf den Kernel-Quellcode beziehen. Hier werden wir einige auswählen, die sehr wichtig sind). Verständnis des Gerätemodells.

Parent, das übergeordnete Gerät des Geräts, normalerweise der Bus, die Steuerung und andere Geräte, denen das Gerät untergeordnet ist.

p, ein privater Datenstrukturzeiger für Strukturgeräte. Dieser Zeiger speichert die verknüpfte Untergeräteliste, den verknüpften Listenheader, der früher zu Bus/Treiber/Prent und anderen Geräten hinzugefügt wurde, usw. Weitere Informationen finden Sie im Quellcode .

kobj, das Strukturkobject, das dieser Datenstruktur entspricht.

init_name, der Name des Geräts.

​ Hinweis 1: Im Gerätemodell ist der Name eine sehr wichtige Variable. Jedes im Kernel registrierte Gerät muss einen gültigen Namen haben, der bei der Initialisierung oder vom Kernel entsprechend „Busname + Geräte-ID“ vergeben werden kann.

type, die Struktur struct device_type ist eine neu eingeführte Struktur in der neuen Version des Kernels. Ihre Beziehung zu struct device ist der Beziehung zwischen stuct kobj_type und struct kobject sehr ähnlich, die später ausführlich erläutert wird.

Bus, zu welchem ​​Bus das Gerät gehört (wird später ausführlich beschrieben).

Treiber, der dem Gerät entsprechende Gerätetreiber.

platform_data, ein Zeiger, der zum Speichern spezifischer plattformbezogener Daten verwendet wird. Das spezifische Treibermodul kann hier vorübergehend einige private Daten speichern und bei Bedarf herausnehmen, sodass das Gerätemodell sich nicht um die tatsächliche Bedeutung des Zeigers kümmert.

power, pm_domain, die Logik im Zusammenhang mit der Energieverwaltung wird später im Thema Energieverwaltung erläutert.

Pins, „PINCTRL“-Funktion, noch nicht beschrieben.

numa_node, „NUMA“-Funktion, noch nicht beschrieben.

dma_mask~archdata, DMA-bezogene Funktionen, werden noch nicht beschrieben.

devt, dev_t ist eine 32-Bit-Ganzzahl, die aus zwei Teilen (Major und Minor) besteht. Sie wird als Gerätenummer in Geräten verwendet, die Schnittstellen zum Benutzerbereich in Form von Geräteknoten (Zeichengeräte und Block) bereitstellen müssen Geräte). Hier wird diese Variable hauptsächlich verwendet, um für jedes Gerät mit einer Gerätenummer im SYS-Dateisystem ein entsprechendes Verzeichnis unter /sys/dev/* zu erstellen, wie folgt:

1|root@android:/storage/sdcard0 #ls /sys/dev/char/1:
1:1/ 1:11/ 1:13/ 1:14/ 1:2/ 1:3/ 1:5/ 1:7/ 1:8/ 1:9/
1|root@android:/storage/sdcard0 #ls /sys/dev/char/1:1
1:1/ 1:11/ 1:13/ 1:14/
1|root@android:/storage/sdcard0 # ls /sys/dev/char/1:1
/sys/dev/char/1:1

Klasse, zu welcher Klasse das Gerät gehört.

Gruppen, die Standardattributsammlung des Geräts. Die entsprechende Datei wird bei der Registrierung des Geräts automatisch in sysfs erstellt.

  • struct device_driver
 1: /* include/linux/device.h, line 213 */
 2: struct device_driver {  
 3:     const char *name;  
 4:     struct bus_type     *bus;
 5:  
 6:     struct module       *owner;
 7:     const char *mod_name; /* used for built-in modules */
 8:  
 9:     bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
 10:  
 11:    const struct of_device_id   *of_match_table;
 12:    const struct acpi_device_id *acpi_match_table;
 13:  
 14:    int (*probe) (struct device *dev);
 15:    int (*remove) (struct device *dev);
 16:    void (*shutdown) (struct device *dev);
 17:    int (*suspend) (struct device *dev, pm_message_t state);
 18:    int (*resume) (struct device *dev);
 19:    const struct attribute_group **groups;
 20:  
 21:    const struct dev_pm_ops *pm;
 22:  
 23:    struct driver_private *p;
 24: };
Nach dem Login kopieren

device_driver就简单多了(在早期的内核版本中driver的数据结构为”struct driver”,不知道从哪个版本开始,就改成device_driver了):

name,该driver的名称。和device结构一样,该名称非常重要,后面会再详细说明。

bus,该driver所驱动设备的总线设备。为什么driver需要记录总线设备的指针呢?因为内核要保证在driver运行前,设备所依赖的总线能够正确初始化。

owner、mod_name,內核module相关的变量,暂不描述。

suppress_bind_attrs,是不在sysfs中启用bind和unbind attribute,如下:root@android:/storage/sdcard0 # ls /sys/bus/platform/drivers/switch-gpio/
bind uevent unbind
在kernel中,bind/unbind是从用户空间手动的为driver绑定/解绑定指定的设备的机制。这种机制是在bus.c中完成的,后面会详细解释。

probe、remove,这两个接口函数用于实现driver逻辑的开始和结束。Driver是一段软件code,因此会有开始和结束两个代码逻辑,就像PC程序,会有一个main函数,main函数的开始就是开始,return的地方就是结束。而内核driver却有其特殊性:在设备模型的结构下,只有driver和device同时存在时,才需要开始执行driver的代码逻辑。这也是probe和remove两个接口名称的由来:检测到了设备和移除了设备(就是为热拔插起的!)。

shutdown、suspend、resume、pm,电源管理相关的内容,会在电源管理专题中详细说明。

groups,和struct device结构中的同名变量类似,driver也可以定义一些默认attribute,这样在将driver注册到内核中时,内核设备模型部分的代码(driver/base/driver.c)会自动将这些attribute添加到sysfs中。

p,私有数据的指针,具体的driver代码可以把任何需要的内容放在这里,反正设备模型代码不关心。

3. 设备模型框架下驱动开发的基本步骤

在设备模型框架下,设备驱动的开发是一件很简单的事情,主要包括2个步骤:

步骤1:分配一个struct device类型的变量,填充必要的信息后,把它注册到内核中。

步骤2:分配一个struct device_driver类型的变量,填充必要的信息后,把它注册到内核中。

这两步完成后,内核会在合适的时机(后面会讲),调用struct device_driver变量中的probe、remove、suspend、resume等回调函数,从而触发或者终结设备驱动的执行。而所有的驱动程序逻辑,都会由这些回调函数实现,此时,驱动开发者眼中便不再有“设备模型”,转而只关心驱动本身的实现。

以上两个步骤的补充说明:

\1. 一般情况下,Linux驱动开发很少直接使用device和device_driver,因为内核在它们之上又封装了一层,如soc device、platform device等等,而这些层次提供的接口更为简单、易用(也正是因为这个原因,本文并不会过多涉及device、device_driver等模块的实现细节)。

\2. 内核提供很多struct device结构的操作接口(具体可以参考include/linux/device.h和drivers/base/core.c的代码),主要包括初始化(device_initialize)、注册到内核(device_register)、分配存储空间+初始化+注册到内核(device_create)等等,可以根据需要使用。

\3. device和device_driver必须具备相同的名称,内核才能完成匹配操作,进而调用device_driver中的相应接口。这里的同名,作用范围是同一个bus下的所有device和device_driver。

\4. device和device_driver必须挂载在一个bus之下,该bus可以是实际存在的,也可以是虚拟的。

\5. driver开发者可以在struct device变量中,保存描述设备特征的信息,如寻址空间、依赖的GPIOs等,因为device指针会在执行probe等接口时传入,这时driver就可以根据这些信息,执行相应的逻辑操作了。

4. 设备驱动probe的时机

所谓的”probe”,是指在Linux内核中,如果存在相同名称的device和device_driver(注:还存在其它方式,我们先不关注了),内核就会执行device_driver中的probe回调函数,而该函数就是所有driver的入口,可以执行诸如硬件设备初始化、字符设备注册、设备文件操作ops注册等动作(”remove”是它的反操作,发生在device或者device_driver任何一方从内核注销时,其原理类似,就不再单独说明了)。

设备驱动prove的时机有如下几种(分为自动触发和手动触发):

  • 将struct device类型的变量注册到内核中时自动触发(device_register,device_add,device_create_vargs,device_create)
  • 将struct device_driver类型的变量注册到内核中时自动触发(driver_register)
  • 手动查找同一bus下的所有device_driver,如果有和指定device同名的driver,执行probe操作(device_attach)
  • 手动查找同一bus下的所有device,如果有和指定driver同名的device,执行probe操作(driver_attach)
  • 自行调用driver的probe接口,并在该接口中将该driver绑定到某个device结构中—-即设置dev->driver(device_bind_driver)

注2:probe动作实际是由bus模块(会在下一篇文章讲解)实现的,这不难理解:device和device_driver都是挂载在bus这根线上,因此只有bus最清楚应该为哪些device、哪些driver配对。

注3:每个bus都有一个drivers_autoprobe变量,用于控制是否在device或者driver注册时,自动probe。该变量默认为1(即自动probe),bus模块将它开放到sysfs中了,因而可在用户空间修改,进而控制probe行为。

5. 其它杂项

5.1 device_attribute和driver_attribute

在”Linux设备模型(4)_sysfs”中,我们有讲到,大多数时候,attribute文件的读写数据流为:vfs—->sysfs—->kobject—->attibute—->kobj_type—->sysfs_ops—->xxx_attribute,其中kobj_type、sysfs_ops和xxx_attribute都是由包含kobject的上层数据结构实现。

Linux内核中关于该内容的例证到处都是,device也不无例外的提供了这种例子,如下:

 1: /* driver/base/core.c, line 118 */
 2: static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
 3: char *buf) 
 4: {   
 5:     struct device_attribute *dev_attr = to_dev_attr(attr);
 6:     struct device *dev = kobj_to_dev(kobj);
 7:     ssize_t ret = -EIO;
 8: 
 9:     if (dev_attr->show)
 10:        ret = dev_attr->show(dev, dev_attr, buf);
 11:        if (ret >= (ssize_t)PAGE_SIZE) {
 12:            print_symbol("dev_attr_show: %s returned bad count\n",
 13:                        (unsigned long)dev_attr->show);
 14:    }
 15:    return ret;
 16: }
 17:  
 18: static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
 19: const char *buf, size_t count)
 20: {
 21:    struct device_attribute *dev_attr = to_dev_attr(attr);
 22:    struct device *dev = kobj_to_dev(kobj);
 23:    ssize_t ret = -EIO;
 24: 
 25:    if (dev_attr->store)
 26:        ret = dev_attr->store(dev, dev_attr, buf, count);
 27:    return ret;
 28: }
 29:  
 30: static const struct sysfs_ops dev_sysfs_ops = {
 31:    .show   = dev_attr_show,
 32:    .store  = dev_attr_store,
 33: };
 34:  
 35: /* driver/base/core.c, line 243 */
 36: static struct kobj_type device_ktype = {
 37:    .release    = device_release,
 38:    .sysfs_ops  = &dev_sysfs_ops,
 39:    .namespace = device_namespace,
 40: };
 41:  
 42: /* include/linux/device.h, line 478 */
 43: /* interface for exporting device attributes */
 44: struct device_attribute {
 45:    struct attribute    attr;
 46:    ssize_t (*show)(struct device *dev, struct device_attribute *attr,
 47:                    char *buf);
 48:    ssize_t (*store)(struct device *dev, struct device_attribute *attr,
 49:                    const char *buf, size_t count);
 50: };
Nach dem Login kopieren

至于driver的attribute,则要简单的多,其数据流为:vfs—->sysfs—->kobject—->attribute—->driver_attribute,如下:

 1: /* include/linux/device.h, line 247 */
 2: /* sysfs interface for exporting driver attributes */
 3:  
 4: struct driver_attribute {
 5:     struct attribute attr;
 6:     ssize_t (*show)(struct device_driver *driver, char *buf);
 7:     ssize_t (*store)(struct device_driver *driver, const char *buf,
 8:                     size_t count);
 9: };
 10:  
 11: #define DRIVER_ATTR(_name, _mode, _show, _store)    \
 12: struct driver_attribute driver_attr_##_name =       \
 13:    __ATTR(_name, _mode, _show, _store)
Nach dem Login kopieren

5.2 device_type

device_type是内嵌在struct device结构中的一个数据结构,用于指明设备的类型,并提供一些额外的辅助功能。它的的形式如下:

 1: /* include/linux/device.h, line 467 */
 2: struct device_type {
 3:     const char *name;
 4:     const struct attribute_group **groups;
 5:     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
 6:     char *(*devnode)(struct device *dev, umode_t *mode,
 7:                     kuid_t *uid, kgid_t *gid);
 8:     void (*release)(struct device *dev);
 9:  
 10:    const struct dev_pm_ops *pm;
 11: };
Nach dem Login kopieren

device_type的功能包括:

  • name表示该类型的名称,当该类型的设备添加到内核时,内核会发出”DEVTYPE=‘name’”类型的uevent,告知用户空间某个类型的设备available了
  • groups,该类型设备的公共attribute集合。设备注册时,会同时注册这些attribute。这就是面向对象中“继承”的概念
  • uevent,同理,所有相同类型的设备,会有一些共有的uevent需要发送,由该接口实现
  • devnode,devtmpfs有关的内容,暂不说明
  • release,如果device结构没有提供release接口,就要查询它所属的type是否提供。用于释放device变量所占的空间

5.3 root device

在sysfs中有这样一个目录:/sys/devices,系统中所有的设备,都归集在该目录下。有些设备,是通过device_register注册到Kernel并体现在/sys/devices/xxx/下。但有时候我们仅仅需要在/sys/devices/下注册一个目录,该目录不代表任何的实体设备,这时可以使用下面的接口:

 1: /* include/linux/device.h, line 859 */
 2: /*
 3:  * Root device objects for grouping under /sys/devices
 4:  */
 5: extern struct device *__root_device_register(const char *name,
 6: struct module *owner);
 7:  
 8: /*
 9:  * This is a macro to avoid include problems with THIS_MODULE,
 10:  * just as per what is done for device_schedule_callback() above.
 11:  */
 12: #define root_device_register(name) \
 13: __root_device_register(name, THIS_MODULE)
 14:  
 15: extern void root_device_unregister(struct device *root);
Nach dem Login kopieren

该接口会调用device_register函数,向内核中注册一个设备,但是(你也想到了),没必要注册与之对应的driver(顺便提一下,内核中有很多不需要driver的设备,这是之一)。

Das obige ist der detaillierte Inhalt vonLinux-Gerätemodell (5)_Gerät und Gerätetreiber. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:lxlinux.net
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!