【正点原子Linux连载】第二十一章 Linux MISC驱动实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

本文主要是介绍【正点原子Linux连载】第二十一章 Linux MISC驱动实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第二十一章 Linux MISC驱动实验

misc的意思是混合、杂项的,因此MISC驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用MISC驱动。MISC驱动其实就是最简单的字符设备驱动,通常嵌套在platform总线驱动中,实现复杂的驱动,本章我们就来学习一下MISC驱动的编写。

21.1 MISC设备驱动简介
所有的MISC设备驱动的主设备号都为10,不同的设备使用不同的从设备号。随着Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号,MISC设备驱动就用于解决此问题。MISC设备会自动创建cdev,不需要像我们以前那样手动创建,因此采用MISC设备驱动可以简化字符设备驱动的编写。我们需要向Linux注册一个miscdevice设备,miscdevice是一个结构体,定义在文件include/linux/miscdevice.h中,内容如下:
示例代码21.1.1 miscdevice结构体代码

1  struct miscdevice  {
2   	int minor;									/* 子设备号 		*/
3   	const char *name;							/* 设备名字 		*/
4   	const struct file_operations *fops; 	/* 设备操作集 	*/
5   	struct list_head list;
6   	struct device *parent;
7   	struct device *this_device;
8   	const struct attribute_group **groups;
9   	const char *nodename;
10  	umode_t mode;
11 };
定义一个MISC设备(miscdevice类型)以后我们需要设置minor、name和fops这三个成员变量。minor表示子设备号,MISC设备的主设备号为10,这个是固定的,需要用户指定子设备号,Linux系统已经预定义了一些MISC设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h文件中,如下所示:

示例代码21.1.2 预定义的MISC设备子设备号

1  #define PSMOUSE_MINOR        		1
2  #define MS_BUSMOUSE_MINOR    		2   /* unused */
3  #define ATIXL_BUSMOUSE_MINOR 	3   /* unused */
4  /*#define AMIGAMOUSE_MINOR   		4   FIXME OBSOLETE */
5  #define ATARIMOUSE_MINOR 			5   /* unused */
6  #define SUN_MOUSE_MINOR      		6   /* unused */
......
47 #define MISC_DYNAMIC_MINOR  		255
我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。

name就是此MISC设备名字,当此设备注册成功以后就会在/dev目录下生成一个名为name的设备文件。fops就是字符设备的操作集合,MISC设备驱动最终是需要使用用户提供的fops操作集合。
当设置好miscdevice以后就需要使用misc_register函数向系统中注册一个MISC设备,此函数原型如下:
int misc_register(struct miscdevice * misc)
函数参数和返回值含义如下:
misc:要注册的MISC设备。
返回值:负数,失败;0,成功。
以前我们需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中我们会使用如下几个函数完成设备创建过程:
示例代码21.1.3 传统的创建设备过程

1 alloc_chrdev_region();    	/* 申请设备号 	*/
2 cdev_init();          		/* 初始化cdev  */
3 cdev_add();               	/* 添加cdev  	*/
4 class_create();           	/* 创建类 		*/
5 device_create();      		/* 创建设备 		*/
现在我们可以直接使用misc_register一个函数来完成示例代码21.1.3中的这些步骤。当我们卸载设备驱动模块的时候需要调用misc_deregister函数来注销掉MISC设备,函数原型如下:

int misc_deregister(struct miscdevice *misc)
函数参数和返回值含义如下:
misc:要注销的MISC设备。
返回值:负数,失败;0,成功。
以前注销设备驱动的时候,我们需要调用一堆的函数去删除此前创建的cdev、设备等等内容,如下所示:
示例代码21.1.4 传统的删除设备的过程

1 cdev_del();						/*  删除cdev 	*/
2 unregister_chrdev_region(); 	/* 注销设备号 	*/
3 device_destroy();     			/* 删除设备 		*/
4 class_destroy();      			/* 删除类 		*/
现在我们只需要一个misc_deregister函数即可完成示例代码21.1.4中的这些工作。关于MISC设备驱动就讲解到这里,接下来我们就使用platform加MISC驱动框架来编写LED灯蜂鸣器驱动。

