1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html 4)正点原子官方B站:https://space.bilibili.com/394620890 5)正点原子STM32MP157技术交流群:691905614
misc的意思是混合、杂项的,因此MISC驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用MISC驱动。MISC驱动其实就是最简单的字符设备驱动,通常嵌套在platform总线驱动中,实现复杂的驱动,本章我们就来学习一下MISC驱动的编写。
37.1 MISC设备驱动简介 所有的MISC设备驱动的主设备号都为10,不同的设备使用不同的从设备号。随着Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号,MISC设备驱动就用于解决此问题。MISC设备会自动创建cdev,不需要像我们以前那样手动创建,因此采用MISC设备驱动可以简化字符设备驱动的编写。我们需要向Linux注册一个miscdevice设备,miscdevice是一个结构体,定义在文件include/linux/miscdevice.h中,内容如下:
示例代码37.1.1 miscdevice结构体代码
66 struct miscdevice {
67 int minor; /* 子设备号 */
68 const char *name; /* 设备名字 */
69 const struct file_operations *fops; /* 设备操作集 */
70 struct list_head list;
71 struct device *parent;
72 struct device *this_device;
73 const struct attribute_group **groups;
74 const char *nodename;
75 umode_t mode;
76 };
定义一个MISC设备(miscdevice类型)以后我们需要设置minor、name和fops这三个成员变量。minor表示子设备号,MISC设备的主设备号为10,这个是固定的,需要用户指定子设备号,Linux系统已经预定义了一些MISC设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h文件中,如下所示:
示例代码37.1.2 预定义的MISC设备子设备号
15 #define PSMOUSE_MINOR 1
16 #define MS_BUSMOUSE_MINOR 2 /* unused */
17 #define ATIXL_BUSMOUSE_MINOR 3 /* unused */
18 /*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
19 #define ATARIMOUSE_MINOR 5 /* unused */
20 #define SUN_MOUSE_MINOR 6 /* unused */
......
61 #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,成功。 以前我们需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中我们会使用如下几个函数完成设备创建过程:
示例代码37.1.3 传统的创建设备过程
1 alloc_chrdev_region(); /* 申请设备号 */
2 cdev_init(); /* 初始化cdev */
3 cdev_add(); /* 添加cdev */
4 class_create(); /* 创建类 */
5 device_create(); /* 创建设备 */
现在我们可以直接使用misc_register一个函数来完成示例代码37.1.3中的这些步骤。当我们卸载设备驱动模块的时候需要调用misc_deregister函数来注销掉MISC设备,函数原型如下:
int misc_deregister(struct miscdevice *misc) 函数参数和返回值含义如下: misc:要注销的MISC设备。 返回值:负数,失败;0,成功。 以前注销设备驱动的时候,我们需要调用一堆的函数去删除此前创建的cdev、设备等等内容,如下所示:
示例代码37.1.4 传统的删除设备的过程
1 cdev_del(); /* 删除cdev */
2 unregister_chrdev_region(); /* 注销设备号 */
3 device_destroy(); /* 删除设备 */
4 class_destroy(); /* 删除类 */
现在我们只需要一个misc_deregister函数即可完成示例代码37.1.4中的这些工作。关于MISC设备驱动就讲解到这里,接下来我们就使用platform加MISC驱动框架来编写beep蜂鸣器驱动。
37.2 硬件原理图分析 本章实验我们只使用到正点原子的STM32MP1开发板上的BEEP蜂鸣器,因此实验硬件原理图参考26.2小节即可。 37.3 实验程序编写 本实验对应的例程路径为:开发板光盘1、程序源码2、Linux驱动例程19_miscbeep。 本章实验我们采用platform加misc的方式编写beep驱动,这也是实际的Linux驱动中很常用的方法。采用platform来实现总线、设备和驱动,misc主要负责完成字符设备的创建。 37.3.1 修改设备树 本章实验我们需要用到蜂鸣器,因此需要在stm32mp157d-atk.dts文件中创建蜂鸣器设备节点。 1、添加pinctrl节点 在STM32MP157开发板上的蜂鸣器使用了PC7这个引脚,打开stm32mp15-pinctrl.dtsi文件,在pinctrl节点下添加一个‘beep_pins_a’子节点,这个节点就是蜂鸣器的pinctrl配置。
示例代码37.3.1.1 蜂鸣器的pinctrl节点
1 beep_pins_a: beep_pins {
2 pins {
3 pinmux = ; /* BEEP */
4 drive-push-pull;
5 bias-pull-up;
6 output-high;
7 slew-rate = ;
8 };
9 };
2、添加beep设备节点
在26.3.1小节我们创建了一个名为“beep”的子节点,本章我们就在这个子节点里面稍微修改即可,修改完成以后的beep子节点内容如下:
示例代码37.3.1.2 蜂鸣器节点
1 beep {
2 compatible = "alientek,beep";
3 status = "okay";
4 pinctrl-names = "default";
5 pinctrl-0 = ;
6 beep-gpio = ;
7 };
相比26.3.1小节里面的beep节点,这里仅仅添加了第4~5行,这两行指定BEEP所使用的引脚pinctrl子节点。
37.3.2 beep驱动程序编写 新建名为“19_miscbeep”的文件夹,然后在19_miscbeep文件夹里面创建vscode工程,工作区命名为“miscbeep。新建名为miscbeep.c的驱动文件,在miscbeep.c中输入如下所示内容:
示例代码37.3.2.1 miscbeep.c文件代码段
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 #include
19
20 #define MISCBEEP_NAME "miscbeep" /* 名字 */
21 #define MISCBEEP_MINOR 144 /* 子设备号 */
22 #define BEEPOFF 0 /* 关蜂鸣器 */
23 #define BEEPON 1 /* 开蜂鸣器 */
24
25 /* miscbeep设备结构体 */
26 struct miscbeep_dev{
27 dev_t devid; /* 设备号 */
28 struct cdev cdev; /* cdev */
29 struct class *class; /* 类 */
30 struct device *device; /* 设备 */
31 int beep_gpio; /* beep所使用的GPIO编号 */
32 };
33
34 struct miscbeep_dev miscbeep; /* beep设备 */
35
36 /*
37 * @description : beep相关初始化操作
38 * @param – pdev : struct platform_device指针,也就是platform设备指针
39 * @return : 成功返回0,失败返回负数
40 */
41 static int beep_gpio_init(struct device_node *nd)
42 {
43 int ret;
44
45 /* 从设备树中获取GPIO */
46 miscbeep.beep_gpio = of_get_named_gpio(nd, "beep-gpio", 0);
47 if(!gpio_is_valid(miscbeep.beep_gpio)) {
48 printk("miscbeep:Failed to get beep-gpio\n");
49 return -EINVAL;
50 }
51
52 /* 申请使用GPIO */
53 ret = gpio_request(miscbeep.beep_gpio, "beep");
54 if(ret) {
55 printk("beep: Failed to request beep-gpio\n");
56 return ret;
57 }
58
59 /* 将GPIO设置为输出模式并设置GPIO初始化电平状态 */
60 gpio_direction_output(miscbeep.beep_gpio, 1);
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 miscbeep_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 miscbeep_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 beepstat;
90
91 retvalue = copy_from_user(databuf, buf, cnt);
92 if(retvalue dev.of_node);
134 if(ret
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?