zl程序教程

您现在的位置是:首页 >  系统

当前栏目

【IMX6ULL】Linux中的MISC设备驱动(搭配platform设备驱动)

Linux设备驱动 Platform 搭配 Misc IMX6ULL
2023-09-11 14:20:46 时间

Linux MISC驱动

MISC驱动也叫杂项驱动,但是本质上就是一个字符设备驱动,通常嵌套在platform总线驱动中。

MISC设备驱动的主设备号都为10,不同设备使用不同的从设备号,也就是说,这些不同的设备在设备树上的位置处于MISC设备节点的子节点的层次。即 根节点 --> MISC节点 --> 设备节点。

miscdevice 结构体

在这里插入图片描述

minor:子设备的设备号;
name:设备名字;
fops:操作函数集合,一个结构体变量,存放各种操作函数;

注册MISC设备API

/*
	@function: 用于注册MISC设备
	@param:
			misc:要注册的 MISC 设备
	@return:
			0,成功;负数,失败
*/
int misc_register(struct miscdevice * misc)

MISC的优点

  1. MISC驱动本质上是一个字符设备驱动,但是内核集成一些API函数来帮助我们注册设备,这也就使得原来繁琐的字符设备驱动的注册和注销过程缩减了,即 init 和 exit 这两个函数内的内容。
  2. 所有的不知道在设备树上放哪的设备驱动节点,都可以挂载在MISC这个节点上,相当于作为MISC节点的子节点,那么这些设备的父设备号实际上就是MISC设备的设备号,而这些设备自身的设备号,就是MISC设备的子设备号。这样就节约了主设备号的浪费,就等于是原来只是一根树枝,现在是有很多枝杈的树枝。

学习过程中的猜想

MISC对于只需要一个IO进行通信的设备应该算是很友好的,那么一些依靠单总线协议的设备应该也能依靠MISC来编写相关驱动,例如DS18B20温湿度传感器等。

MISC设备下beep设备驱动代码编写

#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.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_NAME "miscbeep"	/* 设备名字 */
#define MISCBEEP_MINOR 144			/* 子设备号 */
#define BEEPOFF 0
#define BEEPON 1

/* MISC_beep 设备结构体 */
struct miscbeep_dev
{
    dev_t devid;				/* 设备号 */
    struct cdev cdev;			/* cdev */
    struct class *class;		/* 类 */
    struct device *device;		/* 设备 */
    struct device_node *nd;		/* 设备节点 */
    int beep_gpio;				/* beep 所使用的 GPIO 编号 */
};

/* beep 设备 */
struct miscbeep_dev miscbeep;

/* 打开设备 */
static int miscbeep_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &miscbeep;
    return 0;
}

/* 向设备写数据 */
static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int retvalue;
    unsigned char databuf[1];
    unsigned char beepstat;
    struct miscbeep_dev *dev = filp->private_data;

    retvalue = copy_from_user(databuf, buf, cnt);

    beepstat = databuf[0];
    if (beepstat == BEEPON)
    {
        gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */
    }
    else if (beepstat == BEEPOFF)
    {
        gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */
    }
    return 0;
}

/* 设备操作函数 */
static struct file_operations miscbeep_fops = {
    .owner = THIS_MODULE,
    .open = miscbeep_open,
    .write = miscbeep_write,
};

/* MISC 设备结构体 */
static struct miscdevice beep_miscdev = {
    .minor = MISCBEEP_MINOR,
    .name = MISCBEEP_NAME,
    .fops = &miscbeep_fops,
};

/*  flatform 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行 */
static int miscbeep_probe(struct platform_device *dev)
{
    int ret = 0;
    printk("beep driver and device was matched!\r\n");

    /* 1.获取设备节点:beep */
    miscbeep.nd = of_find_node_by_path("/beep");
    if(miscbeep.nd == NULL)
    {
        printk("beep node not find!\r\n");
        return -EINVAL;
    }

    /* 2.获取设备树中的GPIO属性,得到 beep 使用的 beep 编号 */
    miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);
    if(miscbeep.beep_gpio < 0)
    {
        printk("can't get beep-gpio");
        return 0;
    }

    /* 3.设置 GPIO5_IO01 为输出,并且输出高电平, 默认关闭 beep */
    ret = gpio_direction_output(miscbeep.beep_gpio, 1);
    if(ret < 0) 
    {
        printk("can't set gpio!\r\n");
    }
    /* 注册 misc 设备,取代了注册字符设备的那5个步骤 */
    ret = misc_register(&beep_miscdev);
    if(ret < 0)
    {
        printk("misc device register failed!\r\n");
        return -EFAULT;
    }
    return 0;
}

/* remove 函数,移除 platform 驱动的时候此函数会执行 */
static int miscbeep_remove(struct platform_device *dev)
{
    gpio_set_value(miscbeep.beep_gpio, 1);
    /* 注销 misc 设备驱动 */
    misc_deregister(&beep_miscdev);
    return 0;
}

/* 匹配列表 */
static const struct of_device_id beep_of_match[] = {
    { .compatible = "atkalpha-beep" },
    { /* sentinel */ }
};

/* platform 驱动结构体 */
static struct platform_driver beep_driver = {
    .driver = {
        .name = "imx6ul-beep",
        .of_match_table = beep_of_match,
    },
    .probe = miscbeep_probe,
    .remove = miscbeep_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("Swiler");