21.2 硬件原理图分析
本章实验我们只使用到正点原子的ATK-DLRK3568开发板上的LED,也就是绿色的LED灯。
21.3 实验程序编写
本实验对应的例程路径为:开发板光盘01、程序源码Linux驱动例程18_miscled。
本章实验我们采用platform加misc的方式编写led驱动,这也是实际的Linux驱动中很常用的方法。采用platform来实现总线、设备和驱动,misc主要负责完成字符设备的创建。
21.3.1 修改设备树
本章实验我们需要用到LED,因此需要在rk3568-evb.dtsi文件中LED设备节点。
1、关闭LED1默认功能
在上一章实验中,我们将ATK-DLRK3568开发板上的LED用Linux内核自带的LED驱动,并将其设置为“work”。此我们首先需要关闭上一章实验设置的这个LED功能,只需要将“work”这个节点中的status属性改为disabled即可,如图21.3.1.1所示:
在这里插入图片描述

图21.3.1.1 关闭LED1的默认功能
2、添加LED1的misc设备节点
在rk3568-atk-evb1-ddr4-v10.dtsi文件的“/”节点下中创建一个名为“miscled”的子节点,miscled子节点内容如下:
示例代码21.3.1.1 LED1杂项设备节点
1 miscled {
2 compatible = “alientek,miscled”;
3 miscled-gpio = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>;
4 status = “okay”;
5 };
设备树修改完成,重新编译内核并烧写。
21.3.2 misc驱动程序编写
新建名为“18_miscled”的文件夹,然后在18_miscled文件夹里面创建vscode工程,工作区命名为“miscled。新建名为miscled.c的驱动文件,在miscled.c中输入如下所示内容:
示例代码21.3.2.1 miscled.c文件代码段

