引言 在 Linux 内核的设备驱动模型中,总线(Bus)扮演着至关重要的角色。它是连接 CPU 和各种设备的桥梁,负责数据传输和设备管理。Platform 总线作为 Linux 内核中的一种特殊总线,主要用于管理那些直接连接到 CPU 上的设备,也就是所谓的“平台设备”。本文将深入探讨 Platform 总线架构的原理、工作机制以及如何编写基于 Platform 总线的设备驱动程序。
Platform 总线是 Linux 内核为那些没有明确总线连接的设备而设计的一种虚拟总线。这些设备通常直接集成在主板上,如嵌入式系统中的 GPIO 控制器、定时器、看门狗等。由于它们没有像 PCI、USB 那样的物理总线,因此需要一种特殊的机制来管理和驱动它们,Platform 总线应运而生。
在 Linux 内核中,设备驱动模型遵循分离原则,即设备(Device)和驱动(Driver)是分离的。设备描述了硬件的属性和资源,而驱动则负责实现对硬件的操作。Platform 总线提供了一个统一的框架,使得设备和驱动可以独立开发和注册,提高了代码的可维护性和可移植性。
设备和驱动的注册 在 Platform 总线架构中,设备和驱动需要分别向内核注册。设备注册时会提供设备的资源信息,如内存地址、中断号等;驱动注册时会提供对设备的操作方法,如初始化、读写等。
以下是一个简单的 Platform 设备注册示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> static struct resource my_device_resources [] = { [0 ] = { .start = 0x1000 , .end = 0x10ff , .flags = IORESOURCE_MEM, }, [1 ] = { .start = 10 , .end = 10 , .flags = IORESOURCE_IRQ, }, }; static struct platform_device my_platform_device = { .name = "my_device" , .id = -1 , .num_resources = ARRAY_SIZE(my_device_resources), .resource = my_device_resources, }; static int __init my_device_init (void ) { return platform_device_register(&my_platform_device); } static void __exit my_device_exit (void ) { platform_device_unregister(&my_platform_device); } module_init(my_device_init); module_exit(my_device_exit); MODULE_LICENSE("GPL" );
上述代码定义了一个简单的 Platform 设备,并在模块初始化时向内核注册该设备。设备资源包括一段内存地址和一个中断号。
以下是一个简单的 Platform 驱动注册示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> static int my_driver_probe (struct platform_device *pdev) { printk(KERN_INFO "My driver probed!\n" ); return 0 ; } static int my_driver_remove (struct platform_device *pdev) { printk(KERN_INFO "My driver removed!\n" ); return 0 ; } static struct platform_driver my_platform_driver = { .probe = my_driver_probe, .remove = my_driver_remove, .driver = { .name = "my_device" , .owner = THIS_MODULE, }, }; static int __init my_driver_init (void ) { return platform_driver_register(&my_platform_driver); } static void __exit my_driver_exit (void ) { platform_driver_unregister(&my_platform_driver); } module_init(my_driver_init); module_exit(my_driver_exit); MODULE_LICENSE("GPL" );
上述代码定义了一个简单的 Platform 驱动,并在模块初始化时向内核注册该驱动。驱动提供了 probe 和 remove 两个函数,分别在设备和驱动匹配以及设备移除时调用。
设备和驱动的匹配 当设备和驱动都注册到 Platform 总线后,内核会根据设备和驱动的 name 属性进行匹配。如果两者的 name 相同,则认为设备和驱动匹配成功,内核会调用驱动的 probe 函数进行设备的初始化。
设备资源的管理 Platform 设备注册时会提供设备的资源信息,驱动可以通过 platform_get_resource 函数来获取这些资源。例如,获取内存资源的代码如下:
1 2 3 4 5 struct resource *res ;res = platform_get_resource(pdev, IORESOURCE_MEM, 0 ); if (res) { }
上述代码通过 platform_get_resource 函数获取设备的第一个内存资源。
步骤概述 编写基于 Platform 总线的设备驱动程序通常需要以下步骤:
定义 Platform 驱动结构体,包含 probe、remove 等函数。
实现 probe 函数,在该函数中进行设备的初始化和资源分配。
实现 remove 函数,在该函数中进行设备的清理和资源释放。
注册和注销 Platform 驱动。
示例代码 以下是一个完整的基于 Platform 总线的设备驱动程序示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> static int my_driver_probe (struct platform_device *pdev) { struct resource *res ; void __iomem *base_addr; res = platform_get_resource(pdev, IORESOURCE_MEM, 0 ); if (!res) { dev_err(&pdev->dev, "Failed to get memory resource\n" ); return -ENODEV; } base_addr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base_addr)) { dev_err(&pdev->dev, "Failed to map memory resource\n" ); return PTR_ERR(base_addr); } printk(KERN_INFO "My driver probed, memory mapped to %p\n" , base_addr); return 0 ; } static int my_driver_remove (struct platform_device *pdev) { printk(KERN_INFO "My driver removed\n" ); return 0 ; } static struct platform_driver my_platform_driver = { .probe = my_driver_probe, .remove = my_driver_remove, .driver = { .name = "my_device" , .owner = THIS_MODULE, }, }; static int __init my_driver_init (void ) { return platform_driver_register(&my_platform_driver); } static void __exit my_driver_exit (void ) { platform_driver_unregister(&my_platform_driver); } module_init(my_driver_init); module_exit(my_driver_exit); MODULE_LICENSE("GPL" );
上述代码实现了一个简单的基于 Platform 总线的设备驱动程序。在 probe 函数中,驱动获取设备的内存资源并进行映射;在 remove 函数中,驱动进行简单的清理工作。
总结 Platform 总线架构为 Linux 内核中没有明确总线连接的设备提供了一种有效的管理和驱动机制。通过设备和驱动的分离,提高了代码的可维护性和可移植性。本文详细介绍了 Platform 总线的原理、工作机制以及如何编写基于 Platform 总线的设备驱动程序。希望通过本文的介绍,读者能够对 Platform 总线架构有更深入的理解。