Linux MISC 驱动

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

57 struct miscdevice {
58 int minor; /* 子设备号 */
59 const char *name; /* 设备名字 */ 
60 const struct file_operations *fops; /* 设备操作集 */
61 struct list_head list;
62 struct device *parent;
63 struct device *this_device;
64 const struct attribute_group **groups;
65 const char *nodename;
66 umode_t mode;
67 };

定义一个 MISC 设备(miscdevice 类型)以后我们需要设置 minor、name 和 fops 这三个成员变量。minor 表示子设备号,MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号,Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h 文件中,如下所示:

13 #define PSMOUSE_MINOR 1
14 #define MS_BUSMOUSE_MINOR 2 /* unused */
15 #define ATIXL_BUSMOUSE_MINOR 3 /* unused */
16 /*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
17 #define ATARIMOUSE_MINOR 5 /* unused */
18 #define SUN_MOUSE_MINOR 6 /* unused */
......
52 #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,成功。

以前我们需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中我们会使用
如下几个函数完成设备创建过程:

1 cdev_del(); /* 删除 cdev */
2 unregister_chrdev_region(); /* 注销设备号 */
3 device_destroy(); /* 删除设备 */
4 class_destroy(); /* 删除类 */

现在我们只需要一个 misc_deregister 函数即可完成这些工作。关于MISC 设备驱动就讲解到这里,接下来我们就使用 platform 加 MISC 驱动框架来编写 beep 蜂鸣器驱动。
2 硬件原理图分析
硬件原理图参考自己的电路。
3 实验程序编写
采用 platform 加 misc 的方式编写 beep 驱动,这也是实际的 Linux 驱动中很常用的方法。采用 platform 来实现总线、设备和驱动,misc 主要负责完成字符设备的创建。
3.1 修改设备树
在根节点“/”下创建 BEEP 节点,节点名为“beep”,节点内容如下:
1 beep {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = “atkalpha-beep”; 5 pinctrl-names = “default”; 6 pinctrl-0 = <&pinctrl_beep>;
7 beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;
8 status = “okay”;
9 };
第 6 行,pinctrl-0 属性设置蜂鸣器所使用的 PIN 对应的 pinctrl 节点。
第 7 行,beep-gpio 属性指定了蜂鸣器所使用的 GPIO。
3.2 beep 驱动程序编写
新建名为“19_miscbeep”的文件夹,然后在 19_miscbeep 文件夹里面创建 vscode 工程,工作区命名为“miscbeep。新建名为 miscbeep.c 的驱动文件,在 miscbeep.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 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
21 文件名 : miscbeep.c
22 作者 :  
23 版本 : V1.0
24 描述 : 采用 MISC 的蜂鸣器驱动程序。
25 其他 : 无
26 论坛 :  
27 日志 : 初版 V1.0 2019/8/20  
28 ***************************************************************/
29 #define MISCBEEP_NAME "miscbeep" /* 名字 */
30 #define MISCBEEP_MINOR 144 /* 子设备号 */
31 #define BEEPOFF 0 /* 关蜂鸣器 */
32 #define BEEPON 1 /* 开蜂鸣器 */
33 
34 /* miscbeep 设备结构体 */
35 struct miscbeep_dev{
36 dev_t devid; /* 设备号 */
37 struct cdev cdev; /* cdev */
38 struct class *class; /* 类 */
39 struct device *device; /* 设备 */
40 struct device_node *nd; /* 设备节点 */
41 int beep_gpio; /* beep 所使用的 GPIO 编号 */
42 };
43
44 struct miscbeep_dev miscbeep; /* beep 设备 */
45 
46 /*
47 * @description : 打开设备
48 * @param – inode : 传递给驱动的 inode
49 * @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
50 * 一般在 open 的时候将 private_data 指向设备结构体。
51 * @return : 0 成功;其他 失败
52 */
53 static int miscbeep_open(struct inode *inode, struct file *filp)
54 {
55 filp->private_data = &miscbeep; /* 设置私有数据 */
56 return 0;
57 }
58 
59 /*
60 * @description : 向设备写数据
61 * @param - filp : 设备文件,表示打开的文件描述符
62 * @param - buf : 要写给设备写入的数据
63 * @param - cnt : 要写入的数据长度
64 * @param - offt : 相对于文件首地址的偏移
65 * @return : 写入的字节数,如果为负值,表示写入失败
66 */
67 static ssize_t miscbeep_write(struct file *filp,
const char __user *buf, size_t cnt, loff_t *offt)
68 {
69 int retvalue;
70 unsigned char databuf[1];
71 unsigned char beepstat;
72 struct miscbeep_dev *dev = filp->private_data;
73 
74 retvalue = copy_from_user(databuf, buf, cnt);
75 if(retvalue < 0) {
76 printk("kernel write failed!\r\n");
77 return -EFAULT;
78 }
79 
80 beepstat = databuf[0]; /* 获取状态值 */
81 if(beepstat == BEEPON) { 
82 gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */
83 } else if(beepstat == BEEPOFF) {
84 gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */
85 }
86 return 0;
87 }
88 
89 /* 设备操作函数 */
90 static struct file_operations miscbeep_fops = {
91 .owner = THIS_MODULE,
92 .open = miscbeep_open,
93 .write = miscbeep_write,
94 };
95 
96 /* MISC 设备结构体 */
97 static struct miscdevice beep_miscdev = {
98 .minor = MISCBEEP_MINOR,
99 .name = MISCBEEP_NAME,
100 .fops = &miscbeep_fops,
101 };
102
103 /*
104 * @description : flatform 驱动的 probe 函数,当驱动与
105 * 设备匹配以后此函数就会执行
106 * @param - dev : platform 设备
107 * @return : 0,成功;其他负值,失败
108 */
109 static int miscbeep_probe(struct platform_device *dev)
110 {
111 int ret = 0;
112
113 printk("beep driver and device was matched!\r\n");
114 /* 设置 BEEP 所使用的 GPIO */
115 /* 1、获取设备节点:beep */
116 miscbeep.nd = of_find_node_by_path("/beep");
117 if(miscbeep.nd == NULL) {
118 printk("beep node not find!\r\n");
119 return -EINVAL;
120 }
121
122 /* 2、 获取设备树中的 gpio 属性,得到 BEEP 所使用的 BEEP 编号 */
123 miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);
124 if(miscbeep.beep_gpio < 0) {
125 printk("can't get beep-gpio");
126 return -EINVAL;
127 }
128
129 /* 3、设置 GPIO5_IO01 为输出,并且输出高电平,默认关闭 BEEP */
130 ret = gpio_direction_output(miscbeep.beep_gpio, 1);
131 if(ret < 0) {
132 printk("can't set gpio!\r\n");
133 }
134 
135 /* 一般情况下会注册对应的字符设备,但是这里我们使用 MISC 设备
136 * 所以我们不需要自己注册字符设备驱动,只需要注册 misc 设备驱动即可
137 */
138 ret = misc_register(&beep_miscdev);
139 if(ret < 0){
140 printk("misc device register failed!\r\n");
141 return -EFAULT;
142 }
143
144 return 0;
145 }
146
147 /*
148 * @description : remove 函数,移除 platform 驱动的时候此函数会执行
149 * @param - dev : platform 设备
150 * @return : 0,成功;其他负值,失败
151 */
152 static int miscbeep_remove(struct platform_device *dev)
153 {
154 /* 注销设备的时候关闭 LED 灯 */
155 gpio_set_value(miscbeep.beep_gpio, 1);
156
157 /* 注销 misc 设备驱动 */
158 misc_deregister(&beep_miscdev);
159 return 0;
160 }
161
162 /* 匹配列表 */
163 static const struct of_device_id beep_of_match[] = {
164 { .compatible = "atkalpha-beep" },
165 { /* Sentinel */ }
166 };
167 
168 /* platform 驱动结构体 */
169 static struct platform_driver beep_driver = {
170 .driver = {
171 .name = "imx6ul-beep", /* 驱动名字 */
172 .of_match_table = beep_of_match, /* 设备树匹配表 */
173 },
174 .probe = miscbeep_probe,
175 .remove = miscbeep_remove,
176 };
177
178 /*
179 * @description : 驱动入口函数
180 * @param : 无
181 * @return : 无
182 */
183 static int __init miscbeep_init(void)
184 {
185 return platform_driver_register(&beep_driver);
186 }
187
188 /*
189 * @description : 驱动出口函数
190 * @param : 无
191 * @return : 无
192 */
193 static void __exit miscbeep_exit(void)
194 {
195 platform_driver_unregister(&beep_driver);
196 }
197
198 module_init(miscbeep_init);
199 module_exit(miscbeep_exit);
200 MODULE_LICENSE("GPL");
201 MODULE_AUTHOR("zuozhongkai");

