Linux MISC 驱动实验

2023-11-16

目录

一、MISC 设备驱动简介

misc_deregister 函数

二、MISC驱动编写

 1、编写框架

2、platform结构体对应的函数 

2、宏定义和miscbeep设备结构体

3、定义miscdevice结构体

字符设备操作集

4、probe函数

5、remove函数​编辑

 验证

6、添加开关

三、总代码

APP

miscbeep.c

验证


        misc 的意思是混合、杂项的,因此 MISC 驱动也叫做杂项驱动,也就是当我们板子上的某
些外设无法进行分类的时候就可以使用 MISC 驱动。

一、MISC 设备驱动简介

        所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号, MISC 设备驱动就用于解决此问题。 MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 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的设备文件。 fops 就是字符设备操作集合, MISC 设备驱动最终是需要使用用户提供的 fops操作集合

misc_deregister 函数

        以前我们需要自己调用一堆的函数去创建和卸载设备,比如在以前的字符设备驱动中我们会使用如下几个函数完成设备创建和卸载过程:

创建

1 alloc_chrdev_region();/* 申请设备号*/
2 cdev_init(); /* 初始化 cdev */
3 cdev_add(); /* 添加 cdev */
4 class_create(); /* 创建类 */
5 device_create(); /* 创建设备 */

卸载

1 cdev_del(); /* 删除 cdev */
2 unregister_chrdev_region(); /* 注销设备号 */
3 device_destroy(); /* 删除设备 */
4 class_destroy(); /* 删除类 */

         现在只需要一个 misc_deregister 函数即可完成上面的操作,设置好 miscdevice结构体 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下:

int misc_register(struct miscdevice * misc)
misc:要注册的 MISC 设备。返回值: 负数,失败; 0,成功

 卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:

int misc_deregister(struct miscdevice *misc)
misc:要注销的 MISC 设备。返回值: 负数,失败; 0,成功。

下面开始编写

二、MISC驱动编写

在“蜂鸣器驱动”实验中已经创建好了设备树节点,可以直接使用

 创建文件miscbeep.c文件和APP,修改makefile

添加头文件 #include <linux/miscdevice.h>

 1、编写框架

45行,因为这里使用设备树,所以.name字段无需作匹配功能 ,如果不使用设备树,就要在编写设备的时候保证设备和驱动的name字段要一致

2、platform结构体对应的函数 

39行,compatible字段的值一定要与使用的设备树节点compatible值的要一致

2、宏定义和miscbeep设备结构体

 29行,设置次设备号为144

  

3、定义miscdevice结构体

 65行,次设备号,使用宏定义

66行,设备名字,使用宏定义

67行,字符设备操作集,对应的函数需要实现

字符设备操作集

4、probe函数

 75行,因为已经使用设备树匹配设备节点,所以不需要再通过路径进行搜索,直接利用

dev->dev.of_node 即可获取

76行,获取设备树中的gpio属性,得到BEEP所使用的BEEP编号

81行,申请一个 GPIO 管脚,gpio 设置名字为beep-gpio

88行,设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP 

94行, 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备, 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可

5、remove函数

 退出的时候要把资源释放

 验证

编译后把模块加载到开发板,如下

  可以看到,主设备号为10,次设备号为57(设置的自动分配)

6、添加开关

 添加开关宏定义,在miscbeep_write函数里面获取APP传来的数据,然后根据数据去开关beep

三、总代码

APP

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

/*
    argc:应用程序参数个数(argv数组元素个数)
    argv:具体参数,也可以写作char **argv
    ./miscbeepAPP <filename> <0:1>   0表示关,1表示开
    ./miscbeepAPP  /dev/miscbeep 0    关
    ./miscbeepAPP  /dev/miscbeep 1    开
*/
int main(int argc, char *argv[])
{
    int fd,retvalue;
    char *filename;
    unsigned char databuf[1];

    /*判断命令行输入参数是否正确*/
    if(argc != 3){
        printf("error usage!\r\n");
        return -1;
    }
    /*用指针指向文件*/
    filename = argv[1];
    /*打开文件*/
    fd = open(filename , O_RDWR);
    if(fd < 0){
        printf("file open failed\r\n",filename);
        return -1;
    }
    /*获取控制开关的数字*/
    databuf[0] = atoi(argv[2]);/*将字符转换为数字*/
    /*如果输入的控制命令不是1或者0直接退出*/
    if(((int)databuf[0]) < 0 || ((int)databuf[0]) >1)
    {
        printf("control parameter error\r\n");
        close(fd);
        return -1;
    }
    /*写入操作*/
    retvalue = write(fd,databuf,sizeof(databuf));
    if(retvalue < 0){
        printf("LED control failed\r\n");
        close(fd);
        return -1;
    }
    /*关闭文件*/
    close(fd);

    return 0;
}

