掌握 Linux 设备模型、设备树、sysfs
和平台设备的概念,为编写更复杂的驱动程序奠定基础。
Linux 内核采用了 设备模型(Device Model) 来管理系统中的所有设备。设备模型为内核提供了一个统一的方式来管理硬件设备和与之交互的驱动程序。
struct device
来表示。struct device_driver
来表示。设备树(Device Tree)是一个数据结构,描述了硬件设备的布局。在嵌入式系统中,设备树用于帮助内核识别和初始化硬件设备。设备树不依赖于内核代码,它是一个纯粹的数据文件,允许内核在启动时加载硬件信息。
/boot/dtbs
目录下,文件扩展名为 .dtb
。/dts-v1/;
/ {
model = "My Custom Board";
compatible = "generic-board";
memory {
reg = <0x80000000 0x40000000>; // 内存起始地址和大小
};
uart0: serial@9000000 {
compatible = "uart16550";
reg = <0x9000000 0x1000>;
interrupts = <0x14>;
};
};
在这个示例中,定义了一个 UART 设备(serial@9000000
),它的内存映射基地址是 0x9000000
,并且配置了中断。
compatible
字段定义了该设备支持的设备类型。reg
表示设备的寄存器地址。interrupts
定义了设备中断号。sysfs
:与用户空间交互的接口sysfs 详细解释
sysfs
是 Linux 提供的一个虚拟文件系统,旨在使内核中的设备信息和状态暴露给用户空间程序。通过读取和写入 sysfs
中的文件,用户可以与设备驱动进行交互,并且通过这种方式进行设备配置、状态查询等操作。sysfs
通过 /sys
目录提供了一种访问内核对象(如设备、驱动、总线等)属性的方式。
sysfs
的常见路径/sys/class/
:设备类型目录,每个设备类别(例如 net
,block
等)都会在这里有一个子目录。/sys/devices/
:所有设备的路径信息,包括硬件设备的具体描述。/sys/bus/
:与总线相关的设备,像 PCI、USB 总线等会在这里列出。sysfs
作用/sys
中的文件写入数据,可以改变设备的配置。/sys
中的文件获取设备的实时状态或参数。/sys
中的文件,用户可以查看内核的内存、CPU 资源使用情况等信息。创建设备属性文件
设备属性文件允许用户空间程序通过简单的文件 I/O 操作(如读取、写入)来交互。驱动程序可以使用 sysfs
来创建这些文件,并通过文件暴露设备的状态或控制接口。
代码示例:
struct device_attribute dev_attr_myattribute = {
.attr = { .name = "myattribute", .mode = S_IRUGO },
.show = myattribute_show,
};
struct device_attribute
:这是 Linux 内核中定义的一个结构体,用来表示一个设备的属性。通过这个结构体,设备驱动可以指定属性的名称、访问权限以及读取(show
)和写入(store
)操作函数。
dev_attr_myattribute
:这是定义的设备属性结构体实例。每个属性结构体包含了以下信息:
.attr
:属性的具体信息,包括文件名(name
)和访问模式(mode
)。S_IRUGO
表示该文件是只读的,且可以被用户空间读取。
.show
:设备属性文件的读取操作函数。该函数的作用是将设备的状态或配置输出到缓冲区 buf
中。
show
函数:
static ssize_t myattribute_show(struct device *dev, struct device_attribute *attr, char *buf) {
return sprintf(buf, "42\n");
}
myattribute_show
:这是属性的读取函数,它会在用户空间读取 /sys
文件时调用。
dev
:设备指针,指向调用该函数的设备实例。
attr
:属性指针,指向该属性。
buf
:缓冲区,用于将属性值返回给用户空间。
sprintf(buf, "42\n")
:将 42
作为属性值写入缓冲区 buf
中。
该函数的作用是当用户读取 /sys/.../myattribute
文件时,返回 “42” 字符串。
注册设备属性
设备属性文件需要通过 sysfs_create_file
注册到 sysfs
中。这使得属性文件出现在 /sys
下,并且允许用户空间应用通过文件访问该属性。
注册属性:
sysfs_create_file(&dev->kobj, &dev_attr_myattribute.attr);
dev->kobj
:每个设备都有一个 kobject
,它是与设备相关的内核对象。在 sysfs
中,所有的设备属性都与 kobject
紧密相关。kobject
是内核对象的一个基本表示,它具有与设备相关的属性。
dev_attr_myattribute.attr
:这是我们在前面定义的属性结构体 dev_attr_myattribute
。通过 sysfs_create_file
,该属性会被创建并注册到设备的 kobject
中,形成 /sys
目录下的一个文件。
sysfs_create_file
:此函数将属性文件添加到 sysfs
文件系统中。如果成功,用户可以通过 /sys
目录访问该文件。例如,如果设备 dev
关联了属性 myattribute
,那么该文件将出现在 /sys/.../myattribute
下。
访问属性文件
一旦设备属性文件创建并注册到 sysfs
中,用户空间的应用程序就可以通过标准的文件 I/O 操作(如读取)来与设备进行交互。例如,可以使用 cat /sys/.../myattribute
来读取属性值。
/sys/.../myattribute
文件时,myattribute_show
函数会被调用,返回属性值 “42”。删除设备属性
当设备不再需要该属性时,需要通过 sysfs_remove_file
删除该属性文件,防止内存泄漏:
sysfs_remove_file(&dev->kobj, &dev_attr_myattribute.attr);
总结
通过 sysfs
,驱动程序能够将内核对象的属性暴露给用户空间。sysfs
通过虚拟文件系统提供了一种简单、直观的机制,使得设备、内核信息可以通过文件系统接口访问。驱动程序通过创建和注册设备属性文件,允许用户空间读取和写入设备参数或状态。这种机制简化了内核与用户空间的交互,广泛应用于设备配置、调试和状态查询等场景。
平台设备(Platform Devices) 是嵌入式系统中用于表示硬件设备的一种设备类型,通常与平台(如 ARM)相关联。平台设备不依赖于总线(例如:PCI、USB),而是通过静态配置或设备树来配置。
platform_device_register()
和 platform_driver_register()
来注册。static struct platform_device *pdev;
static int __init platform_dev_init(void) {
pdev = platform_device_register_simple("my_platform_device", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
return 0;
}
static void __exit platform_dev_exit(void) {
platform_device_unregister(pdev);
}
module_init(platform_dev_init);
module_exit(platform_dev_exit);
在这个例子中,我们使用 platform_device_register_simple
来注册一个平台设备,并使用 platform_device_unregister
来注销设备。
platform_driver_register
:注册平台驱动程序。platform_device_register
:注册平台设备。device_create
:在 /dev
中创建一个设备文件。class_create
:创建一个设备类(/sys/class/
)。sysfs_create_file
:在 sysfs
中创建一个设备属性文件。alloc_chrdev_region
:分配一个字符设备号。cdev_add
:注册字符设备。sysfs
文件操作sysfs
操作sysfs
文件,并通过 show
函数读取一个值。sysfs
操作是否成功。内容项 | 说明 |
---|---|
设备模型 | Linux 使用设备模型管理设备、驱动和总线。 |
设备树 | 设备树是硬件配置的描述文件,帮助内核识别硬件设备。 |
sysfs |
用于内核与用户空间交互的虚拟文件系统,设备信息通过文件暴露。 |
平台设备 | 嵌入式系统中硬件设备的抽象,通常通过设备树或静态配置。 |
驱动注册和注销 | 使用 platform_driver_register 和 platform_device_register 注册驱动与设备。 |
解释设备树中的 reg
和 compatible
字段的作用。
在设备树(Device Tree)中,reg
和 compatible
是两个非常常见且重要的字段,它们用来描述设备的硬件资源和设备类型。它们的作用如下:
compatible
字段作用:
compatible
字段用于描述设备或硬件节点的硬件类型或设备驱动程序类型。具体作用:
compatible
字段来确定哪个驱动程序适配该设备。compatible
字段值与内核中驱动程序的 of_match_table
匹配,内核就会选择对应的驱动程序进行初始化。示例:
compatible = "vendor,device-model", "arm,platform";
在这个示例中,设备的硬件类型被定义为 vendor,device-model
,并且该设备与 arm,platform
兼容。内核会根据这些信息来选择合适的驱动程序。
reg
字段作用:
reg
字段用于定义设备的地址或端口等硬件资源。在设备树中,这个字段通常指定了设备的物理地址、I/O 端口、内存区间等资源位置。reg
字段指定了设备寄存器的地址范围或内存映射的区域。reg
通常与设备的硬件资源(如地址空间)一起使用,内核通过这些信息来确定如何访问和控制硬件设备。具体作用:
reg
字段指定了设备在内存中的地址,或者设备的 I/O 地址(如 PCI 总线地址、内存映射寄存器的物理地址)。示例:
reg = <0x10000000 0x1000>;
在这个示例中,reg
定义了设备的地址为 0x10000000
,大小为 0x1000
字节。
compatible
和 reg
结合使用设备树中的 compatible
和 reg
字段通常一起使用,来标识设备类型和资源位置。内核会根据 compatible
字段找到对应的驱动程序,然后根据 reg
字段提供的地址信息来配置硬件设备。
结合示例:
mydevice@10000000 {
compatible = "vendor,mydevice";
reg = <0x10000000 0x1000>;
};
mydevice
是一个设备节点。compatible = "vendor,mydevice"
指定了设备的类型,内核将使用这个字段来查找驱动程序。reg = <0x10000000 0x1000>
表示该设备的寄存器地址为 0x10000000
,并且设备的寄存器空间大小为 0x1000
字节。compatible
:标识设备的类型和兼容性,帮助内核加载合适的驱动程序。reg
:定义设备的硬件资源地址,如内存地址、I/O 地址等。这些字段帮助设备树描述硬件设备的具体配置,使内核能够识别、初始化和配置硬件设备。
sysfs
创建一个简单的属性文件,允许用户空间访问。为什么平台设备常用于嵌入式系统?
平台设备(Platform Devices)在嵌入式系统中常被使用,原因可以从多个方面进行分析,主要包括以下几个关键点:
platform_driver
与设备驱动进行匹配。通过这种方式,设备驱动和硬件设备之间的关系更加紧密,可以为每种平台硬件设备提供定制化的驱动程序,简化了驱动程序的管理和维护。平台设备的这些特点使得它非常适合嵌入式系统的应用。嵌入式系统通常有较为固定的硬件配置,并且对启动速度和系统效率有较高的要求,平台设备提供了一个简洁高效的方式来管理这些硬件设备。通过设备树和平台设备,嵌入式系统能够实现高效、可定制的硬件管理,从而满足各种嵌入式应用的需求。