• RK3568驱动指南|第六期-平台总线-第51章 注册platform设备实验


    瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


    【公众号】迅为电子

    【粉丝群】824412014(加群获取驱动文档+例程)

    【视频观看】嵌入式学习之Linux驱动(第六期_平台总线_全新升级)_基于RK3568

    【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


    第51章 注册platform设备实验

    51.1 注册platform设备

    51.1.1 platform_device_register 函数

    platform_device_register函数用于将platform_device结构体描述的平台设备注册到内核中。下面是对platform_device_register函数的详细介绍:

    函数原型:

    int platform_device_register(struct platform_device *pdev);

    头文件:

    #include

    函数作用:
    platform_device_register函数用于将platform_device结构体描述的平台设备注册到内核中,使其能够参与设备的资源分配和驱动的匹配。

    参数含义:

    pdev:指向platform_device结构体的指针,描述要注册的平台设备的信息。

    返回值:

    成功:返回0,表示设备注册成功。

    失败:返回负数,表示设备注册失败,返回的负数值表示错误代码。

    pdev参数是一个指向platform_device结构体的指针,其中包含了描述平台设备的各种属性和信息。platform_device结构体包含了设备名称、设备资源、设备ID等信息,用于描述和标识平台设备,会在接下来的小节对该结构体进行详细的介绍。

    该函数在内核源码目录下的“/include/linux/platform_device.h”文件中,具体内容如下所示:

    extern int platform_device_register(struct platform_device *);

    函数声明中的extern关键字表示该函数在其他地方定义,而不是在当前文件中实现。这样的声明通常出现在头文件中,用于告诉编译器该函数的定义存在于其他源文件中,以便在编译时能够正确引用该函数。

    而platform_device_register实际定义在“/drivers/base/platform.c”文件中,相关定义如下所示:

    1. int platform_device_register(struct platform_device *pdev)
    2. {
    3. device_initialize(&pdev->dev);
    4. arch_setup_pdev_archdata(pdev);
    5. return platform_device_add(pdev);
    6. }

    函数内部有三个主要的操作。

    第3行:调用了device_initialize函数,用于对pdev->dev进行初始化。pdev->dev是struct platform_device结构体中的一个成员,它表示平台设备对应的struct device结构体。通过调用device_initialize函数,对pdev->dev进行一些基本的初始化工作,例如设置设备的引用计数、设备的类型等。

    第4行:调用了arch_setup_pdev_archdata函数,用于根据平台设备的架构数据来设置pdev的架构相关数据。这个函数的具体实现可能与具体的架构相关,它主要用于在不同的架构下对平台设备进行特定的设置。

    第5行:调用了platform_device_add函数,将平台设备pdev添加到内核中。platform_device_add函数会完成平台设备的添加操作,包括将设备添加到设备层级结构中、添加设备的资源等。它会返回一个int类型的结果,表示设备添加的结果。

    platform_device_register函数的主要作用是将platform_device结构体描述的平台设备注册到内核中,包括设备的初始化、添加到platform总线和设备层级结构、添加设备资源等操作。通过该函数,平台设备被注册后,就能够参与设备的资源分配和驱动的匹配过程。函数的返回值可以用于判断设备注册是否成功。

    51.1.2 platform_device_unregister 函数

    platform_device_unregister函数用于取消注册已经注册的平台设备,即从内核中移除设备。在设备不再需要时,调用该函数可以进行设备的清理和释放操作。

    函数原型:

    void platform_device_unregister(struct platform_device *pdev);

    头文件:

    #include

    函数作用:
    platform_device_unregister函数用于取消注册已经注册的平台设备,从内核中移除设备。

    参数含义:

    pdev:指向要取消注册的平台设备的platform_device结构体指针。

    返回值:
    无返回值。

    该函数在内核源码目录下的“/include/linux/platform_device.h”文件中,具体内容如下所示:

    extern int platform_device_unregister(struct platform_device *);

    函数声明中的extern关键字表示该函数在其他地方定义,而不是在当前文件中实现。这样的声明通常出现在头文件中,用于告诉编译器该函数的定义存在于其他源文件中,以便在编译时能够正确引用该函数。

    而platform_device_unregister实际定义在“/drivers/base/platform.c”文件中,相关定义如下所示:

    1. void platform_device_unregister(struct platform_device *pdev)
    2. {
    3. platform_device_del(pdev);
    4. platform_device_put(pdev);
    5. }

    函数内部有两个主要的操作:

    第3行:调用了platform_device_del函数,用于将设备从platform总线的设备列表中移除。它会将设备从设备层级结构中移除,停止设备的资源分配和驱动的匹配。

    第4行:这一步调用了platform_device_put函数,用于减少对设备的引用计数。这个函数会检查设备的引用计数,如果引用计数减为零,则会释放设备结构体和相关资源。通过减少引用计数,可以确保设备在不再被使用时能够被释放。

    platform_device_unregister函数的作用是取消注册已经注册的平台设备,从内核中移除设备。它先调用platform_device_del函数将设备从设备层级结构中移除,然后调用platform_device_put函数减少设备的引用计数,确保设备在不再被使用时能够被释放。

    51.1.3 platform_device结构体

    platform_device结构体是用于描述平台设备的数据结构。它包含了平台设备的各种属性和信息,用于在内核中表示和管理平台设备。该结构体定义在内核的“/include/linux/platform_device.h”文件中,具体内容如下所示:

    1. struct platform_device {
    2. const char *name; // 设备的名称,用于唯一标识设备
    3. int id; // 设备的ID,可以用于区分同一种设备的不同实例
    4. bool id_auto; // 表示设备的ID是否自动生成
    5. struct device dev; // 表示平台设备对应的 struct device 结构体,用于设备的基本管理和操作
    6. u32 num_resources; // 设备资源的数量
    7. struct resource *resource; // 指向设备资源的指针
    8. const struct platform_device_id *id_entry; // 指向设备的ID表项的指针,用于匹配设备和驱动
    9. char *driver_override; // 强制设备与指定驱动匹配的驱动名称
    10. /* MFD cell pointer */
    11. struct mfd_cell *mfd_cell; // 指向多功能设备(MFD)单元的指针,用于多功能设备的描述
    12. /* arch specific additions */
    13. struct pdev_archdata archdata; // 用于存储特定于架构的设备数据
    14. };

    下面对于几个重要的参数和结构体进行讲解

    const char *name:设备的名称,用于唯一标识设备。必须提供一个唯一的名称,以便内核能够正确识别和管理该设备。

    int id:设备的ID,可以用于区分同一种设备的不同实例。这个参数是可选的,如果不需要使用ID进行区分,可以将其设置为-1,

    struct device dev:表示平台设备对应的struct device结构体,用于设备的基本管理和操作。必须为该参数提供一个有效的struct device对象,该结构体的release方法必须要实现,否则在编译的时候会报错。

    u32 num_resources:设备资源的数量。如果设备具有资源(如内存区域、中断等),则需要提供资源的数量。

    struct resource *resource:指向设备资源的指针。如果设备具有资源,需要提供一个指向资源数组的指针,会在下个小节对该结构体进行详细的讲解。

    51.1.4 resource结构体

    struct resource结构体用于描述系统中的设备资源,包括内存区域、I/O 端口、中断等,该结构体定义在内核的“/include/linux/ioport.h”文件中,具体内容如下所示:

    1. struct resource {
    2. resource_size_t start; /* 资源的起始地址 */
    3. resource_size_t end; /* 资源的结束地址 */
    4. const char *name; /* 资源的名称 */
    5. unsigned long flags; /* 资源的标志位 */
    6. unsigned long desc; /* 资源的描述信息 */
    7. struct resource *parent; /* 指向父资源的指针 */
    8. struct resource *sibling; /* 指向同级兄弟资源的指针 */
    9. struct resource *child; /* 指向子资源的指针 */
    10. /* 以下宏定义用于保留未使用的字段 */
    11. ANDROID_KABI_RESERVE(1);
    12. ANDROID_KABI_RESERVE(2);
    13. ANDROID_KABI_RESERVE(3);
    14. ANDROID_KABI_RESERVE(4);
    15. };

    其中最重要的是前四个参数,每个参数的具体介绍如下所示:

    (1)resource_size_t start:资源的起始地址。它表示资源的起始位置或者起始寄存器的地址。

    (2)resource_size_t end:资源的结束地址。它表示资源的结束位置或者结束寄存器的地址。

    (3)const char *name:资源的名称。它是一个字符串,用于标识和描述资源。

    (4)unsigned long flags:资源的标志位。它包含了一些特定的标志,用于表示资源的属性或者特征。例如,可以用标志位来指示资源的可用性、共享性、缓存属性等。flags参数的具体取值和含义可以根据系统和驱动的需求进行定义和解释,但通常情况下,它用于表示资源的属性、特征或配置选项。下面是一些常见的标志位及其可能的含义:

    1. 资源类型相关标志位:

    1. IORESOURCE_IO:表示资源是I/O端口资源。
    2. IORESOURCE_MEM:表示资源是内存资源。
    3. IORESOURCE_REG:表示资源是寄存器偏移量。
    4. IORESOURCE_IRQ:表示资源是中断资源。
    5. IORESOURCE_DMA:表示资源是DMA(直接内存访问)资源。

    2. 资源属性和特征相关标志位:

    1. IORESOURCE_PREFETCH:表示资源是无副作用的预取资源。
    2. IORESOURCE_READONLY:表示资源是只读的。
    3. IORESOURCE_CACHEABLE:表示资源支持缓存。
    4. IORESOURCE_RANGELENGTH:表示资源的范围长度。
    5. IORESOURCE_SHADOWABLE:表示资源可以被影子资源替代。
    6. IORESOURCE_SIZEALIGN:表示资源的大小表示对齐。
    7. IORESOURCE_STARTALIGN:表示起始字段是对齐的。
    8. IORESOURCE_MEM_64:表示资源是64位内存资源。
    9. IORESOURCE_WINDOW:表示资源由桥接器转发。
    10. IORESOURCE_MUXED:表示资源是软件复用的。
    11. IORESOURCE_SYSRAM:表示资源是系统RAM(修饰符)。

    3.其他状态和控制标志位

    1. IORESOURCE_EXCLUSIVE:表示用户空间无法映射此资源。
    2. IORESOURCE_DISABLED:表示资源当前被禁用。
    3. IORESOURCE_UNSET:表示尚未分配地址给资源。
    4. IORESOURCE_AUTO:表示地址由系统自动分配。
    5. IORESOURCE_BUSY:表示驱动程序将此资源标记为繁忙。

    51.2 实验程序的编写

    本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\40_platform_device\

    本实验将注册一个名为 "my_platform_device" 的平台设备,当注册平台设备时,该驱动程序提供了两个资源:一个内存资源和一个中断资源。这些资源被定义在名为 my_resources 的结构体数组中,具体内容如下:

    内存资源:

    起始地址:MEM_START_ADDR(0xFDD60000)

    结束地址:MEM_END_ADDR(0xFDD60004)

    标记:IORESOURCE_MEM

    中断资源:

    中断资源号:IRQ_NUMBER(101)

    标记:IORESOURCE_IRQ

    编写完成的platform_device.c代码如下所示:

    1. #include
    2. #include
    3. #include
    4. #define MEM_START_ADDR 0xFDD60000
    5. #define MEM_END_ADDR 0xFDD60004
    6. #define IRQ_NUMBER 101
    7. static struct resource my_resources[] = {
    8. {
    9. .start = MEM_START_ADDR, // 内存资源起始地址
    10. .end = MEM_END_ADDR, // 内存资源结束地址
    11. .flags = IORESOURCE_MEM, // 标记为内存资源
    12. },
    13. {
    14. .start = IRQ_NUMBER, // 中断资源号
    15. .end = IRQ_NUMBER, // 中断资源号
    16. .flags = IORESOURCE_IRQ, // 标记为中断资源
    17. },
    18. };
    19. static void my_platform_device_release(struct device *dev)
    20. {
    21. // 释放资源的回调函数
    22. }
    23. static struct platform_device my_platform_device = {
    24. .name = "my_platform_device", // 设备名称
    25. .id = -1, // 设备ID
    26. .num_resources = ARRAY_SIZE(my_resources), // 资源数量
    27. .resource = my_resources, // 资源数组
    28. .dev.release = my_platform_device_release, // 释放资源的回调函数
    29. };
    30. static int __init my_platform_device_init(void)
    31. {
    32. int ret;
    33. ret = platform_device_register(&my_platform_device); // 注册平台设备
    34. if (ret) {
    35. printk(KERN_ERR "Failed to register platform device\n");
    36. return ret;
    37. }
    38. printk(KERN_INFO "Platform device registered\n");
    39. return 0;
    40. }
    41. static void __exit my_platform_device_exit(void)
    42. {
    43. platform_device_unregister(&my_platform_device); // 注销平台设备
    44. printk(KERN_INFO "Platform device unregistered\n");
    45. }
    46. module_init(my_platform_device_init);
    47. module_exit(my_platform_device_exit);
    48. MODULE_LICENSE("GPL");
    49. MODULE_AUTHOR("topeet");

    51.3 运行测试

    51.3.1 编译驱动程序

    在上一小节中的platform_device.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下所示:

    1. export ARCH=arm64#设置平台架构
    2. export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
    3. obj-m += platform_device.o #此处要和你的驱动源文件同名
    4. KDIR :=/home/topeet/Linux/linux_sdk/kernel #这里是你的内核目录
    5. PWD ?= $(shell pwd)
    6. all:
    7. make -C $(KDIR) M=$(PWD) modules #make操作
    8. clean:
    9. make -C $(KDIR) M=$(PWD) clean #make clean操作

    对于Makefile的内容注释已在上图添加,保存退出之后,来到存放platform_device.c和Makefile文件目录下,如下图(图51-5)所示:

    图 51-5

    然后使用命令“make”进行驱动的编译,编译完成如下图(图51-2)所示:

    图 51-2

    编译完生成platform_device.ko目标文件,如下图(图51-3)所示:

    至此驱动模块就编译成功了。

    51.3.2 运行测试

    开发板启动之后,使用以下命令进行驱动模块的加载,如下图(图51-4)所示:

    insmod platform_device.ko

    图 51-4

    然后来到/sys/bus/platform/devices目录下,可以看到我们创建的my_platform_device设备文件夹就成功生成了。

    图 51-5

    然后使用以下命令进行驱动模块的卸载,如下图(图51-6)所示:

    rmmod platform_device.ko

     

    图 51-6

    至此,注册platform设备实验就完成了。


  • 相关阅读:
    C#(二) C#高级进阶
    IDEA使用http client无法识别http-client.env.json的环境配置
    简析CloudCompare文件夹之间的关系
    算法通关村第十九关——动态规划高频问题(白银)
    Bootstrap Blazor Table 组件(二)手动刷新组件数据
    JavaWeb开发之JSP&EL&JSTL
    数学建模与MatheMatica
    FSK解调技术的FPGA实现
    User Account Status 在CDB 和PDB不一致的情况 OPEN & IN ROLLOVER
    关于VScode中一些常用的快捷操作!
  • 原文地址:https://blog.csdn.net/BeiJingXunWei/article/details/133773402