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

在miscdevice结构体的众多成员变量中,我们定义一个 MISC 设备(miscdevice 类型)只需要关注minornamefops 这三个成员
变量。

  minor 表示子设备号,MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号,Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h 文件中。我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。

  name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name

的设备文件。
  
  fops 就是字符设备的操作集合,MISC 设备驱动最终是需要使用用户提供的 fops
操作集合。

二、实验内容

  以蜂鸣器为例,最终实现蜂鸣器驱动的安装并通过应用调用使得蜂鸣器鸣叫。
a.实验步骤
(1)修改设备树文件(linux/arch/arm/boot/dts/imx6ull-alientek-emmc.dts)
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 函数

就会执行。每个of_match_table 匹配表都为of_device_id结构体类型,为结构体数组。
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; //表示返回成功
}

实验结果

 

 

 

 

 

 

posted @   是东东东啊  阅读(106)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言
点击右上角即可分享
微信分享提示