设为首页 加入收藏

TOP

linux学习--字符设备驱动(一)
2019-09-02 23:50:07 】 浏览:30
Tags:linux 学习 字符 设备驱动

linux驱动有基本的接口进行注册和卸载,这里不再做详细说明,本文主要关注linux字符设备驱动框架实现的细节。

1.字符设备驱动抽象结构

字符设备驱动管理的核心对象是字符设备,从字符设备驱动程序的设计框架出发,内核为字符设备抽象出数据结构struct cdev,定义如下:

include/linux/cdev.h
struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

设备驱动程序中可以有两种方式来产生struct cdev对象,一种是静态定义的方式,另一种是在程序的执行期通过动态分配。而一个struct cdev对象在被加入系统前,应该被初始化,可以通过cdev_init接口完成,具体实现如下:

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

2.设备号及设备节点

由于struct cdev在添加到系统时需要设备号和设备节点的知识,所以此处先做个简介。

2.1 设备号分配与管理

linux系统中设备号由主设备号和次设备号组成,内核使用主设备号来定位设备驱动程序,用次设备号来管理同类设备。使用dev_t类型变量标记设备号,具体定义如下:

include/linux/types.h 
typedef __u32 __kernel_dev_t; 
typedef __kernel_dev_t dev_t;

在当前版本内核,dev_t的低20位表示次设备号,高12位表示主设备号,具体定义如下:

include/linux/kdev_t.h
#define MINORBITS20
#define MINORMASK ((1U << MINORBITS) - 1)

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

字符设备涉及到设备号分配的内核函数

fs/char_dev.c
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
    struct char_device_struct *cd;
    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;
        cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name);
        if (IS_ERR(cd))
            goto fail;
    }

    return 0;
fail:
    to = n;
    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
    }
    return PTR_ERR(cd);
}

该函数第一个参数表示设备号,第二个参数表示连续设备编号个数,即驱动管理的同类设备个数,第三个参数表示设备的名称,该函数的核心是__register_chrdev_region,讨论该函数前需要先看下全局指针数组chrdevs,这个数组每一项都指向一个struct char_device_struct结构,具体定义如下:

static struct char_device_struct
{
    struct char_device_struct *next;
    unsigned int major;
    unsigned int baseminor;
    int minorct;
    char name[64];
    struct cdev *cdev;/* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

__register_chrdev_region将当前设备驱动程序使用的设备号记录到这个数组中,首先分配一个struct char_device_struct的对象,然后对其初始化,完成后哈希遍历char devs并将这个对象添加到数组中。 在对字符设备初始化后,需要将该设备添加到内核当中,可以查看cdev_add函数定义。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    int error;

    p->dev = dev;
    p->count = count;

    error = kobj_map(cdev_map, dev, count, NULL,exact_match, exact_lock, p);
    if (error)
        return error;
    kobject_get(p->kobj.parent);

return 0;
}

cdev_add函数的核心是通过kobj_map实现,后者通过操作一个全局变量cdev_map把设备加入到哈希表中,cdev_map定义如下: static struct kobj_map *cdev_map;

struct kobj_map {
    struct probe {  
        struct probe *next;
        dev_t dev;
        unsigned long range;
        struct module *owner;
        kobj_probe_t *get;
        int (*lock)(dev_t, void *);
        void *data;
    } *probes[255];
    struct mutex *lock;
}; 

简单的说,设备驱动程序通过调用cdev_add把它所管理的设备对象指针嵌入到一个struct probe节点对象中,然后再把该节点加入到cdev_map的哈希链表中。

2.2 设备节点的生成

设备节点的创建可以使用mknod指令在/dev目录下进行创建,例如mknod /dev/demodev c 2 0,如果该指令成功执行,将会在/dev目录下生成一个demodev的字符设备节点。

而mknod实际上是通过系统调用sys_mknod进入内核空间,函数的原型asmlinkage long sys_mknod(const char __user *filename, umo

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇nginx重新安装 引起的问题 下一篇解决git push时发现有超过100M的..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目