第 29~94 行,标准的字符设备驱动。
第 97~101 行,MISC 设备 beep_miscdev,第 98 行设置子设备号为 144,第 99 行设置设备
名字为“miscbeep”,这样当系统启动以后就会在/dev/目录下存在一个名为“miscbeep”的设备
文件。第 100 行,设置 MISC 设备的操作函数集合,为 file_operations 类型。
第 109~145 行,platform 框架的 probe 函数,当驱动与设备匹配以后此函数就会执行,首先
在此函数中初始化 BEEP 所使用的 IO。最后在 138 行通过 misc_register 函数向 Linux 内核注册MISC 设备,也就是前面定义的 beep_miscdev。 第 152~160 行,platform 框架的 remove 函数,在此函数中调用 misc_deregister 函数来注销MISC 设备。
第 163~196,标准的 platform 驱动。
3.3 编写测试 APP
新建 miscbeepApp.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 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
10 文件名 : miscbeepApp.c
11 作者 :  
12 版本 : V1.0
13 描述 : MISC 驱动框架下的 beep 测试 APP。
14 其他 : 无
15 使用方法 :./miscbeepApp /dev/miscbeep 0 关闭蜂鸣器
16 ./misdcbeepApp /dev/miscbeep 1 打开蜂鸣器
17 论坛 : 
18 日志 : 初版 V1.0 2019/8/20  
19 ***************************************************************/
20 #define BEEPOFF 0
21 #define BEEPON 1
22
23 /*
24 * @description : main 主程序
25 * @param - argc : argv 数组元素个数
26 * @param - argv : 具体参数
27 * @return : 0 成功;其他 失败
28 */
29 int main(int argc, char *argv[])
30 {
31 int fd, retvalue;
32 char *filename;
33 unsigned char databuf[1];
34 
35 if(argc != 3){
36 printf("Error Usage!\r\n");
37 return -1;
38 }
39
40 filename = argv[1];
41 fd = open(filename, O_RDWR); /* 打开 beep 驱动 */
42 if(fd < 0){
43 printf("file %s open failed!\r\n", argv[1]);
44 return -1;
45 }
46
47 databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
48 retvalue = write(fd, databuf, sizeof(databuf));
49 if(retvalue < 0){
50 printf("BEEP Control Failed!\r\n");
51 close(fd);
52 return -1;
53 }
54
55 retvalue = close(fd); /* 关闭文件 */
56 if(retvalue < 0){
57 printf("file %s close failed!\r\n", argv[1]);
58 return -1;
59 }
60 return 0;
61 }

