字符设备驱动的简化版混杂设备驱动

我们在上一篇文章中聊的字符设备驱动的写法,参见linux驱动三种写法之——软硬件信息未分离(以字符设备驱动程序为例)
混杂设备的说明
如标题,内核提供了一种简化字符设备驱动的写法,是将主设备号固定为10,然后可以设置内核自动分配次设备号来区分设备个体,本质上还是字符设备,就是将上述链接中的字符设备驱动框架中的【设备号处理工作】和【字符设备对象的处理工作】打包在【混杂设备对象的处理工作】中了。
字符设备驱动框架 与 混杂设备驱动框架 对比图
在这里插入图片描述
在这里插入图片描述

看一下【字符设备驱动框架】和【混杂设备驱动框架】的差异就明白了。接下来我们来看看具体混杂设备对象是如何实现的。
描述混杂设备对象结构体 struct miscdevice

struct miscdevice  {
    int minor; //混杂设备对应的次设备号,注意哦:主设备号为 10
    const char *name; // 将来的设备文件名,并且设备文件是自动创建
    const struct file_operations *fops; //提供的硬件操作接口对象
    struct list_head list;
    struct device *parent;
    struct device *this_device;
    const char *nodename;
    umode_t mode;
};

混杂设备对象的配套函数misc_register() 与 misc_deregister()

  • 混杂设备注册函数misc_register(),其定义如下
int misc_register(struct miscdevice *misc);

函数说明:向内核注册一个混杂设备对象, 内核会帮你自动创建一个名称为 name 的设备文件并且帮你分配一个次设备号

  • 混杂设备卸载函数misc_deregister(),其定义如下
int misc_deregister(struct miscdevice *misc);

函数说明:从内核卸载一个混杂设备对象, 内核会帮你删除创建的设备文件内核会帮你释放申请的次设备号
将上述链接中的linux字符设备驱动实例改写成linux混杂设备驱动程序,如下


/*编写混杂设备驱动,利用ioctl随意开关上述四个LED等*/

//头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>

//定义描述led灯硬件的数据结构
struct led_resource{
    int gpio;
    char *name;
};
//定义初始化4个led灯的硬件信息
struct led_resource led_info[] = {
    {PAD_GPIO_C+12, "LED1"},
    {PAD_GPIO_C+7, "LED2"},
    {PAD_GPIO_C+11, "LED3"},
    {PAD_GPIO_B+26, "LED4"}
};

#define LED_ON  0X100001
#define LED_OFF 0X100002
//调用关系:应用程序ioctl -> 软中断 -> 内核sys_ioctl ->驱动led_ioctl
//例子:int index=1;ioctl(fd, LED_ON, &index);//开第1个灯
//参数关系:
//      fd<---->file 亲戚关系
//      cmd=LED_ON或者cmd=LED_OFF
//      arg=(unsigned long)&index
long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
    //分配内核缓冲区
    int kindex;
    copy_from_user(&kindex, (int *)arg, sizeof(kindex));
    switch(cmd){
        case LED_ON:
            gpio_set_value(led_info[kindex-1].gpio, 0);
            printk("%s:开第%d个灯\n",__func__, kindex);
            break;
        case LED_OFF:
                gpio_set_value(led_info[kindex - 1].gpio, 1);
                printk("%s:关第%d个灯\n",__func__, kindex);
                break;
        default:
                printk("无效命令!\n");
                return -1;
    }
    return 0;
}

//定义led灯的硬件操作接口
struct file_operations led_fops = {
    .unlocked_ioctl = led_ioctl,
    .owner = THIS_MODULE
};
//定义初始化led的混杂设备对象
struct miscdevice led_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .fops = &led_fops,
    .name = "vaccine_misc"
};
//入口函数
int led_drv_init(void){
    //申请gpio资源
    int i;
    for(i = 0; i <  ARRAY_SIZE(led_info); i++){
        gpio_request(led_info[i].gpio, led_info[i].name);
        gpio_direction_output(led_info[i].gpio, 1);
    }
 //向内核注册一个混杂设备对象,内核会自动创建一个名称为name的设备文件并且会自动分配一个次设备号
    misc_register(&led_misc);
    return 0;
}
//出口函数
void led_drv_exit(void){
    //释放gpio资源
    int i;
    for(i = 0; i < ARRAY_SIZE(led_info); i++){
        gpio_set_value(led_info[i].gpio, 1);
        gpio_free(led_info[i].gpio);
    }
  //从内核卸载一个混杂设备对象,内核会删除创建的设备文件
    //且内核会释放掉申请的次设备号
    misc_deregister(&led_misc);
}
//各种修饰和GPL规则
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");


应用程序不变

猜你喜欢

转载自blog.csdn.net/weixin_43326587/article/details/106935492
今日推荐