miscbeep.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/ide.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#define MISCBEEP_NAME   "miscbeep"
#define MISCBEEP_MINOR  144

#define BEEP_OFF 0
#define BEEP_ON  1


/*miscbeep设备结构体*/
struct miscbeep_dev{
    struct device_node *nd;/*设备节点*/
    int beep_gpio;/* beep所使用的GPIO编号*/
};
struct miscbeep_dev miscbeep;/* beep设备 */

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 count, loff_t *ppos)
{
    int ret = 0;
    unsigned char databuf[1];
    struct miscbeep_dev *dev = filp->private_data;
    ret = copy_from_user(databuf,buf,count);
    if(ret < 0){
        return -EINVAL;
    }
    if(databuf[0] == BEEP_ON){
        gpio_set_value(dev->beep_gpio, 0);/*打开*/
    }else if(databuf[0] == BEEP_OFF){
        gpio_set_value(dev->beep_gpio,1);/*关闭*/
    }
    return 0;
}
static int miscbeep_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/*字符设备操作集*/
struct file_operations miscbeep_fops = {
    .owner  =   THIS_MODULE, 
    .open   =   miscbeep_open,
    .write  =   miscbeep_write,
    .release=   miscbeep_release,
};

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

/*probe函数*/
static int miscbeep_probe(struct platform_device *dev)
{
    int ret =0;
    /*1、初始化beep的io*/
    miscbeep.nd = dev->dev.of_node;
    miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd,"beep-gpios",0);
    if(miscbeep.beep_gpio < 0){
        ret = -EINVAL;
        goto fail_findgpio;
    }
    ret = gpio_request(miscbeep.beep_gpio,"beep-gpio");
    if(ret){
        printk("can't request %d gpio\r\n",miscbeep.beep_gpio);
        ret = -EINVAL;
        goto fail_findgpio;
    }
    /*输出默认为高电平*/
    ret = gpio_direction_output(miscbeep.beep_gpio,1);
    if(ret < 0){
        ret = -EINVAL;
        goto fail_setoutput;
    }
    /*2、misc驱动注册*/
    ret = misc_register(&beep_miscdev);
    if(ret < 0){
        ret = -EINVAL;
        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)
{
    /* 注销misc设备 */
    misc_deregister(&beep_miscdev);
    /*拉高关闭beep*/
    gpio_set_value(miscbeep.beep_gpio , 1);
    /*释放GPIO*/
    gpio_free(miscbeep.beep_gpio);
    return 0;
}
/*platform匹配表*/
static const struct of_device_id beep_of_math[] = {
    {.compatible = "my,beep"},
    {/* sentinel */},
};
/*platfrom*/
static struct platform_driver miscbeep_driver = {
    .driver = {
        .name = "imx6ull-beep",
        .of_match_table = beep_of_math,/*设备树匹配表*/
    },
    .probe = miscbeep_probe,
    .remove = miscbeep_remove,

};
/*驱动入口函数*/
static int __init miscbeep_init(void)
{
    return platform_driver_register(&miscbeep_driver);
}
/*驱动出口函数*/
static void __exit miscbeep_exit(void)
{
    platform_driver_unregister(&miscbeep_driver);
}


module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");

