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

发布于:2025-02-10 ⋅ 阅读:(40) ⋅ 点赞:(0)

前言

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

MISC设备驱动简介

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

重要结构体

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

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 设备,此函数原型如下,可以代替之前所使用的五个函数:

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

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

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

MISC实现框架

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

#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

网站公告

今日签到

点亮在社区的每一天
去签到