1   #include <linux/types.h>
2   #include <linux/kernel.h>
3   #include <linux/delay.h>
4   #include <linux/ide.h>
5   #include <linux/init.h>
6   #include <linux/module.h>
7   #include <linux/errno.h>
8   #include <linux/gpio.h>
9   #include <linux/cdev.h>
10  #include <linux/device.h>
11  #include <linux/of.h>
12  #include <linux/of_address.h>
13  #include <linux/of_gpio.h>
14  #include <linux/platform_device.h>
15  #include <linux/miscdevice.h>
16  //#include <asm/mach/map.h>
17  #include <asm/uaccess.h>
18  #include <asm/io.h>
19  
20  #define MISCLED_NAME        "miscled"   	/* 名字   		*/
21  #define MISCLED_MINOR       144         	/* 子设备号 		*/
22  #define LEDOFF              	0           	/* 关LED 		*/
23  #define LEDON               	1           	/* 开LED 		*/
24  
25  /* miscled设备结构体 */
26  struct miscled_dev{
27      dev_t devid;            	/* 设备号     		*/
28      struct cdev cdev;       	/* cdev     		*/
29      struct class *class;    	/* 类      			*/
30      struct device *device;  	/* 设备    			*/
31      int led_gpio;           	/* led所使用的GPIO编号        */
32  };
33  
34  struct miscled_dev miscled;     /* led设备 */
35  
36  /*
37   * @description  	: beep相关初始化操作
38   * @param – pdev 	: platform_device指针,也就是platform设备指针
39   * @return        	: 成功返回0,失败返回负数
40   */
41  static int led_gpio_init(struct device_node *nd)
42  {
43      int ret;
44      
45      /* 从设备树中获取GPIO */
46      miscled.led_gpio = of_get_named_gpio(nd, "miscled-gpio", 0);
47      if(!gpio_is_valid(miscled.led_gpio)) {
48          printk("miscled:Failed to get led-gpio\n");
49          return -EINVAL;
50      }
51      
52      /* 申请使用GPIO */
53      ret = gpio_request(miscled.led_gpio, "led");
54      if(ret) {
55          printk("led: Failed to request miscled-gpio\n");
56          return ret;
57      }
58      
59      /* 将GPIO设置为输出模式并设置GPIO初始化电平状态 */
60      gpio_direction_output(miscled.led_gpio, 0);
61      
62      return 0;
63  }
64  
65  /*
66   * @description  	: 打开设备
67   * @param – inode	: 传递给驱动的inode
68   * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
69   *                    一般在open的时候将private_data指向设备结构体。
70   * @return        	: 0 成功;其他 失败
71   */
72  static int miscled_open(struct inode *inode, struct file *filp)
73  {
74      return 0;
75  }
76  
77  /*
78   * @description  	: 向设备写数据 
79   * @param - filp 	: 设备文件,表示打开的文件描述符
80   * @param - buf  	: 要写给设备写入的数据
81   * @param - cnt  	: 要写入的数据长度
82   * @param – offt	: 相对于文件首地址的偏移
83   * @return        	: 写入的字节数,如果为负值,表示写入失败
84   */
85  static ssize_t miscled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
86  {
87      int retvalue;
88      unsigned char databuf[1];
89      unsigned char ledstat;
90  
91      retvalue = copy_from_user(databuf, buf, cnt);
92      if(retvalue < 0) {
93          printk("kernel write failed!\r\n");
94          return -EFAULT;
95      }
96  
97      ledstat = databuf[0];       /* 获取状态值 */
98      if(ledstat == LEDON) {  
99          gpio_set_value(miscled.led_gpio, 1);    /* 打开LED */
100     } else if(ledstat == LEDOFF) {
101         gpio_set_value(miscled.led_gpio, 0);    /* 关闭LED */
102     }
103     return 0;
104 }
105 
106 /* 设备操作函数 */
107 static struct file_operations miscled_fops = {
108     .owner = THIS_MODULE,
109     .open = miscled_open,
110     .write = miscled_write,
111 };
112 
113 /* MISC设备结构体 */
114 static struct miscdevice led_miscdev = {
115     .minor = MISCLED_MINOR,
116     .name = MISCLED_NAME,
117     .fops = &miscled_fops,
118 };
119 
120  /*
121   * @description 	: flatform驱动的probe函数,当驱动与
122   *                    设备匹配以后此函数就会执行
123   * @param – dev	: platform设备
124   * @return      	: 0,成功;其他负值,失败
125   */
126 static int miscled_probe(struct platform_device *pdev)
127 {
128     int ret = 0;
129 
130     printk("led driver and device was matched!\r\n");
131 
132     /* 初始化BEEP */
133     ret = led_gpio_init(pdev->dev.of_node);
134     if(ret < 0)
135         return ret;
136         
137     /* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备
138      * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可
139      */
140     ret = misc_register(&led_miscdev);
141     if(ret < 0){
142         printk("misc device register failed!\r\n");
143         goto free_gpio;
144     }
145 
146     return 0;
147     
148 free_gpio:
149     gpio_free(miscled.led_gpio);
150     return -EINVAL;
151 }
152 
153 /*
154  * @description 	: platform驱动的remove函数
155  * @param - dev  	: platform设备
156  * @return       	: 0,成功;其他负值,失败
157  */
158 static int miscled_remove(struct platform_device *dev)
159 {
160     /* 注销设备的时候关闭LED灯 */
161     gpio_set_value(miscled.led_gpio, 1);
162     
163     /* 释放LED */
164     gpio_free(miscled.led_gpio);
165 
166     /* 注销misc设备 */
167     misc_deregister(&led_miscdev);
168     return 0;
169 }
170 
171  /* 匹配列表 */
172  static const struct of_device_id led_of_match[] = {
173      { .compatible = "alientek,miscled" },
174      { /* Sentinel */ }
175  };
176  
177  /* platform驱动结构体 */
178 static struct platform_driver led_driver = {
179      .driver     = {
180          .name   = " alientek,miscled",   	/* 驱动名字,用于和设备匹配 */
181          .of_match_table = led_of_match, 	/* 设备树匹配表          */
182      },
183      .probe      = miscled_probe,
184      .remove     = miscled_remove,
185 };
186 
187 /*
188  * @description 	: 驱动出口函数
189  * @param       	: 无
190  * @return      	: 无
191  */
192 static int __init miscled_init(void)
193 {
194     return platform_driver_register(&led_driver);
195 }
196 
197 /*
198  * @description 	: 驱动出口函数
199  * @param       	: 无
200  * @return      	: 无
201  */
202 static void __exit miscled_exit(void)
203 {
204     platform_driver_unregister(&led_driver);
205 }
206 
207 module_init(miscled_init);
208 module_exit(miscled_exit);
209 MODULE_LICENSE("GPL");
210 MODULE_AUTHOR("ALIENTEK");
211 MODULE_INFO(intree, "Y");

