Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

linux驱动开发之字符设备框架-cdev

$
0
0

前言

字符设备是linux驱动中最基本的一种设备类型。这里介绍linux系统中,字符设备的框架。 主要的内容是cdev结构体和file_operation函数操作集

正文

cdev结构体

在linux中,使用cdev来描述一个字符设备。
struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

在上述的 cdev 结构体中,有两个很重要的成员,分别是 file_operations 和 dev_t 。

首先来说一下这个 dev,这里的dev是用来保存申请的设备号的。
dev_t 是一个 32 位类型的变量,它的高 12 位为主设备号,低设备号为次设备号。可以使用 kdev_t.h中的 MAJOR(dev)和MINOR(dev) 两个宏从设备号中获得主设备号和次设备号;也可以通过 MKDEV(ma,mi) 由主设备号和次设备号合成设备号。

另一个结构体成员是一个函数操作集,他是字符设备的主体。

内核中提供了一组函数用来操作cdev结构体

void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

cdev_alloc()函数用于动态的申请一个cdev内存。

/**
 * cdev_alloc() - allocate a cdev structure
 *
 * Allocates and returns a cdev structure, or NULL on failure.
 */
struct cdev *cdev_alloc(void)
{
    struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    if (p) {
        INIT_LIST_HEAD(&p->list);
        kobject_init(&p->kobj, &ktype_cdev_dynamic);
    }
    return p;
}

对申请到的cdev结构体,比如需要进行初始化的操作。

/**
 * cdev_init() - initialize a cdev structure
 * @cdev: the structure to initialize
 * @fops: the file_operations for this device
 *
 * Initializes @cdev, remembering @fops, making it ready to add to the
 * system with cdev_add().
 */
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev);
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj, &ktype_cdev_default);
    cdev->ops = fops;
}

cdev_init() 函数,主要的是将给cdev 的成员 ops 进行 赋值操作。
这样,以后我们的操作,可以通过找到cdev,调用cdev的 ops中的各种方法了。

初始操作完善后,需要将cdev添加到一个全局的 cdev_map 中。在这之前,还需要为字符设备申请一个设备号。 通过 alloc_chrdev_region()得到设备号

/**
 * alloc_chrdev_region() - register a range of char device numbers
 * @dev: output parameter for first assigned number
 * @baseminor: first of the requested range of minor numbers
 * @count: the number of minor numbers required
 * @name: the name of the associated device or driver
 *
 * Allocates a range of char device numbers.  The major number will be
 * chosen dynamically, and returned (along with the first minor number)
 * in @dev.  Returns zero or a negative error code.
 */
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name)
{
    struct char_device_struct *cd;
    cd = __register_chrdev_region(0, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    *dev = MKDEV(cd->major, cd->baseminor);
    return 0;
}

主设备号由系统进行分配,需要提供申请的此设备号是多少。连续申请几个设备,还有申请设备的名字。
如果已知设备号,可以使用 int register_chrdev_region(dev_t from, unsigned count, const char *name) 进行注册设备

在申请完设备号之后,需要将 cdev 添加到系统中。

/**
 * cdev_add() - add a char device to the system
 * @p: the cdev structure for the device
 * @dev: the first device number for which this device is responsible
 * @count: the number of consecutive minor numbers corresponding to this
 *         device
 *
 * cdev_add() adds the device represented by @p to the system, making it
 * live immediately.  A negative error code is returned on failure.
 */
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;
    p->count = count;
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

字符设备的注册过程基本就是这样,需要 设备号 对cdev的操作,还有 ops函数操作集。

有申请,有注册的过程,必然有释放、撤销的过程
对设备号的释放

/**
 * unregister_chrdev_region() - return a range of device numbers
 * @from: the first in the range of numbers to unregister
 * @count: the number of device numbers to unregister
 *
 * This function will unregister a range of @count device numbers,
 * starting with @from.  The caller should normally be the one who
 * allocated those numbers in the first place...
 */
void unregister_chrdev_region(dev_t from, unsigned count)
{
    dev_t to = from + count;
    dev_t n, next;

    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        if (next > to)
            next = to;
        kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
    }
}

对cdev的操作


/**
 * cdev_del() - remove a cdev from the system
 * @p: the cdev structure to be removed
 *
 * cdev_del() removes @p from the system, possibly freeing the structure
 * itself.
 */
void cdev_del(struct cdev *p)
{
    cdev_unmap(p->dev, p->count);
    kobject_put(&p->kobj);
}

这里介绍了 cdev 相关的一些操作函数。 下一节在介绍file_operations 函数操作集的相关内容。

总结

字符设备框架就是围绕着 cdev 来进行的一些操作。
注册的过程,要申请cdev结构体 初始化cdev 申请设备号 注册cdev
注销的过程和注册的过程顺序想法,先释放申请的设备号,后释放cdev

作者:u013377887 发表于2017/5/4 21:04:06 原文链接
阅读:86 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles