RK3568驱动指南|第二篇 字符设备基础-第11章 创建设备节点实验

瑞芯微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主板


第11章 创建设备节点实验

在上两个章节的学习中,我们已经成功的申请了设备号并且注册了相应的字符设备。系统通过设备号对设备进行查找,而字符设备注册到内核之后,并不能直接进行设备文件操作命令(打开、关闭、读、写等),需要相应的设备文件作为桥梁以此来进行设备的访问,在本章节将对如何创建设备节点进行学习。

11.1 创建设备节点

在Linux操作系统中一切皆文件,设备访问也是通过文件的方式来进行的,对于用来进行设备访问的文件称之为设备节点,设备节点被创建在/dev目录下,将内核中注册的设备与用户层进行链接,这样应用程序才能对设备进行访问。

根据设备节点的创建方式不同,分为了手动创建设备节点和自动创建设备节点,下面对两种设备节点创建方式进行介绍。

11.1.1 手动创建设备节点

使用mknod命令手动创建设备节点,mknod 命令格式为:

mknod NAME TYPE MAJOR MINOR

参数含义:

  • NAME: 要创建的节点名称
  • TYPE: b表示块设备,c表示字符设备,p表示管道
  • MAJOR:要链接设备的主设备号
  • MINOR: 要链接设备的从设备号

例如使用以下命令创建一个名为device_test的字符设备节点,链接设备的主设备号和从设备号分别为236和0:

mknod /dev/device_test c 236 0

11.1.2 自动创建设备节点

设备文件的自动创建是利用udev(mdev)机制来实现,多数情况下采用自动创建设备节点的方式。udev(mdev)可以检测系统中硬件设备状态,可以根据系统中硬件设备状态来创建或者删除设备文件。在驱动中首先使用class_create(…)函数对class进行创建,这个类存放于/sys/class/ 目录下,之后使用device_create(…)函数创建相应的设备,在进行模块加载时,用户空间中的udev会自动响应device_create()函数,寻找对应的类从而创建设备节点。

下面对于自动创建节点中所用到的函数进行解释说明:

class_create(…)函数

该函数在“内核源码/include/linux/device.h”文件中所引用(由于上一小节中引用的cdev.h文件已包含device.h,所以不需要再重复引用),如下(图11-1)所示:

 #define class_create(owner, name)       \
 ({                      \
    static struct lock_class_key __key; \
     __class_create(owner, name, &__key);    \                                                                           
 })

函数作用:

​ 用于动态创建设备的逻辑类,并完成部分字段的初始化,然后将其添加进Linux内核系统。

参数含义:

​ owner:struct module结构体类型的指针,指向函数即将创建的这个struct
class的模块。一般赋值为THIS_MODULE。

​ name:char类型的指针,代表即将创建的struct class变量的名字。

返回值:struct class * 类型的结构体。

class_destroy(…)函数

该函数在“内核源码/include/linux/device.h”文件中所引用,如下(图11-2)所示:

extern void class_destroy(struct class *cls);  

图 11-2

函数作用:

​ 用于删除设备的逻辑类,即从Linux内核系统中删除设备的逻辑类。

参数含义:

​ owner:struct module结构体类型的指针,指向函数即将创建的这个struct
class的模块。一般赋值为THIS_MODULE。

​ name:char类型的指针,代表即将创建的struct class变量的名字。

返回值:无

device_create(…)函数

该函数在“内核源码/include/linux/device.h”文件中所引用,如下(图11-3)所示:

 struct device *device_create(struct class *cls, struct device *parent,                                                  
                dev_t devt, void *drvdata,
                const char *fmt, ...);

函数作用:

​ 用来在class类中下创建一个设备属性文件,udev会自动识别从而进行设备节点的创建。

参数含义:

​ cls:指定所要创建的设备所从属的类。

​ parent:指定该设备的父设备,如果没有就指定为NULL。

​ devt:指定创建设备的设备号。

​ drvdata:被添加到该设备回调的数据,没有则指定为NULL。

​ fmt:添加到系统的设备节点名称。

返回值:struct device * 类型结构体

device_destroy(…)函数

在“内核源码/include/linux/device.h”文件中所引用,如下(图11-4)所示:

 extern void device_destroy(struct class *cls, dev_t devt); 

函数作用:

​ 用来删除class类中的设备属性文件,udev会自动识别从而进行设备节点的删除。

参数含义:

​ cls:指定所要创建的设备所从属的类。

​ devt:指定创建设备的设备号。

返回值:无

