linux字符设备

linux有一个全局的结构体数组,共255个元素,记录系统中的设备节点。
主设备号相同,次设备号不同的设备组成链表。
参考:http://edsionte.com/techblog/archives/1393

注册一个字符设备调用函数
register_chrdev(major, DEV_NAME, &fops) static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) { return __register_chrdev(major, 0, 256, name, fops); } //申请次设备号最小从0开始,申请256个次设备。 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; //从全局的字符设备结构体数组中获取可以使用的结构体char_device_struct 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; } 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) if ((*cp)->major > major || ((*cp)->major == major && (((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor)))) break;    //检查次设备号的范围是否有重叠,但是这个函数一开始就已经指定了此设备0-255(256个),
   //所以肯定会重叠,这就要求,主设备号不能与已经存在的字符设备重叠,否则注册设备必然会失败
   //其他的函数范围可能更小,就需要下面进行判断
/* 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); }

另一种注册字符设备函数,过程与上面的类似。
struct cdev cdev;
dev_t devno;
int __init test_init(void)
{
    int ret;
  //静态分配
  //指定主设备号和次设备号,并设备好的范围是1
  //dev_t devno = MKDEV(globalmem_major, 0);
  //ret = register_chrdev_region(&devno, 1, DEV_NAME);  
  //动态分配
    ret = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
    if(ret)
    {   
        printk("alloc_chrdev_region failed!\n");
        return -EAGAIN;
    }   
    else
        printk("major = %d\n", MAJOR(devno));

    cdev_init(&cdev, &fops);
    cdev.owner = THIS_MODULE;

    ret = cdev_add(&cdev, devno, 1);
    if(ret)
    {   
        //释放之前分配的设备号
        unregister_chrdev_region(devno, 1);
        printk("cdev_add failed!\n");
    }   

    return ret;
}

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


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

结构体关系如下所示

linux字符设备_第1张图片

你可能感兴趣的:(linux字符设备)