字符设备的申请和注册

设备号设计原则

 一般一个主设备号的一段连续子设备号共用一个驱动,称为cdev实例,所以有主设备号和子设备号之分

struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;  // 主设备号 + 第一个子设备号
    unsigned int count; // 子设备号数量
} __randomize_layout;

 

一个cdev实例共用一个 struct file_operation

设备号的申请

god@god-virtual-machine:/proc/bus/input$ cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  5 ttyprintk
  6 lp
  7 vcs
 10 misc
 13 input
......
252 ndctl
253 tpm
254 gpiochip

Block devices:
  7 loop
  8 sd
......
135 sd
253 device-mapper
254 mdp
259 blkext

由上可知,需要设备号的设备分为字符设备和块设备,数字表示主设备号

以下介绍的,无论是设备号,还是设备注册,都是指字符设备。

 

以下函数功能,包含了设备号的申请、字符设备的申请、字符设备添加到内核的全局变量

static inline int register_chrdev(unsigned int major, const char *name,
                  const struct file_operations *fops)
{
    return __register_chrdev(major, 0, 256, name, fops);
}

以下函数,只包含设备号的申请,可以跨主设备号。需要指定主设备号,所以属于静态申请设备号。from是起始设备号,由12位主设备号 + 20子设备号组成32bit值,count是申请设备号的数量

int register_chrdev_region(dev_t from, unsigned count, const char *name)

 以下函数,动态申请设备号,申请成功,设备号记录在入参的dev

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

以下函数,只包含设备号的申请,不能跨主设备号,alloc_chrdev_region()、register_chrdev() 和 register_chrdev_region() 最终都是调用此函数进行设备号的申请

major为0表示动态申请

static struct char_device_struct *__register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)

 

register_chrdev
设备号的申请,字符设备的申请、字符设备添加到内核的全局变量
register_chrdev_region
设备号的申请(函数名具有迷惑性,没有字符设备添加到内核功能)
alloc_chrdev_region
 设备号的申请
__register_chrdev_region
设备号的申请(函数名具有迷惑性,没有字符设备添加到内核功能)

 

 

static struct char_device_struct *__register_chrdev_region(unsigned int major, unsigned int baseminor,
               int minorct, const char *name)
{
    struct char_device_struct *cd, *curr, *prev = NULL;
    int ret;
    int i;

    if (major >= CHRDEV_MAJOR_MAX) {
        pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
               name, major, CHRDEV_MAJOR_MAX-1);
        return ERR_PTR(-EINVAL);
    }

    if (minorct > MINORMASK + 1 - baseminor) {
        pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n",
            name, baseminor, baseminor + minorct - 1, 0, MINORMASK);
        return ERR_PTR(-EINVAL);
    }

    cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
    if (cd == NULL)
        return ERR_PTR(-ENOMEM);

    mutex_lock(&chrdevs_lock);

    if (major == 0) {
        ret = find_dynamic_major();
        if (ret < 0) {
            pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",
                   name);
            goto out;
        }
        major = ret;
    }

    ret = -EBUSY;
    i = major_to_index(major);
    for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) {
        if (curr->major < major)
            continue;

        if (curr->major > major)
            break;

        if (curr->baseminor + curr->minorct <= baseminor)
            continue;

        if (curr->baseminor >= baseminor + minorct)
            break;

        goto out;
    }

    cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;
    strlcpy(cd->name, name, sizeof(cd->name));

    if (!prev) {
        cd->next = curr;
        chrdevs[i] = cd;
    } else {
        cd->next = prev->next;
        prev->next = cd;
    }

    mutex_unlock(&chrdevs_lock);
    return cd;
out:
    mutex_unlock(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}

由此函数可知,主设备号虽然有12位,但是主设备号的范围只支持 0~511,子设备号则占满20bit。

传入的major主设备号是否为0,如果为0 则表明为动态申请内存,则调用find_dynamic_major()申请一个没有使用的主设备号。

由于主设备号是0~511,而chrdevs数组的容量是255,所以把每个数组成员都设计为链表,即每个数组成员可以对应多个主设备号

 

 上图是没有考虑子设备号情况,考虑到子设备号,实际的数据结构如下,每一个框都对应一个 struct char_device_struct 实例。

 

 

static int find_dynamic_major(void)
{
    int i;
    struct char_device_struct *cd;

    for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) {
        if (chrdevs[i] == NULL)
            return i;
    }

    for (i = CHRDEV_MAJOR_DYN_EXT_START; i >= CHRDEV_MAJOR_DYN_EXT_END; i--) {
        for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next)
            if (cd->major == i) // i这个主设备号已经被占了
                break;

        if (cd == NULL) // i这个设备号没有被占
            return i;
    }

    return -EBUSY;
}

先看 254 ~ 234,是否有未被占用的主设备号;再看 511 ~ 384。由此可知,动态申请的设备号只能在这两个范围内,其他用于静态分配

 

字符设备添加到内核的全局变量cdev_map

函数调用关系:

register_chrdev()
  __register_chrdev()
    __register_chrdev_region() 申请设备号
    cdev_alloc()     字符设备申请
    cdev_add()      字符设备添加到内核的全局变量
 
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
{
    return __register_chrdev(major, 0, 256, name, fops);
}

 

int __register_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name, const struct file_operations *fops)
{
    struct char_device_struct *cd;
    struct cdev *cdev;
    int err = -ENOMEM;

    cd = __register_chrdev_region(major, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);

    cdev = cdev_alloc();
    if (!cdev)
        goto out2;

    cdev->owner = fops->owner;
    cdev->ops = fops;
    kobject_set_name(&cdev->kobj, "%s", name);

    err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
    if (err)
        goto out;

    cd->cdev = cdev;

    return major ? 0 : cd->major;
out:
    kobject_put(&cdev->kobj);
out2:
    kfree(__unregister_chrdev_region(cd->major, baseminor, count));
    return err;
}

 

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

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

    if (WARN_ON(dev == WHITEOUT_DEV))
        return -EBUSY;

    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实例对应一个主设备号的多个连续子设备,每个cdev实例都会存储在全局变量cdev_map中,类型为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;
};

 

void __init chrdev_init(void)
{
    cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
}

 

struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
{
    struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
    struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
    int i;

    if ((p == NULL) || (base == NULL)) {
        kfree(p);
        kfree(base);
        return NULL;
    }

    base->dev = 1;
    base->range = ~0;
    base->get = base_probe;
    for (i = 0; i < 255; i++)
        p->probes[i] = base;
    p->lock = lock;
    return p;
}

全局变量cdev_map内有一个 struct probe*类型的数组,容量为255,每个数组成员都是一个链表,使用起来和数组chrdevs一样

int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, struct module *module, kobj_probe_t *probe, int (*lock)(dev_t, void *), void *data)
{
    unsigned int n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
    unsigned int index = MAJOR(dev);
    unsigned int i;
    struct probe *p;

    if (n > 255)
        n = 255;

    p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);
    if (p == NULL)
        return -ENOMEM;

    for (i = 0; i < n; i++, p++) {
        p->owner = module;
        p->get = probe;
        p->lock = lock;
        p->dev = dev;
        p->range = range;
        p->data = data;
    }
    mutex_lock(domain->lock);
    for (i = 0, p -= n; i < n; i++, p++, index++) {
        struct probe **s = &domain->probes[index % 255];
        while (*s && (*s)->range < range)
            s = &(*s)->next;
        p->next = *s;
        *s = p;
    }
    mutex_unlock(domain->lock);
    return 0;
}

 

posted @ 2023-03-17 22:53  流水灯  阅读(85)  评论(0编辑  收藏  举报