MISC杂项驱动实验
一、简介
MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动。当我们板子上的某
struct miscdevice { int minor; const char *name; const struct file_operations *fops; struct list_head list; struct device *parent; struct device *this_device; const struct attribute_group **groups; const char *nodename; umode_t mode; };
在miscdevice结构体的众多成员变量中,我们定义一个 MISC 设备(miscdevice 类型)只需要关注minor、name 和 fops 这三个成员
变量。
minor 表示子设备号,MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号,Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h 文件中。我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。
name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name
二、实验内容
1 2 3 4 5 6 7 8 9 10 | /*测试节点 beep*/ beep{ #address-cells = <1>; #size-cells =<1>; compatible = "alientek-beep" ; pinctrl-names = "default" ; pinctrl-0 = <&pinctrl_beep>; beep-gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>; status = "okay" ; }; |
(2)编写驱动入口、出口函数,注册platform驱动
1 2 3 4 5 6 7 8 9 10 11 12 | static int __init <strong>miscbeep_init</strong>( void ) { return platform_driver_register(&<strong>miscbeep_driver</strong>); //向内核注册miscbeep_driver驱动 } static void __exit <strong>miscbeep_exit</strong>( void ) { platform_driver_unregister(&<strong>miscbeep_driver</strong>); } module_init(<strong>miscbeep_init</strong>); module_exit(<strong>miscbeep_exit</strong>); MODULE_LICENSE( "GPL" ); MODULE_AUTHOR( "dongdong" ); |
(3)我们需要查找到设备树里面的beep相关属性
我们采用OF类型匹配来匹配驱动和设备。编写platform_driver 结构体,其中主要是设置of_match_table匹配表。
of_match_table保存着驱动的compatible匹配表。设备树中的每个设备节点的 compatible 属性会和 of_match_table 表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /*platform匹配表*/ static const struct <strong>of_device_id</strong> <strong>beep_of_match[]</strong>={ {.compatible = "<strong>alientek-beep</strong>" }, //主要是根据compatible属性查找 }; /*platform*/ static struct <strong>platform_driver miscbeep_driver</strong> = { .driver={ .name = "imx6ull-beep" , .<strong>of_match_table</strong> = <strong>beep_of_match</strong>, /*platform匹配表*/ }, .probe = <strong>miscbeep_probe</strong>, . remove = <strong>miscbeep_remove</strong>, }; |
(4)当驱动和设备匹配成功以后就会执行 probe 函数、当注销驱动模块的时候 remove 函数就会执行。接下来编写probe、remove函数。前面我们查找到了设备树里面的beep设备,现在我们要为beep设备创建节点,获取beep设备硬件IO,设置IO输出,至此我们成功利用platform驱动注册了名为miscbeep的设备驱动。此时我们可以安装和卸载miscbeep驱动了!MISC驱动注册很简单,只需要使用misc_register函数就可以了。
可以观察到主设备号为10。
/*MISC驱动 mcsidevice结构体变量 以及 file_operations操作结构体*/
/*beep结点相关属性*/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | struct file_operations miscbeep_fops ={ .owner = THIS_MODULE, .open = miscbeep_open, .release = miscbeep_release, .read = miscbeep_read, .write =miscbeep_write, }; <strong> static struct miscdevice beep_miscdev={ .minor = MISCBEEP_MINOR, .name = MISCBEEP_NAME, .fops = &miscbeep_fops, }; </strong> struct miscbeep_dev{ struct device_node *nd; int beep_gpio; }; struct miscbeep_dev miscbeep;<br> /*probe函数*/ static int miscbeep_probe( struct platform_device *dev) { int ret=0; /*gpio*/ miscbeep.nd = dev->dev.of_node; miscbeep.beep_gpio = <strong>of_get_named_gpio</strong>(miscbeep.nd, "beep-gpios" ,0); if (miscbeep.beep_gpio<0) { ret = -EINVAL; goto fail_findgpio; } ret = <strong>gpio_request</strong>(miscbeep.beep_gpio, "beep-gpio" ); if (ret) { printk( "can't request %d gpio" ,miscbeep.beep_gpio); goto fail_findgpio; } ret = <strong>gpio_direction_output</strong>(miscbeep.beep_gpio,1); if (ret<0) { goto fail_setoutput; } /*misc驱动注册*/ ret = <strong>misc_register</strong>(&beep_miscdev); if (ret<0) { goto fail_setoutput; } return 0; fail_setoutput: gpio_free(miscbeep.beep_gpio); fail_findgpio: return ret; } /*remove函数*/ static int miscbeep_remove( struct platform_device *dev) { gpio_set_value(miscbeep.beep_gpio,1); gpio_free(miscbeep.beep_gpio); misc_deregister(&beep_miscdev); return 0; } |
(5)接下来编写设备的具体操作函数。file_operations 结构体就是设备的具体操作函数。这是内核里面与用户对接的函数。比如write函数可以接收来自用户的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /* * @description : 打开设备 * @param - inode : 传递给驱动的inode * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量 * 一般在open的时候将private_data指向设备结构体。 * @return : 0 成功;其他 失败 */ static int miscbeep_open( struct inode *inode, struct file *filp) { filp->private_data = &miscbeep; /* 设置私有数据 */ return 0; } /* * @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符) * @param - buf : 返回给用户空间的数据缓冲区 * @param - cnt : 要读取的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 读取的字节数,如果为负值,表示读取失败 */ static ssize_t miscbeep_read( struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { struct miscbeep *dev = ( struct miscbeep *)filp->private_data; int retvalue = 0; /* 向用户空间发送数据 */ return 0; } /* * @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符 * @param - buf : 要写给设备写入的数据 * @param - cnt : 要写入的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 写入的字节数,如果为负值,表示写入失败 */ static ssize_t miscbeep_write( struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { struct miscbeep *dev = ( struct miscbeep *)filp->private_data; int retvalue = 0; unsigned char state; unsigned char databuf[1]; //存储beep开关的数据 /* 接收用户空间传递给内核的数据并且打印出来 */ retvalue = copy_from_user(databuf, buf, cnt); if (retvalue < 0) { return -EFAULT; } if (databuf[0]==BEEP_ON) { gpio_set_value(miscbeep.beep_gpio,0); } else if (databuf[0]==BEEP_OFF) { gpio_set_value(miscbeep.beep_gpio,1); } return 0; } /* * @description : 关闭/释放设备 * @param - filp : 要关闭的设备文件(文件描述符) * @return : 0 成功;其他 失败 */ static int miscbeep_release( struct inode *inode, struct file *filp) { struct miscbeep *dev = ( struct miscbeep *)filp->private_data; return 0; } |
(6)编写应用APP,安装驱动,运行APP,测试beep。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include "stdio.h" #include "sys/types.h" #include "sys/stat.h" #include "stdlib.h" #include "string.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /*************************************************************** Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : miscbeepAPP.c 作者 : study from 正点原子 版本 : V1.0 描述 : led驱测试APP。 其他 : 使用方法:./miscbeepAPP/dev/miscbeep <0>|<1> 0:open 1:close ***************************************************************/ #define BEEP_OFF 1 #define BEEP_ON 0 /* * @description : main主程序 * @param - argc : argv数组元素个数 * @param - argv : 具体参数 * @return : 0 成功;其他 失败 */ int main( int argc, char *argv[]) { int fd; char *filename; if (argc != 3) //检查传入参数个数是否正确 { printf ( "useage error\r\n" ); return -1; } int ret = 0; char readbuf[100]; char writebuf[1]; /*打开文件*/ filename = argv[1]; fd = open(filename, O_RDWR); if (fd < 0) { printf ( "can't open file %s\r\n" , filename); return -1; } /*写文件*/ writebuf[0] = atoi (argv[2]); //将字符转换成数字 ret = write(fd, writebuf, sizeof (writebuf)); /* 要执行的操作:打开或关闭 */ if (ret < 0) { printf ( "can't write file %s\r\n" , filename); return -1; } else <br> { ret = close(fd); if (ret < 0) { printf ( "can't close file %s\r\n" , filename); return -1; } } return 0; //表示返回成功 } |
实验结果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言