chrdev与cdev函数分析

                        设备表结构与源码分析
一。设备结构体
#define CHRDEV_MAJOR_HASH_SIZE    255
static DEFINE_MUTEX(chrdevs_lock);

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];      //只能挂255个字符主设备
二。申请设备号操作函数源码分析
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
               int minorct, const char *name)
{
    struct char_device_struct *cd, **cp;
    int ret = 0;
    int i;

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

    mutex_lock(&chrdevs_lock);                   //互斥访问

    /* temporary */
    if (major == 0) {                           //如果主设备号为0,则系统自动分配一个没有使用的设备号
        for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
            if (chrdevs[i] == NULL)
                break;
        }

        if (i == 0) {
            ret = -EBUSY;
            goto out;
        }
        major = i;
        ret = major;
    }

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

    i = major_to_index(major); /获得主设备号

    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)        //找到比NEW的第一个大的结构体指针
        if ((*cp)->major > major ||
            ((*cp)->major == major &&
             (((*cp)->baseminor >= baseminor) ||
              ((*cp)->baseminor + (*cp)->minorct > baseminor))))
            break;

    /* Check for overlapping minor ranges.  */
    if (*cp && (*cp)->major == major) {
        int old_min = (*cp)->baseminor;                //判断两个是否有得叠
        int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
        int new_min = baseminor;
        int new_max = baseminor + minorct - 1;

        /* New driver overlaps from the left.  */
        if (new_max >= old_min && new_max <= old_max) {
            ret = -EBUSY;
            goto out;
        }

        /* New driver overlaps from the right.  */
        if (new_min <= old_max && new_min >= old_min) {
            ret = -EBUSY;
            goto out;
        }
    }

    cd->next = *cp;         //插入
    *cp = cd;
    mutex_unlock(&chrdevs_lock);
    return cd;
out:
    mutex_unlock(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}
================================================
static struct char_device_struct *
__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
{
    struct char_device_struct *cd = NULL, **cp;
    int i = major_to_index(major);

    mutex_lock(&chrdevs_lock);
    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
        if ((*cp)->major == major &&
            (*cp)->baseminor == baseminor &&
            (*cp)->minorct == minorct)
            break;
    if (*cp) {
        cd = *cp;
        *cp = cd->next;
    }
    mutex_unlock(&chrdevs_lock);
    return cd;
}

int register_chrdev_region(dev_t from, unsigned count, const char *name) //from:设备号  count:次设备个数  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);
}
=============================================================
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);            //主设备号为0。系统自动分配一个
    if (IS_ERR(cd))
        return PTR_ERR(cd);                                          //如果失败,返回一个错误号
    *dev = MKDEV(cd->major, cd->baseminor);                               //如果成功返回设备号
    return 0;
}
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的初始化与添加与操作的连接的源码分析
struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev);               //初始化cdev
    INIT_LIST_HEAD(&cdev->list);            //初始化链表
    kobject_init(&cdev->kobj, &ktype_cdev_default);     //后期分析
    cdev->ops = fops;                                //将文件操作与cdev连接起来
}

struct cdev *cdev_alloc(void)
{
    struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);     //分配一个cdev结构内存
    if (p) {
        INIT_LIST_HEAD(&p->list);                //初始化一个链表
        kobject_init(&p->kobj, &ktype_cdev_dynamic);        //后期分析
    }
    return p;
}

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;                   //初始化cdev
    p->count = count;
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);    //后期分析
}
===========================================================
void cdev_del(struct cdev *p)                       //删除cdev结构体
{
    cdev_unmap(p->dev, p->count);
    kobject_put(&p->kobj);                   //减少引用计数

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值