第72~111行,标准的字符设备驱动。
第114~118行,MISC设备led_miscdev,第115行设置子设备号为144,第116行设置设备名字为“miscled”,这样当系统启动以后就会在/dev/目录下存在一个名为“miscled”的设备文件。第117行,设置MISC设备的操作函数集合,为file_operations类型。
第126~151行,platform框架的probe函数,当驱动与设备匹配以后此函数就会执行,首先在此函数中初始化LED1所使用的IO。最后在140行通过misc_register函数向Linux内核注册MISC设备,也就是前面定义的led_miscdev。
第158~169行,platform框架的remove函数,在此函数中调用misc_deregister函数来注销MISC设备。
第192~205,标准的platform驱动。
21.3.3 编写测试APP
新建miscledApp.c文件,然后在里面输入如下所示内容:
示例代码21.3.2.2 miscledApp.c文件代码段

1  #include <stdio.h>
2  #include <unistd.h>
3  #include <sys/types.h>
4  #include <sys/stat.h>
5  #include <fcntl.h>
6  #include <stdlib.h>
7  #include <string.h>
8  
9  /*
10  * @description   	: main主程序
11  * @param - argc  	: argv数组元素个数
12  * @param - argv 	: 具体参数
13  * @return        	: 0 成功;其他 失败
14  */
15 int main(int argc, char *argv[])
16 {
17  int fd, retvalue;
18  char *filename;
19  unsigned char databuf[1];
20  
21  if(argc != 3){
22      printf("Error Usage!\r\n");
23      return -1;
24  }
25 
26  filename = argv[1];
27  fd = open(filename, O_RDWR);    /* 打开led驱动 */
28  if(fd < 0){
29      printf("file %s open failed!\r\n", argv[1]);
30      return -1;
31  }
32 
33  databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
34  retvalue = write(fd, databuf, sizeof(databuf));
35  if(retvalue < 0){
36      printf("BEEP Control Failed!\r\n");
37      close(fd);
38      return -1;
39  }
40 
41  retvalue = close(fd); /* 关闭文件 */
42  if(retvalue < 0){
43      printf("file %s close failed!\r\n", argv[1]);
44      return -1;
45  }
46  return 0;
47 }
miscledApp.c文件内容和其他例程的测试APP基本一致,很简单,这里就不讲解了。

21.4 运行测试
21.4.1 编译驱动程序和测试APP
1、编译驱动程序
编写Makefile文件,本章实验的Makefile文件和第五章实验基本一样,只是将obj-m变量的值改为“miscled.o”,Makefile内容如下所示:
示例代码21.4.1.1 Makefile文件

1  KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
...... 
4  obj-m := miscled.o
......
11 clean:
12  $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
第4行,设置obj-m变量的值为“miscled.o”。
输入如下命令编译出驱动模块文件:

make ARCH=arm64 //ARCH=arm64必须指定,否则编译会失败
编译成功以后就会生成一个名为“miscled.ko”的驱动模块文件。
2、编译测试APP
输入如下命令编译测试miscledApp.c这个测试程序:
/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc miscledApp.c -o miscledApp
编译成功以后就会生成miscledApp这个应用程序。
21.4.2 运行测试
在Ubuntu中将上一小节编译出来的miscled.ko和miscledApp通过adb命令发送到开发板的/lib/modules/4.19.232目录下,命令如下:
adb push miscled.ko miscledApp /lib/modules/4.19.232
发送成功以后进入到开发板目录lib/modules/4.19.232中,输入如下命令加载miscled.ko这个驱动模块:
depmod //第一次加载驱动的时候需要运行此命令
modprobe miscled //加载驱动模块
当驱动模块加载成功以后我们可以在/sys/class/misc这个目录下看到一个名为“miscled”的子目录,如图21.4.2.1所示:
在这里插入图片描述

图21.4.2.1 miscled子目录
所有的misc设备都属于同一个类,/sys/class/misc目录下就是misc这个类的所有设备,每个设备对应一个子目录。
驱动与设备匹配成功以后就会生成/dev/miscled这个设备驱动文件,输入如下命令查看这个文件的主次设备号:
ls /dev/miscled -l
结果如图21.4.2.2所示:
在这里插入图片描述

图21.4.2.2 /dev/miscled设备文件
从图21.4.2.2可以看出,/dev/miscled这个设备的主设备号为10,次设备号为144,和我们驱动程序里面设置的一致。
输入如下命令打开LED:
./miscledApp /dev/miscled 1 //打开LED
在输入如下命令关闭LED灯:
./miscledApp /dev/miscled 0 //关闭LED
观察一下LED能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的话输入如下命令即可:
rmmod miscled.ko

这篇关于【正点原子Linux连载】第二十一章 Linux MISC驱动实验摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/852253

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了