misc的意思是混合、杂项的,因此MISC驱动也叫做杂项驱动,也就是当板子上的某些外设无法进行分类的时候就可以使用MISC驱动。MISC驱动其实就是最简单的字符设备驱动,通常嵌套在platform总线驱动中,实现复杂的驱动,本章就来学习一下MISC驱动的编写。
MISC设备驱动简介
所有的MISC设备驱动的主设备号都为10,不同的设备使用不同的从设备号。随着Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号,MISC设备驱动就用于解决此问题。 MISC设备会自动创建cdev,不需要手动创建,因此采用MISC设备驱动可以简化字符设备驱动的编写。需要向Linux注册一个miscdevice设备,miscdevice是一个结构体,定义在文件include/linux/miscdevice.h中,内容如下:
定义一个MISC设备(miscdevice类型)以后需要设置minor、name和fops这三个成员变量。 minor表示子设备号,MISC设备的主设备号为10,这个是固定的,需要用户指定子设备号,Linux系统已经预定义了一些MISC设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h文件中,如下所示:
在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。
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蜂鸣器驱动。
硬件原理图分析
就是之前的BEEP蜂鸣器的原理图。
实验程序编写
本章实验采用platform加misc的方式编写beep驱动,这也是实际的Linux驱动中很常用的方法。采用platform来实现总线、设备和驱动,misc主要负责完成字符设备的创建。
修改设备树
需要在stm32mp157d-atk.dts文件中创建蜂鸣器设备节点。
添加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 = <STM32_PINMUX('C', 7, GPIO)>; /* BEEP */
4 drive-push-pull;
5 bias-pull-up;
6 output-high;
7 slew-rate = <0>;
8 };
9 };
添加beep设备节点
之前已经添加过了,稍作修改即可:
示例代码37.3.1.2 蜂鸣器节点
1 beep {
2 compatible = "alientek,beep";
3 status = "okay";
4 pinctrl-names = "default";
5 pinctrl-0 = <&beep_pins_a>;
6 beep-gpio = <&gpioc 7 GPIO_ACTIVE_HIGH>;
7 };
beep驱动程序编写
设备结构体miscbeep_dev中,只需要dev_t设备号,cdev结构体的cdev,class结构体的类,device结构体的设备,以及beep_gpio这个int类型的变量存储GPIO编号。
具象化beep设备,struct miscbeep_dev miscbeep。
通过beep_gpio_init初始化beep相关,通过of_get_named_gpio获取GPIO存入miscbeep.beep_gpio;然后gpio_request申请使用GPIO;最后通过gpio_direction_output设置初始化电平以及设置为输出。
msicbeep_write函数,要记一下是static ssize_t的,里面一样通过copy_to_user把数据输入进来然后存到databuf里面,databuf再传给beepstat,从而获取状态值,根据状态变量来gpio_set_value控制蜂鸣器状态。
还需要定义miscdevice结构体的MISC设备结构体beep_miscdev,里面要初始化.minor,.name和.fops。
然后就是platform驱动的probe函数static int miscbeep_probe,里面要通过beep_gpio_init初始化BEEP,然后通过misc_register注册MISC设备。
platform的remove函数里面,需要gpio_set_value关闭BEEP,然后gpio_free释放BEEP的IO,最后misc_deregister注销MISC设备。
还需要写一个匹配列表of_device_id结构体变量的beep_of_match,里面需要有匹配的.compatible属性的设备名(与设备树中节点的匹配)。
然后写一下platform的驱动结构体,.driver中的.name需要与设备匹配,再.of_match_table填入刚刚的匹配列表;还需要些.probe和.remove函数。
最后写一下驱动的入口和出口函数,就是platform的驱动注册和驱动注销函数。
编写测试APP
这里就是argc是3个,然后filename把argv[1]传入,open打开beep驱动,然后把要执行的操作通过atoi(argv[2])传入databuf里面,之后调用write来操作,最后close关闭文件。
运行测试
编译驱动程序
老样子,修改Makefile的obj-m为miscbeep.o,然后“make”以下就可以了。
编译测试APP
可以通过如下命令编译:
arm-none-linux-gnueabihf-gcc miscbeepApp.c -o miscbeepApp |
运行测试
将上一小节编译出来miscbeep.ko和miscbeepApp这两个文件拷贝到rootfs/lib/modules/5.4.31目录中,重启开发板,进入到目录lib/modules/5.4.31中,输入如下命令加载miscbeep.ko这个驱动模 块:
depmod //第一次加载驱动的时候需要运行此命令 modprobe miscbeep.ko //加载设备模块 |
当驱动模块加载成功以后可以在/sys/class/misc这个目录下看到一个名为“miscbeep"的子目录。所有的misc设备都属于同一个类,/sys/class/misc目录下就是misc这个类的所有设备,每个设备对应一个子目录。
驱动与设备匹配成功以后就会生成/dev/miscbeep这个设备驱动文件,输入如下命令查看这个文件的主次设备号:
ls /dev/miscbeep -l |
可以通过如下命令控制BEEP:
./miscbeepApp /dev/miscbeep 1 //打开BEEP ./miscbeepApp /dev/miscbeep 0 //关闭BEEP |
如果要卸载驱动的话输入如下命令即可:
rmmod miscbeep.ko |
总结
这一章就是通过MISC的方法来结合platform写一个驱动。
设备结构体就得到了简化,gpio的初始化是一样的;write函数也是一样的;需要自行写一个MISC设备结构体,以及platform驱动的probe和remove函数,这两者中的注册和注销就可以直接用misc_register和misc_deregister