【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十五章 注册字符类装备 [复制链接]
发表于 2026-4-24 09:37:10 | 显示全部楼层 |阅读模式
i.MX8MM处置惩罚器接纳了先辈的14LPCFinFET工艺,提供更快的速率和更高的电源服从;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口包罗万象。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相干进程,支持8路PDM接口、5路SAI接口、2路Speaker。体系支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9体系。实用于智能充电桩,物联网物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网物联网应用、

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

四十五 注册字符类装备

本章导读
在整个Linux装备驱动的学习中,字符装备驱动较为底子。本章将教学注册字符类装备的编程方法。
45.1章节教学了注册字符装备的根本步调
45.2章节编写注册字符装备的驱动步伐
45.3章节编写应用层测试步伐
45.4章节编译驱动步伐为驱动模块
本章内容对应视频教学链接(在线观看):
注册字符类装备 → https://www.bilibili.com/video/BV1Vy4y1B7ta?p=16
步伐源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动步伐例程\07-注册字符类装备”路径下。
45.1 注册字符类装备简介

在Linux内核中,使用cdev结构体形貌一个字符装备,cdev结构体的界说如下:
  1. struct cdev {   //描述字符设备的一个结构体
  2.     struct kobject kobj;
  3.     struct module *owner;
  4.     const struct file_operations *ops;
  5.     struct list_head list;
  6.     dev_t dev;
  7.     unsigned int count;
  8. };
复制代码
cdev结构体的dev_t成员界说了装备号,为32位,此中12位为主装备号,20位为次装备号。使用下列宏可以从dev_t得到主装备号和次装备号:
   MAJOR(dev_t dev)
  MINOR(dev_t dev)
  而使用下列宏则可以通过主装备号和次装备号天生dev_t
   MKDEV(int major, int minor)
  cdev结构体的另一个告急成员file_operations界说了字符装备驱动提供给捏造文件体系的接口函数。
Linux内核提供了一组函数以用于操纵cdev结构体:
  1. void cdev_init(struct cdev *, struct file_operations *);
  2. struct cdev *cdev_alloc(void);
  3. void cdev_put(struct cdev *p);
  4. int cdev_add(struct cdev *, dev_t, unsigned);
  5. void cdev_del(struct cdev *);
复制代码
cdev_add()函数和cdev_del()函数分别向体系添加和删除一个cdev,完成字符装备的注册和注销。对cdev_add()的调用通常发生在字符装备驱动模块加载函数中,而对cdev_del()函数的调用则通常发生在字符装备驱动模块卸载函数中。
函数
void cdev_init(struct cdev *, const struct file_operations *);
第一个参数
要初始化的cdev
第二个参数
文件操纵集 cdev->ops = fops; //实际就是把文件操纵集写给ops
功能
cdev_init()函数用于初始化cdev的成员,并创建cdev和file_operations之间的毗连。
函数
void cdev_init(struct cdev *, const struct file_operations *);
第一个参数
要初始化的cdev
第二个参数
文件操纵集 cdev->ops = fops; //实际就是把文件操纵集写给ops
功能
cdev_init()函数用于初始化cdev的成员,并创建cdev和file_operations之间的毗连。
函数
void cdev_del(struct cdev *);
第一个参数
cdev的结构体指针
天生装备节点
字符装备注册完以后不会主动天生装备节点。我们必要使用mknod下令创建一个装备节点
格式:mknod 名称 范例 主装备号 次装备号
举例:
   mknod /dev/test c 247 0
  45.2 编写字符装备驱动

通过45.1章节注册字符类装备的学习,我们已经把根本概念搞懂了。我们在ubuntu的/home/topeet/imx8mm/07目次下新建chrdev.c文件。我们可以在前次实行编写的chrdev.c底子上举行编辑。

