Linux MISC 驱动实验

目录

一、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就是开;开着卸载驱动也会关

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux驱动开发是指在Linux操作系统下开发和编写设备驱动程序的过程。设备驱动程序是操作系统与硬件之间的桥梁,负责将操作系统的指令翻译成硬件可以理解的信号和命令,从而实现对硬件设备的控制和管理。 Linux驱动开发涉及到多个方面的知识和技术,包括设备文件的创建、设备的注册和初始化、中断处理、内存管理、驱动的加载与卸载等。下面分别对这些方面进行详细解释: 1. 设备文件的创建:在Linux系统中,每个设备都被视为一个特殊文件。驱动开发者需要通过创建设备文件来与硬件设备进行交互。这可以通过使用mknod命令或者udev规则来完成。 2. 设备的注册和初始化:驱动程序需要将自己注册到Linux内核中,以便操作系统能够正确地识别和调用驱动程序。这可以通过调用相关的注册函数(如platform_driver_register()或者misc_register())来完成。同时,驱动程序还需要对设备进行初始化,包括配置硬件寄存器、申请内存资源等。 3. 中断处理:中断是设备与CPU之间的一种异步通信机制,用于处理设备发生的事件。驱动程序需要注册中断处理函数,并在中断发生时执行相应的操作。在Linux中,可以通过request_irq()函数来申请中断,并通过irq_handler_t类型的函数来处理中断。 4. 内存管理:驱动程序需要管理设备所使用的内存资源,包括申请和释放内存。在Linux中,可以使用kmalloc()和kfree()函数来进行动态内存分配和释放。 5. 驱动的加载与卸载:驱动程序需要被加载到内核中才能生效。可以通过编译成内核模块的方式加载驱动,也可以将驱动编译进内核。加载驱动可以使用insmod命令,卸载驱动可以使用rmmod命令。 此外,驱动开发者还需要了解Linux内核的架构和相关的API接口,熟悉C语言和汇编语言编程,以及调试和排查驱动问题的技巧。 希望以上内容对你有所帮助!如有更多问题,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值