嵌入式驱动开发详解10(MISC杂项实现)

文章目录

前言

MISC 驱动也叫做杂项驱动,也就是当我们板子上的某 些外设无法进行分类的时候就可以使用 MISC 驱动。MISC 驱动其实就是最简单的字符设备驱 动,通常嵌套在 platform 总线驱动中,

MISC设备驱动简介

所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux 字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号,MISC 设备驱动就用于解决此问题。MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设备驱动可以简化字符设备驱动的编写。

重要结构体

编写MISC设备需要向 Linux 注册一个 miscdevice 设备,miscdevice 是一个结构体,定义在文件 include/linux/miscdevice.h 中。

c 复制代码
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;
};

定义一个 MISC 设备(miscdevice 类型)以后我们需要设置 minor、name 和 fops 这三个成员变量。

  • minor 表示子设备号,MISC 设备的主设备号为 10,这个是固定的;
  • 需要用户指定子设备 号,Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在 include/linux/miscdevice.h 文件中;
  • name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name 的设备文件;
  • ops 就是字符设备的操作集合,MISC 设备驱动最终是需要使用用户提供的 fops 操作集合。

API函数

当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下,可以代替之前所使用的五个函数:

c 复制代码
int misc_register(struct miscdevice * misc)
------>  //效果跟下面五个函数一起使用一致
alloc_chrdev_region(); /* 申请设备号 */
cdev_init(); /* 初始化 cdev */ 
cdev_add(); /* 添加 cdev */ 
class_create(); /* 创建类 */ 
device_create(); /* 创建设备 */

卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下,可以代替之前所使用的四个函数:

c 复制代码
int misc_deregister(struct miscdevice *misc)
-----> 效果跟下面五个函数一起使用一致
cdev_del(); /* 删除 cdev */
unregister_chrdev_region(); /* 注销设备号 */
device_destroy(); /* 删除设备 */
class_destroy(); /* 删除类 */

MISC实现框架

下面以蜂鸣器的驱动为例进行讲解:

c 复制代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>  //copy
#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.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define MISCBEEP_MINOR 144
#define MISCBEEP_NAME "miscbeep"
#define BEEP_ON 1
#define BEEP_OFF 0

struct beep_dev{
/*
	dev_t devid; 
	struct cdev cdev;
	struct class *class;
	struct device *device;
*/
	struct device_node *nd;
	int beep_gpio;
};

struct beep_dev beepdev;

static int beep_open(struct inode *inode, struct file *file)
{
	file->private_data = &beepdev; /* 设置私有数据 */
	return 0;		
}

static ssize_t beep_write(struct file *file, const char __user *buf,
				size_t count, loff_t *off)
{
	unsigned char status;
	int ret;
	struct beep_dev *dev = file->private_data;
	ret = copy_from_user(&status,buf,count);
	if(ret < 0){
		printk("kernel write failed!!!");
		return -1;
	}
	printk("device write%d\r\n",status);
	if(status == BEEP_ON){
		gpio_set_value(dev->beep_gpio, 0);
	}else if(status == BEEP_OFF){
		gpio_set_value(dev->beep_gpio, 1);
	}
	return 0;
}

static const struct file_operations beep_fops = {
	.owner	= THIS_MODULE,
	.open	= beep_open,
	.write	= beep_write,
};

static struct miscdevice beep_miscdevice = {
	.minor	= MISCBEEP_MINOR,
	.name	= MISCBEEP_NAME,
	.fops	= &beep_fops,
};

static int imscbeep_probe(struct platform_device *dev)
{
	int ret = 0;
	printk("beep driver and device was matched\r\n");
	beepdev.nd = dev->dev.of_node;
	if(beepdev.nd == NULL) {
		printk("beep node not find!\r\n");
		return -EINVAL;
	}
	beepdev.beep_gpio = of_get_named_gpio(beepdev.nd,"beep-gpio",0);
	if(beepdev.beep_gpio < 0) {
		printk("can't get beep-gpio");
		return -EINVAL;
	}
	gpio_request(beepdev.beep_gpio,"beep");
	gpio_direction_output(beepdev.beep_gpio,1);
	ret = misc_register(&beep_miscdevice);
	return 0;
}

static int imscbeep_remove(struct platform_device *dev)
{
	gpio_set_value(beepdev.beep_gpio,1);
	gpio_free(beepdev.beep_gpio);
	misc_deregister(&beep_miscdevice);
	return 0;
}

static const struct of_device_id beep_of_match[] = {
	{.compatible	= "hbb-beep"},
	{				}
};


static struct platform_driver beep_driver = {
	.driver = {
		.name	= "beep_driver",
		.of_match_table	= beep_of_match,
	},
	.probe	= imscbeep_probe,
	.remove	= imscbeep_remove,
};

static int __init miscbeep_init(void)
{
	return platform_driver_register(&beep_driver);
}

static void __exit miscbeep_exit(void)
{
	platform_driver_unregister(&beep_driver);
}

module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("hubinbin");

如上所示,当驱动挂载后会在/sys/class/misc 这个目录下看到一个名为"miscbeep" 的子目录,所有的 misc 设备都属于同一个类,/sys/class/misc 目录下就是 misc 这个类的所有设备,每 个设备对应一个子目录。

此外驱动与设备匹配成功以后还会生成/dev/miscbeep 这个设备驱动文件。

后续

以上便是本人对MISC杂项的理解,本人认为主要起到三个作用,一是管理分类不是很明确的外设,二是简化驱动流程,三是节约设备号资源。具体的详细实现过程没深入研究,仅仅只是会使用MISC,后续如果从事驱动开发相关的工作会继续深入研究。

参考文献

  1. 个人专栏系列文章
  2. 正点原子嵌入式驱动开发指南
  3. 对代码有兴趣的同学可以查看链接https://github.com/NUAATRY/imx6ull_dev
相关推荐
沫璃染墨10 小时前
《从零入门Linux系统篇(五十八):线程篇·十一——线程安全与死锁详解:从可重入到多锁管理》
linux·服务器·开发语言·c++·驱动开发·安全·架构
ebiobiz11 小时前
Zig 工具链编译 STM32 开发指南
驱动开发·stm32·嵌入式硬件
sukalot12 小时前
Windows 驱动实例分析系列:libwdi 驱动分析 - 应用篇(一)
windows·驱动开发
Carl_奕然13 小时前
【智能体】Loop 的四种设计模式之:Event-Driven Loop(2026 最新版)
人工智能·驱动开发·python·设计模式
gwf2161 天前
Completion Queue(CQ)与中断处理:轮询、中断聚合与错误码 —— 面向AI集群的驱动级深度剖析
驱动开发·性能调优·pcie·rdma·ai集群·中断聚合·cq
kakakahahahaha1 天前
Windows鼠标失灵排查:Ctrl Alt Delete、Mouse Keys、USB控制器与驱动修复
windows·驱动开发·电脑·笔记本电脑·软件需求
傲世仙尊2 天前
TCP报头全解-序号确认应答与可靠性是一个准数
驱动开发·网络协议·tcp/ip
sukalot2 天前
Windows 驱动实例分析系列:libwdi 驱动分析 - libwdi 篇(五)
windows·驱动开发
沫璃染墨3 天前
《从零入门Linux系统篇(五十六):线程篇·九——生产者消费者模型:从条件变量到BlockingQueue实现》
linux·服务器·c++·驱动开发·设计模式·架构·系统架构
傲世仙尊3 天前
UDP底层与守护进程化-报头结构体skbuff指针移动与自成会话
驱动开发·网络协议·udp