• 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! ");
            return -EAGAIN;
        }   
        else
            printk("major = %d ", 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! ");
        }   

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

    结构体关系如下所示

  • 相关阅读:
    C++操作文件行(读取,删除,修改指定行)
    Windows注册表中修改UAC(用户账号控制)及批处理脚本
    Centos7.x 安装libevent2.x
    【Docker】:使用docker安装redis,挂载外部配置和数据
    【Docker】:使用docker安装mysql,挂载外部配置和数据
    开源定时任务框架Quartz(二)
    开源定时任务框架Quartz(一)
    Spring Boot系列教程十四:Spring boot同时支持HTTP和HTTPS
    数据结构与算法:单向链表实现与封装(有头)
    【C++札记】指针函数与函数指针
  • 原文地址:https://www.cnblogs.com/helloworldtoyou/p/5128014.html
Copyright © 2020-2023  润新知