cdev方式注册驱动

1、简介

内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。

(1)register_chrdev 比较老的内核注册的形式 早期的驱动
(2)register_chrdev_region/alloc_chrdev_region + cdev 新的驱动形式

区别:register_chrdev()函数是老版本里面的设备号注册函数,可以实现静态和动态注册两种方法,主要是通过给定的主设备号是否为0来进行区别,为0的时候为动态注册。register_chrdev_region以及alloc_chrdev_region就是将上述函数的静态和动态注册设备号进行了拆分的强化。

1.2 使用流程

字符驱动cdev注册流程
1. 申请并注册主从设备号
2. 初始化已定义的cdev变量,cdev变量指定file_operations接口
3. 添加cdev变量到内核,完成驱动注册,添加cdev时需要一个已申请成功的主从设备号

 字符驱动cdev注销流程
4. 删除已添加的cdev
5. 注销申请的主从设备号

1.3 主要方法
//动态分配设备编号
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
dev:自动分配的主设备号将保存在dev中
firstminor:第一个次设备号 一般为0
count:要分配的设备数量
name:设备名

//指定一个主设备号和一个起始从设备号,如果成功返回0 失败返回错误码
dev_t first = MKDEV(int major, int minor);
int register_chrdev_region(dev_t first, unsigned int count, char *name);
First :要分配的设备编号范围的初始值, 这组连续设备号的起始设备号, 相当于register_chrdev()中主设备号
Count:连续编号范围.   是这组设备号的大小(也是次设备号的个数)
Name:编号相关联的设备名称. (/proc/devices); 本组设备的驱动名称

//注销设备
void unregister_chrdev_region(dev_t from,unsigned count)

//初始化cdev变量,并设置fops
void cdev_init(struct cdev *cdev, const struct file_operations *fops)

//添加cdev到linux内核,完成驱动注册
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
dev:申请好的主设备号+起始从设备号
count:为该驱动所占用从设备号的数目

//从内核中删除cdev数据
void cdev_del(struct cdev *p)

demo

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
/*声明class_create 和device_create相关信息*/
#include <linux/device.h>
//cdev用到的头文件
#include <linux/cdev.h>

#define DEMO_DEBUG
#ifdef  DEMO_DEBUG
#define dem_dbg(fmt, arg...)        printk(KERN_WARNING fmt, ##arg)
#else
#define dem_dbg(fmt, arg...)        printk(KERN_DEBUG fmt, ##arg)
#endif

#define DEVICE_COUNT        1

//1 定义cdev结构体变量和dev_nr主从设备号变量
static struct cdev demo_cdev;
static dev_t dev_nr;

static int demo_open (struct inode *pnode, struct file *filp)
{
    dem_dbg("==>%s  major: %d  minor: %d\n",
            __FUNCTION__, imajor(pnode), iminor(pnode));
    return 0;
}

static ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
    unsigned char ary[100] = "read successfully\n";
    unsigned long len = min(count, sizeof(ary));
    int retval;

    printk("==>%s  major: %d  minor: %d\n",
            __FUNCTION__, imajor(filp->f_dentry->d_inode), 
            iminor(filp->f_dentry->d_inode));

    if(copy_to_user(buf, ary, len) != 0){
        retval = -EFAULT;
        goto cp_err;
    }

    return len;
cp_err:
    return retval;      
}

static int demo_release (struct inode *pnode, struct file *filp)
{
    dem_dbg("==>%s  major: %d  minor: %d\n",
            __FUNCTION__, imajor(pnode), iminor(pnode));
    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = demo_read,
    .open = demo_open,
    .release = demo_release,
};  

static struct class *demo_class;
static int __init demo_init(void)
{   
    int res, i;
    struct device *demo_device;

    dem_dbg("==>demo_init\n");

    //2.1 动态申请主从设备号
    res = alloc_chrdev_region(&dev_nr, 0, DEVICE_COUNT, "demo_chrdev");
    //2.2 指定主从设备号
    //dev_nr = MKDEV(200, 0);
    //res = register_chrdev_region(dev_nr, DEVICE_COUNT, "demo_cdev");
    if(res){
        dem_dbg("==>alloc chrdev region failed!\n");
        goto chrdev_err;
    } 

    //3 初始化cdev数据
    cdev_init(&demo_cdev, &fops);

    //4 添加cdev变量到内核,完成驱动注册
    res = cdev_add(&demo_cdev, dev_nr, DEVICE_COUNT);
    if(res){
        dem_dbg("==>cdev add failed!\n");
        goto cdev_err;
    }

    //创建设备类
    demo_class = class_create(THIS_MODULE,"demo_class");
    if(IS_ERR(demo_class)){
        res =  PTR_ERR(demo_class);
        goto class_err;
    }

    for(i=0; i<DEVICE_COUNT; i++){
        //创建设备节点
        demo_device = device_create(demo_class,NULL, MKDEV(MAJOR(dev_nr), i), NULL,"demo%d",i);
        if(IS_ERR(demo_device)){
            res = PTR_ERR(demo_device);
            goto device_err;
        }
    }
    return 0;

device_err:
    while(i--)
        device_destroy(demo_class,MKDEV(MAJOR(dev_nr), i));
    class_destroy(demo_class);

class_err:
    cdev_del(&demo_cdev);    

cdev_err:
    unregister_chrdev_region(dev_nr, DEVICE_COUNT);

chrdev_err:
    //申请主设备号失败

    return res;
}

static void __exit demo_exit(void)
{
    int i;
    dem_dbg("==>demo_exit\n");

    //5 删除添加的cdev结构体,并释放申请的主从设备号
    cdev_del(&demo_cdev);    
    unregister_chrdev_region(dev_nr, DEVICE_COUNT);

    for(i=0; i<DEVICE_COUNT; i++)
        device_destroy(demo_class,MKDEV(MAJOR(dev_nr), i));
    class_destroy(demo_class);
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("Dual BSD/GPL");
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值