Linux驱动_Misc杂项驱动

MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动,他的作用就是解决Linux系统设备号紧缺的问题。

所有的MISC设备驱动的主设备号都为10,不同的设备使用不同的从设备号!


一、为什么使用MISC

        MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设
备驱动可以简化字符设备驱动的编写,可以省去以下函数:

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

二、使用步骤

        ① 要使用MISC设备驱动,我们需要往Linux系统中注册一个miscdevice设备。miscdevice
是一个结构体定义如下

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;
};

        要想Linux系统注册一个miscdevice设备,至少需要配置minor、name和fops三个参数。其中minor表示从设备号,name表示设备名字,fops是字符设备操作函数集合。

        ②注册和注销MISC设备

        注册函数为:

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

        注销函数为:

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

        通常在platformprobe函数中注册,remove函数中注销 。

三、实验代码

        驱动代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MISCBEEP_MINOR 144  /*子设备号*/
#define MISCBEEP_NAME "misbeep" /*设备名*/

#define BEEPON 1    /*开蜂鸣器*/
#define BEEPOFF 0   /*关蜂鸣器*/

struct miscbeep_dev{
    dev_t devid;    /*设备号*/
    struct cdev cdev;   /*字符设备*/
    struct class *class;    /*定义类*/
    struct device *device; /*定义设备*/
    struct device_node *node; /*添加节点*/
    int beep_gpio;   /*beep所用的gpio号*/
    struct miscdevice *beep_miscdev;
};


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 ret = 0;
    unsigned char databuf[1];
    
    ret = copy_from_user(databuf, buf, cnt);
    if(ret < 0){
        return -EINVAL;
    }
    if(databuf[0] == BEEPON){
        gpio_set_value(miscbeep.beep_gpio, 0);
    }
    else if(databuf[0] == BEEPOFF){
        gpio_set_value(miscbeep.beep_gpio, 1);
    }
    return 0;
}

/*定义字符操作集*/
static const struct file_operations miscbeep_fops = {
    .owner = THIS_MODULE,
    .write = miscbeep_write,
    .open = miscbeep_open,
};

static struct miscdevice beep_miscdev = {
    .minor = MISCBEEP_MINOR,
    .name = MISCBEEP_NAME,
    .fops = &miscbeep_fops,
 };

static int miscbeep_probe(struct platform_device *dev){
    int ret = 0;
    printk("miscbeep_init!\r\n");

    miscbeep.beep_miscdev = &beep_miscdev;

    /*设置Beep所用GPIO*/
    miscbeep.node = of_find_node_by_path("/beep");   /*获取设备节点*/
    if(miscbeep.node == NULL){
        printk("can't find beep node\r\n");
        ret = -EINVAL;
        goto failed_findnode;
    }

    /*获取设备树gpio属性,beep的gpio号*/
    miscbeep.beep_gpio = of_get_named_gpio(miscbeep.node, "beep-gpios", 0);
    if(miscbeep.beep_gpio < 0){
        printk("can't find beep gpio \r\n");
        ret = -EINVAL;
        goto failed_findnode;
    }
    printk("beep gpio num = %d \r\n",miscbeep.beep_gpio);

    /*设置 GPIO5_IO01 为输出,并且输出高电平,默认关闭 BEEP */
    ret = gpio_direction_output(miscbeep.beep_gpio, 1);
    if(ret < 0) {
        printk("can't set gpio!\r\n");
    }

    /* 一般情况下会注册对应的字符设备,但是这里我们使用 MISC 设备
    * 所以我们不需要自己注册字符设备驱动,只需要注册 misc 设备驱动即可
    */
    ret = misc_register(miscbeep.beep_miscdev);
    if(ret < 0){
        printk("misc device register failed!\r\n");
        return -EFAULT;
    }
    return 0;

failed_findnode:
    return ret;
}

static int miscbeep_remove(struct platform_device *dev){
    printk("miscbeep_exit!\r\n");

    /* 注销设备的时候关闭蜂鸣器 */
    gpio_set_value(miscbeep.beep_gpio, 1);

    /* 注销 misc 设备驱动 */
    misc_deregister(miscbeep.beep_miscdev);
    return 0;
}


struct of_device_id miscbeep_of_match[] = {
    {.compatible = "alientek,beep"},
    {/* Sentinel */},
};

struct platform_driver miscbeep_driver = {
    .driver ={
        .name = "imx6ul-beep",   /*无设备树的时候,会和设备里面platform_device->driver->name匹配*/
        .of_match_table = miscbeep_of_match, /*设备树匹配表*/
    },
    .probe = miscbeep_probe,
    .remove = miscbeep_remove,
};

/*驱动加载函数*/
static int __init miscbeep_init(void){
        /*注册platform驱动*/
    platform_driver_register(&miscbeep_driver);
    return 0;
}

/*驱动卸载函数*/
static void __exit misbeep_exit(void){
    platform_driver_unregister(&miscbeep_driver);
}


/*驱动入口和出口*/
module_init(miscbeep_init);
module_exit(misbeep_exit);

/*协议*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

 应用代码:

#include 
#include 
#include 
#include 
#include 
#include 

/*
*argc:应用程序参数个数
*argv[]文件参数名,字符串形式
*./beepApp /dev/led 0 关更名器
*./beepApp /dev/led 1 开蜂鸣器
*/
#define LEDOFF 0
#define LEDON 1

int main(int argc, int *argv[])
{
    int fd, retval;
    char *filename;
    unsigned char databuf[1];

    if(argc != 3){
        printf("Error use\r\n");
        return -1;
    }
    /*保存设备文件名*/
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("open file failed \r\n");
        return -1;
    }
    /*将字符转化为数字*/
    databuf[0] = atoi(argv[2]);

    retval = write(fd, databuf, sizeof(databuf));
    if(retval < 0){
        printf("led write failed\r\n");
        close(fd);
        return -1;
    }
    close(fd);

    return 0;
}

你可能感兴趣的:(linux驱动,#,IMX6ULL,linux,驱动开发,嵌入式,c语言)