完备代码如下所示:
  1. /*
  2.  * @Descripttion: 注册字符设备
  3.  */
  4. #include <linux/init.h>   //初始化头文件
  5. #include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
  6. #include <linux/fs.h>     //包含了文件操作相关struct的定义,例如大名鼎鼎的struct file_operations
  7. #include <linux/kdev_t.h>
  8. #include <linux/cdev.h>        // 对字符设备结构cdev以及一系列的操作函数的定义。包含了cdev 结构及相关函数的定义。
  9. #define DEVICE_NUMBER 1        //定义次设备号的个数
  10. #define DEVICE_SNAME "schrdev" //定义静态注册设备的名称
  11. #define DEVICE_ANAME "achrdev" //定义动态注册设备的名称
  12. #define DEVICE_MINOR_NUMBER 0  //定义次设备号的起始地址
  13. static int major_num, minor_num; //定义主设备号和次设备号
  14. struct cdev cdev;                      //定义一个cdev结构体
  15. module_param(major_num, int, S_IRUSR); //驱动模块传入普通参数major_num
  16. module_param(minor_num, int, S_IRUSR); //驱动模块传入普通参数minor_num
  17. int chrdev_open(struct inode *inode, struct file *file)
  18. {
  19.     printk("chrdev_open\n");
  20.     return 0;
  21. }
  22. // file_operations chrdev_ops
  23. struct file_operations chrdev_ops = {
  24.     .owner = THIS_MODULE,
  25.     .open = chrdev_open};
  26. static int hello_init(void)
  27. {
  28.     dev_t dev_num;
  29.     int ret; //函数返回值
  30.     if (major_num)
  31.     {
  32.         /*静态注册设备号*/
  33.         printk("major_num = %d\n", major_num); //打印传入进来的主设备号
  34.         printk("minor_num = %d\n", minor_num); //打印传入进来的次设备号
  35.         dev_num = MKDEV(major_num, minor_num);                              //MKDEV将主设备号和次设备号合并为一个设备号
  36.         ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME); //注册设备号
  37.         if (ret < 0)
  38.         {
  39.             printk("register_chrdev_region error\n");
  40.         }
  41.         printk("register_chrdev_region ok\n"); //静态注册设备号成功
  42.     }
  43.     else
  44.     {
  45.         /*动态注册设备号*/
  46.         ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, 1, DEVICE_ANAME);
  47.         if (ret < 0)
  48.         {
  49.             printk("alloc_chrdev_region error\n");
  50.         }
  51.         printk("alloc_chrdev_region ok\n"); //动态注册设备号成功
  52.         major_num = MAJOR(dev_num);            //将主设备号取出来
  53.         minor_num = MINOR(dev_num);            //将次设备号取出来
  54.         printk("major_num = %d\n", major_num); //打印传入进来的主设备号
  55.         printk("minor_num = %d\n", minor_num); //打印传入进来的次设备号
  56.     }
  57.     cdev.owner = THIS_MODULE;
  58.     //cdev_init函数初始化cdev结构体成员变量
  59.     cdev_init(&cdev, &chrdev_ops);
  60.     //完成字符设备注册到内核
  61.     cdev_add(&cdev, dev_num, DEVICE_NUMBER);
  62.     return 0;
  63. }
  64. static void hello_exit(void)
  65. {
  66.     unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER); //注销设备号
  67.     cdev_del(&cdev);
  68.     printk("gooodbye! \n");
  69. }
  70. module_init(hello_init);
  71. module_exit(hello_exit);
  72. MODULE_LICENSE("GPL");
复制代码
45.3 编写应用步伐

编写应用步伐如下所示:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. int main(int argc,char *argv[])
  7. {
  8.     int fd;
  9.     char buf[64] = {0};
  10.     fd = open("/dev/test",O_RDWR);  //打开设备节点
  11.     if(fd < 0)
  12.     {
  13.         perror("open error \n");
  14.         return fd;
  15.     }
  16.     //read(fd,buf,sizeof(buf)); //从文件中读取数据放入缓冲区中
  17.     close(fd);
  18.     return 0;
  19. }
复制代码
我们将app.c文件拷贝到Ubuntu的/home/topeet/imx8mm/07目次下,输入以下下令编译app.c。天生的app。

45.4 开发板实行

这里我们以iTOP-iMX8MM开发板为例,将刚刚45.2章节编写的驱动代码编译成模块。将前次编译chrdev.c的Makefile文件和build.sh文件拷贝到chrdev.c同级目次下,修改Makefile为:
  1. obj-m += chrdev.o
  2. KDIR:=/home/topeet/linux/linux-imx
  3. PWD?=$(shell pwd)
  4. all:
  5.         make -C $(KDIR) M=$(PWD) modules ARCH=arm64
  6. clean:
  7.         make -C $(KDIR) M=$(PWD) clean
复制代码
文件如下图所示:

驱动编译乐成如下图所示: 

我们通过nfs将编译好的驱动步伐加载模块,进入共享目次,加载驱动模块如下图所示:
   insmod chrdev.ko
  
 

从上图可知,动态申请好了字符装备号,主装备号是511,次装备号是0。但是我们想要验证我们的字符装备是否注册乐成,必要运行应用步伐。我们输入“mknod /dev/test c 511 0”创建立备节点,然后再运行45.3章节编译好的APP应用步伐,如下图所示: 

如上图所示,应用步伐APP乐成地打开了装备节点,阐明字符装备注册乐成。 

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表