1、编译驱动程序
输入如下命令编译出驱动模块文件:

make -j32

编译成功以后就会生成一个名为“miscbeep.ko”的驱动模块文件
2、编译测试 APP
输入如下命令编译测试 miscbeepApp.c 这个测试程序:

arm-linux-gnueabihf-gcc miscbeepApp.c -o miscbeepApp

编译成功以后就会生成 miscbeepApp 这个应用程序。
4.2 运行测试
加载 miscbeep.ko 这个驱动模块。

depmod //第一次加载驱动的时候需要运行此命令
modprobe miscbeep.ko //加载设备模块

当驱动模块加载成功以后我们可以在/sys/class/misc 这个目录下看到一个名为“miscbeep”
的子目录,如图所示:
在这里插入图片描述
所有的 misc 设备都属于同一个类,/sys/class/misc 目录下就是 misc 这个类的所有设备,每
个设备对应一个子目录。
驱动与设备匹配成功以后就会生成/dev/miscbeep 这个设备驱动文件,输入如下命令查看这
个文件的主次设备号:

ls /dev/miscbeep -l

结果如图所示
在这里插入图片描述
从图可以看出,/dev/miscbeep 这个设备的主设备号为 10,次设备号为 144,和我
们驱动程序里面设置的一致。
输入如下命令打开 BEEP:

输入如下命令打开 BEEP:

./miscbeepApp /dev/miscbeep 1 //打开 BEEP

在输入如下命令关闭 LED 灯:

./miscbeepApp /dev/miscbeep 0 //关闭 BEEP

观察一下 BEEP 能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的
话输入如下命令即可:

rmmod miscbeep.ko
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那肯定是很多年以后!

你的鼓励就我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值