至此,关于自动创建节点相关的函数就介绍完成了,会在下一小节中对于设备节点的自动创建进行相应实验程序的编写。

11.2 实验程序的编写

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

本章实验将编写Linux下的自动创建设备节点实验代码,首先采用自动申请设备号的方式进行设备号的申请,并对获取的主设备号与次设备号进行打印,之后对字符设备进行注册(file_operations结构体只填充owner 字段即可,会在下个章节对file_operations结构体进行讲解),最后自动对设备节点进行创建。

编写完成的chrdev_node.c代码如下(图11-5)所示

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>

static dev_t dev_num;//定义dev_t类型变量dev_num来表示设备号
struct cdev cdev_test;//定义struct cdev 类型结构体变量cdev_test,表示要注册的字符设备
struct file_operations cdev_fops_test = {
    .owner = THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
};//定义file_operations结构体类型的变量cdev_test_ops
struct class *class_test;//定于struct class *类型结构体变量class_test,表示要创建的类

static int __init chrdev_fops_init(void)//驱动入口函数
{
    int ret;//定义int类型的变量ret,用来对函数返回值进行判断
    int major,minor;//定义int类型的主设备号major和次设备号minor
    ret = alloc_chrdev_region(&dev_num,0,1,"chrdev_name");//自动获取设备号,设备名chrdev_name
    if (ret  < 0){
        printk("alloc_chrdev_region is error \n");
    }
    printk("alloc_chrdev_region is ok \n");
    major = MAJOR(dev_num);//使用MAJOR()函数获取主设备号
    minor = MINOR(dev_num);//使用MINOR()函数获取次设备号
    printk("major is %d\n minor is %d \n",major,minor);
    cdev_init(&cdev_test,&cdev_fops_test);//使用cdev_init()函数初始化cdev_test结构体,并链接到cdev_test_ops结构>体
    cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
    ret = cdev_add(&cdev_test,dev_num,1); //使用cdev_add()函数进行字符设备的添加
    if (ret < 0){
         printk("cdev_add is error \n");
    }
    printk("cdev_add is ok \n");
    class_test  = class_create(THIS_MODULE,"class_test");//使用class_create进行类的创建,类名称为class_test
    device_create(class_test,NULL,dev_num,NULL,"device_test");//使用device_create进行设备的创建,设备名称为device_test
    return 0;
}

static void __exit chrdev_fops_exit(void)//驱动出口函数
{
    cdev_del(&cdev_test);//删除添加的字符设备cdev_test
    unregister_chrdev_region(dev_num,1);//释放字符设备所申请的设备号
    device_destroy(class_test,dev_num);//删除创建的设备
    class_destroy(class_test);//删除创建的类
    printk("module exit \n");
}

module_init(chrdev_fops_init);//注册入口函数
module_exit(chrdev_fops_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("topeet");//作者信息

相较于上一章节实验,本章节代码在入口函数中添加了自动创建设备节点相关代码,在驱动出口函数中添加了相应的删除设备节点相关代码(相关代码已加粗)。

需要注意的是,在进行设备节点添加时,类的创建要放在设备创建之前;在进行设备节点删除时,类的删除要放在设备删除之后。

11.3 运行测试

11.3.1 编译驱动程序

在上一小节中的chrdev_node.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下(图11-6)所示:

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

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

img

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

img

编译完生成 chrdev_node.ko目标文件,如下图(图11-9)所示:

img

至此我们的驱动模块就编译成功了,下面进行驱动的运行测试。

11.3.2 运行测试

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

insmod cdev.ko

img

可以看到动态申请设备号成功了,主设备号为236,次设备号为0,然后使用以下命令进行注册设备号的查看,如下图(图11-11)所示:

 cat /proc/devices

img

可以看到主设备号236的设备名为chrdev_name,和驱动程序中设置的相同,证明我们的设备号注册成功了,然后使用以下命令对class目录进行查看,如下图(图11-12)所示:

ls /sys/class/

img

可以看到在驱动程序中创建的class_test 类已经被成功创建了,然后使用以下命令对class_test 目录进行查看,如下图(图11-12)所示:

ls /sys/class/class_test/

img

可以看到在驱动程序中创建的名为device_test的设备属性文件夹也被创建了,然后使用命令“ls /dev/device_test”对/dev目录进行查看,相应的设备节点也已经被自动创建了,如下图(图11-13)所示:

img

最后可以使用以下命令进行驱动的卸载,卸载完成如下图(图11-14)所示:

rmmod chrdev_node.ko

img
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值