资讯动态

Linux misc驱动实践

发布时间:2026/10/15 4:08:11 来源:尧图企业网站定制
阅读了正点原子的《I.MX6U嵌入式Linux驱动开发指南》第五十七章Linux MISC驱动实验了解到misc驱动也是字符设备驱动并且嵌套在platform总线驱动中既然前面学习了platform总线驱动的框架所以决定在理解misc驱动原理后自己不看例程独立完成misc驱动编写。先学习一下misc驱动的原理。所有的MISC设备驱动的主设备号都是10从设备号分配给不同的设备。Linux系统预定义了一些从设备号在include/linux/miscdevice.h文件中可以查到。Linux为misc设备定义了一个结构体定义在include/linux/miscdevice.h中struct miscdevice { int minor; const char *name; const struct file_operations *fos; struct list_head list; struct device *parent; struct device *this_device; const struct attribute_group **group; const char *nodename; umode_t mode; };我们需要设置minor、name和fops这三个成员变量。minor可以自己定义一个尚未使用的子设备号。name就是要编写的misc设备名字当它注册成功后会在/dev目录下生成一个名为name的设备文件。fops是字符设备的操作集合。设置好miscdevice结构体后就需要向系统注册一个MISC设备使用函数int misc_register(struct miscdevice * misc)卸载MISC设备驱动模块时要调用的函数int misc_deregister(struct miscdevice *misc)使用了misc_register()函数后传统的创建设备过程中调用的申请设备号函数alloc_chrdev_region()、初始化cdev函数cdev_init()、添加cdev函数cdev_add()、创建类函数class_create()、创建设备函数device_create()都不需要了用一个misc_register()函数就可以搞定注册和创建设备。同样的注销misc设备驱动时以前需要调用的删除cdev函数cdev_del()、注销设备号函数unregister_chrdev_region()、删除设备函数device_destroy()、删除类函数class_destroy()也不需要了用misc_deregister()函数就可以完成。MISC驱动实验还是去驱动IMX6U-ALPHA开发板上的BEEP峰鸣器。编写完miscbeep驱动程序编译通过运行后出现“Unable to handle kernel NULL pointer dereference at virtual address 00000000报错如下图。针对报错信息怀疑是有未初始的指针被引用了。查看编写的源码看到定义在beepdev_dev结构体时将struct miscdevice 变量也封装进来了然后在beep_probe()函数中对这个变量进行minor、name、fops赋值最后再进行注册。考虑到misc设备结构体是Linux内核提供的这个结构体需要我们定义的成员有3个其他成员并没有定义这就可能造成是空指针。因此不封装struct miscdevice变量而是在驱动程序文件中声明这个变量对变量的name、minor、fops赋值。 修改后的源码如下#include linux/types.h #include linux/kernel.h #include linux/delay.h #include linux/ide.h #include linux/init.h #include linux/module.h #include linux/errno.h #include linux/gpio.h #include linux/cdev.h #include linux/device.h #include linux/of_gpio.h #include linux/semaphore.h #include linux/timer.h #include linux/irq.h #include linux/wait.h #include linux/poll.h #include linux/fs.h #include linux/fcntl.h #include linux/miscdevice.h #include linux/platform_device.h #include asm/mach/map.h #include asm/uaccess.h #include asm/io.h /*************************************************************** Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : leddriver.c 作者 : 版本 : V1.0 描述 : platform驱动 其他 : 无 论坛 : www.openedv.com 日志 : 初版V1.0 2019/8/13 左忠凯创建 ***************************************************************/ #define BEEPDEV_CNT 1 /* 设备号长度 */ #define BEEPDEV_NAME miscbeep /* 设备名字 */ #define BEEPOFF 0 #define BEEPON 1 #define MISC_BEEP_MINOR 240 /* beepdev设备结构体 */ struct beepdev_dev { dev_t devid; /* 设备号 */ struct device_node *node; /*struct miscdevice *miscdev;*/ int beep0; }; struct beepdev_dev beepdev; /* led设备 */ /* * description : beep打开/关闭 * param - sta : BEEPON(0) 打开BEEPBEEPOFF(1) 关闭BEEP * return : 无 */ void beep0_switch(u8 sta) { if(sta BEEPON){ gpio_set_value(beepdev.beep0, 0); }else if(sta BEEPOFF){ gpio_set_value(beepdev.beep0, 1); } } /* * description : 打开设备 * param - inode : 传递给驱动的inode * param - filp : 设备文件file结构体有个叫做private_data的成员变量 * 一般在open的时候将private_data指向设备结构体。 * return : 0 成功;其他 失败 */ static int beep_open(struct inode *inode, struct file *filp) { filp-private_data beepdev; /* 设置私有数据 */ return 0; } /* * description : 向设备写数据 * param - filp : 设备文件表示打开的文件描述符 * param - buf : 要写给设备写入的数据 * param - cnt : 要写入的数据长度 * param - offt : 相对于文件首地址的偏移 * return : 写入的字节数如果为负值表示写入失败 */ static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue; unsigned char databuf[1]; unsigned char beepstat; retvalue copy_from_user(databuf, buf, cnt); if(retvalue 0) { return -EFAULT; } beepstat databuf[0]; /* 获取状态值 */ if(beepstat BEEPON) { beep0_switch(BEEPON); /* 打开LED灯 */ }else if(beepstat BEEPOFF) { beep0_switch(BEEPOFF); /* 关闭LED灯 */ } return 0; } /* 设备操作函数 */ static struct file_operations beep_fops { .owner THIS_MODULE, .open beep_open, .write beep_write, }; #if 1 static struct miscdevice beep_dev { .minor MISC_BEEP_MINOR, .name miscbeep, .fops beep_fops }; #endif /* * description : flatform驱动的probe函数当驱动与 * 设备匹配以后此函数就会执行 * param - dev : platform设备 * return : 0成功;其他负值,失败 */ static int beep_probe(struct platform_device *dev) { int ret 0; printk(beep driver and device has matched!\r\n); #if 0 beepdev.miscdev-minor MISC_BEEP_MINOR; beepdev.miscdev-name miscbeep; beepdev.miscdev-fops beep_fops; #endif ret misc_register(beep_dev); if (ret 0) { printk(beepdev not register!\r\n); return ret; } beepdev.node of_find_node_by_path(/gpiobeep); if (beepdev.node NULL) { printk(gpiobeep node not find!\r\n); return -EINVAL; } beepdev.beep0 of_get_named_gpio(beepdev.node, beep-gpio, 0 ); if (beepdev.beep0 0) { printk(cant get beep-gpio\r\n); return -EINVAL; } gpio_request(beepdev.beep0, beep0); gpio_direction_output(beepdev.beep0, 1); return 0; } /* * description : platform驱动的remove函数移除platform驱动的时候此函数会执行 * param - dev : platform设备 * return : 0成功;其他负值,失败 */ static int beep_remove(struct platform_device *dev) { gpio_set_value(beepdev.beep0, 1); misc_deregister(beep_dev); return 0; } static const struct of_device_id beep_of_match[] { { .compatible atkalpha-beep }, { } }; /* platform驱动结构体 */ static struct platform_driver beep_driver { .driver { .name imx6ul-beep, /* 驱动名字用于和设备匹配 */ .of_match_table beep_of_match, }, .probe beep_probe, .remove beep_remove, }; /* * description : 驱动模块加载函数 * param : 无 * return : 无 */ static int __init beepdriver_init(void) { return platform_driver_register(beep_driver); } /* * description : 驱动模块卸载函数 * param : 无 * return : 无 */ static void __exit beepdriver_exit(void) { platform_driver_unregister(beep_driver); } module_init(beepdriver_init); module_exit(beepdriver_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(bobluo);重新编译后开始加载miscbeep.ko驱动在/sys/class/misc目录下可以看到“miscbeep”的子目录驱动与设备匹配成功后用ls /dev/miscbeep -l命令可以查看主次设备号如下图。输入打开或关闭BEEP的命令如下图可以正常开关蜂鸣器至此自己编写的misc驱动运行成功。参考资料1.I.MX6Uq嵌入式Linux驱动开发指南V2.0.1正点原子2. Linux设备驱动开发 详解基于最新的Linux 4.0内核

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