验证

 主设备号为10,次设备为144,发送0就是关,1就是开;开着卸载驱动也会关

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux MISC 驱动实验 的相关文章

  • 如何让R使用所有处理器?

    我有一台运行 Windows XP 的四核笔记本电脑 但查看任务管理器 R 似乎一次只使用一个处理器 如何让 R 使用全部四个处理器并加速我的 R 程序 我有一个基本系统 我使用它在 for 循环上并行化我的程序 一旦您了解需要做什么 此方
  • 从 Xlib 转换为 xcb

    我目前正在将我的一个应用程序从 Xlib 移植到 libxcb 但在查找有关我有时使用的 XInput2 扩展的信息时遇到了一些麻烦 libxcb 中有 XInput2 实现吗 如果是的话 在哪里可以找到文档 目前我在使用此功能时遇到问题
  • 内核的panic()函数是否完全冻结所有其他进程?

    我想确认内核的panic 功能和其他类似kernel halt and machine halt 一旦触发 保证机器完全冻结 那么 所有的内核和用户进程都被冻结了吗 是panic 可以被调度程序中断吗 中断处理程序仍然可以执行吗 用例 如果
  • 与 pthread 的进程间互斥

    我想使用一个互斥体 它将用于同步对两个不同进程共享的内存中驻留的某些变量的访问 我怎样才能做到这一点 执行该操作的代码示例将非常感激 以下示例演示了 Pthread 进程间互斥体的创建 使用和销毁 将示例推广到多个进程作为读者的练习 inc
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • 在生产服务器上使用 Subversion 使文件生效的最佳方法是什么?

    目前我已经设置了 subversion 这样当我在 Eclipse PDT 中进行更改时 我可以提交更改 它们将保存在 home administrator 中项目文件 该文件具有 subversion 推荐的 branches tags
  • 如何查找哪个 Yocto 项目配方填充图像根文件系统上的特定文件

    我经常与 Yocto 项目合作 一个常见的挑战是确定文件为何 或来自什么配方 包含在 rootfs 中 这有望从构建系统的环境 日志和元数据中得出 理想情况下 一组命令将允许将文件链接回源 即配方 我通常的策略是对元数据执行搜索 例如gre
  • 从网络中的另一台计算机访问本地GAE SDK服务器

    我有开发服务器 来自 google appengine sdk 在我的 Ubuntu 计算机上运行 我可以通过导航到 localhost 8080 来访问我的网站 我想从网络中的另一台计算机 装有 Vista 操作系统 访问该站点 ifco
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • 如何在 Linux 内核中定义并触发我自己的新软中断?

    我想在 Linux 内核中创建自己的软中断 这是正确的方法吗 In the init我想触发该模块的softirq我将添加一个调用 394 void open softirq int nr void action struct softir
  • XAMPP为MariaDB设置root用户密码

    如何在 Ubuntu Kubuntu 16 04 上的 XAMPP 中设置 MariaDB 的 root 用户密码 默认情况下 root 用户没有设置密码 我正在使用 XAMPP 7 1 11 我在 Windows 和 Linux 上都成功
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • express.js api 应用程序中的内存泄漏

    我正在运行一个express js应用程序 它用作REST API 一个端点启动 puppeteer 并使用多个过程测试我的网站 启动应用程序并持续消耗端点后 我的 docker 容器每小时都会耗尽内存 如下所示 首先 我认为我的 pupp
  • [A-Z] 表示 [A-Za-z] 是怎么回事?

    我已经注意到 至少在我使用的一些基于 Unix 的系统上 ls A Z 已经给了我预期的结果ls A Za z 让我无法轻松获得以大写字母开头的该死的文件列表 我刚刚遇到了同样的事情grep 我无法让它停止与小写字母匹配 A Z 直到我最终
  • 如何wget目录中最新的文件

    我想编写一个 bash 脚本来下载并安装最新的每日构建程序 RStudio 是否有可能使wget仅下载目录中最新的文件http www rstudio org download daily desktop http www rstudio
  • 使用.sh脚本设置环境变量

    如何编写 sh 脚本以在 Ubuntu 14 上全局设置环境变量 i e bin sh sets this in to master etc environment export DB HOST 123 我知道我可以运行这个脚本 它只会将其
  • 如何在shell脚本中给出密码?

    在 shell 脚本文件中 我使用一些命令 例如scp and make install要求我输入密码 我运行一个 shell 脚本来编译一个大项目 一段时间后它会要求我输入密码才能使用scp 我需要等待该过程并在此之后提供密码 我只想通过
  • Ubuntu 的打包 - Web 应用程序

    Web 应用程序没有与 C 或类似文件不同的 make 文件 但是 它需要放置在特定的目录中 例如 var www 我是 Linux 打包新手 所以我的问题是 如何将我的应用程序打包到 deb 中 以便在安装时将其放入 etc myprog
  • 如何在Linux中自动启动需要X的应用程序

    我试图在系统进入运行级别 5 时自动启动 X 应用程序 这样做的正确方法是什么 我写了一个脚本并将其放在 etc init d 中 我已运行适当的 chkconfig 命令来设置 etc rcX d 目录中的符号链接 一切工作正常 除了当我
  • 如何找到进程启动时使用的原始用户名?

    有一个 perl 脚本需要以 root 身份运行 但我们必须确保运行该脚本的用户最初没有以用户 foo 身份登录 因为它将在脚本运行期间被删除 那么 我如何查明自登录以来可能已多次起诉的用户是否在该链中的任何时间都没有模拟过 foo 我发现

随机推荐