zoukankan      html  css  js  c++  java
  • register_chrdev_region

     1 /**  
     2  * register_chrdev_region() - register a range of device numbers
     3  * @from: the first in the desired range of device numbers; must include
     4  *        the major number.
     5  * @count: the number of consecutive device numbers required
     6  * @name: the name of the device or driver.
     7  *
     8  * Return value is zero on success, a negative error code on failure.
     9  */
    10 
    11 /*register_chrdev_region函数的功能是在已知起始设备号的情况下去申请一组连续的设备号*/
    12 int register_chrdev_region(dev_t from, unsigned count, const char *name)
    13 {
    14     struct char_device_struct *cd;
    15     dev_t to = from + count;
    16     dev_t n, next;
    17 
    18     for (n = from; n < to; n = next) {  /*每次申请256个设备号*/
    19         next = MKDEV(MAJOR(n)+1, 0); /*先得到下一个设备号(其实也是一个设备号,只不过此时的次设备号为0)并存储于next中*/
    20         if (next > to)  /*溢出的情况*/  /*判断在from的基础上再追加count个设备(dev_t to = from+count)是否已经溢出到下一个主设备号*/
    21             next = to;
    22         /*
    23             如果没有溢出(next小于to),那么整个for语句就只执行个一次__register_chrdev_region函数;
    24             否则当设备号溢出时,会把当前溢出的设备号范围划分为几个小范围,分别调用__register_chrdev_region函数。
    25         */
    26         cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name);  /*核心函数*/
    27         if (IS_ERR(cd))         /*判断错误码*/
    28             goto fail;
    29     }
    30     return 0;
    31 fail:           /*当任何一次分配失败的时候,释放所有已经申请的设备号*/
    32     to = n;     /*此时n=已经注册好的设备号*/
    33     for (n = from; n < to; n = next) {
    34         next = MKDEV(MAJOR(n)+1, 0);
    35         kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));    /*核心函数*/
    36     }
    37     return PTR_ERR(cd);
    38 }

     

    1 MKDEV(ma,mi)

     #define MKDEV(ma,mi) ((ma)<<8 | (mi))  

    所以MKDEV(5, 0),最后得到的值为【5*(2的8次幂)+0】,等于1280。

    2.dev_t

    typedef __u32 __kernel_dev_t;
    typedef __kernel_dev_t    dev_t;

    3.MAJOR 和 MINOR

    #define MAJOR(dev)    ((dev)>>8)
    #define MINOR(dev)    ((dev) & 0xff)

     



    4.major_to_index

    static inline int major_to_index(int major)
    {
        return major % MAX_PROBE_HASH;//MAX_PROBE_HASH=255
    }

     



    5.ERR_PTR,通过指针的方式传递错误码

    linux/err.h
    /*
     * Kernel pointers have redundant information, so we can use a
     * scheme where we can return either an error code or a dentry
     * pointer with the same return value.
     *
     * This should be a per-architecture thing, to allow different
     * error and pointer decisions.
     */
    static inline void *ERR_PTR(long error)
    {
        return (void *) error;
    }

     



    6.IS_ERR 判断错误码

    static inline long IS_ERR(const void *ptr)
    {
        return unlikely((unsigned long)ptr > (unsigned long)-1000L);
    }
     1 1.struct char_device_struct结构
     2 fs/char_dev.c
     3 static struct char_device_struct {
     4     struct char_device_struct *next; // 指向散列冲突链表中的下一个元素的指针
     5     unsigned int major;         // 主设备号
     6     unsigned int baseminor;       // 起始次设备号
     7     int minorct;             // 设备编号的范围大小
     8     const char *name;          // 处理该设备编号范围内的设备驱动的名称
     9     struct file_operations *fops;   // 没有使用
    10     struct cdev *cdev;            /* will die指向字符设备驱动程序描述符的指针*/
    11 } *chrdevs[MAX_PROBE_HASH];
    12 
    13 2.__register_chrdev_region
    14 /*
    15  *例子:__register_chrdev_region(5, 0, 256, “ville”);
    16  *
    17  * Register a single major with a specified minor range.
    18  *
    19  * If major == 0 this functions will dynamically allocate a major and return
    20  * its number.
    21  *
    22  * If major > 0 this function will attempt to reserve the passed range of
    23  * minors and will return zero on success.
    24  *
    25  * Returns a -ve errno on failure.
    26  */
    27 static struct char_device_struct *
    28 __register_chrdev_region(unsigned int major/*5*/, unsigned int baseminor/*0*/,
    29                int minorct/*256*/, const char *name/*ville*/)
    30 {
    31     struct char_device_struct *cd, **cp;
    32     int i;
    33     int ret = 0;
    34 
    35     cd = kmalloc(sizeof(struct char_device_struct), GFP_KERNEL);/*slab分配一个char_device_struct变量*/
    36     if (cd == NULL)
    37         return ERR_PTR(-ENOMEM);
    38 
    39     memset(cd, 0, sizeof(struct char_device_struct));/*将刚刚分配的变量的内存区清零*/
    40 
    41     write_lock_irq(&chrdevs_lock);/*关中断,禁止内核抢占+读写锁*/
    42 
    43     /* temporary */
    44     if (major == 0) {/*分配一个主设备号!*/
    45         for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
    46             if (chrdevs[i] == NULL)
    47                 break;
    48         }
    49 
    50         if (i == 0) {
    51             ret = -EBUSY;
    52             goto out;
    53         }
    54         major = i;
    55         ret = major;
    56     }
    57 
    58     cd->major = major;
    59     cd->baseminor = baseminor;
    60     cd->minorct = minorct;/*申请设备号的个数*/
    61     cd->name = name;
    62     /****************以上为第一部分,处理char_device_struct变量的分配和初始化************/
    63     /****************以下为第二部分,将char_device_struct变量注册到内核*****************/
    64     i = major_to_index(major);/*将major对256取余数,得到可以存放char_device_struct在chrdevs中的索引*/
    65 
    66     /*
    67      *退出循环:
    68          (1)chrdevs[i]为空
    69          (2)chrdevs[i]的主设备号大于major
    70          (3)chrdevs[i]的主设备号等于major,但是次设备号大于等于baseminor
    71      */
    72     for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
    73         if ((*cp)->major > major ||
    74             ((*cp)->major == major && (*cp)->baseminor >= baseminor))
    75             break;
    76     /*
    77      *如果*cp不空,并且*cp的major与要申请的major相同,
    78      *此时,如果(*cp)->baseminor < baseminor + minorct,就会发生冲突
    79      *因为和已经分配了的设备号冲突了。
    80      *出错!
    81      */
    82     if (*cp && (*cp)->major == major &&
    83         (*cp)->baseminor < baseminor + minorct) {
    84         ret = -EBUSY;
    85         goto out;
    86     }
    87     
    88     /*
    89      *所要申请的设备号可以满足
    90      */
    91     cd->next = *cp;/*按照主设备号从小到达的顺序排列*/
    92     *cp = cd;
    93     write_unlock_irq(&chrdevs_lock);
    94     return cd;
    95 out:
    96     write_unlock_irq(&chrdevs_lock);
    97     kfree(cd);
    98     return ERR_PTR(ret);
    99 }
    总结:
    大体上分为两个步骤:
        1.char_device_struct类型变量的分配以及初始化
        2.将char_device_struct变量注册到内核
    
    1.char_device_struct类型变量的分配以及初始化
        (1)首先,调用 kmalloc 分配一个 char_device_struct 变量cd。
            检查返回值,进行错误处理。
        (2)将分配的char_device_struct变量的内存区清零memset。
        (3)获取chrdevs_lock读写锁,并且关闭中断,禁止内核抢占,write_lock_irq。
        (4)如果传入的主设备号major不为0,跳转到第(7)步。
        (5)这时,major为0,首先需要分配一个合适的主设备号。
            将 i 赋值成 ARRAY_SIZE(chrdevs)-1,其中的 chrdevs 是包含有256个char_device_struct *类型的数组,
            然后递减 i 的值,直到在chrdevs数组中出现 NULL。当chrdevs数组中不存在空值的时候,
            ret = -EBUSY;    goto out;
        (6)到达这里,就表明主设备号major已经有合法的值了,接着进行char_device_struct变量的初始化。
            设置major, baseminor, minorct以及name。
    2.将char_device_struct变量注册到内核
        (7)将 i 赋值成 major_to_index(major)
            将major对256取余数,得到可以存放char_device_struct在chrdevs中的索引
        (8)进入循环,在chrdevs[i]的链表中找到一个合适位置。
            退出循环的条件:
             (1)chrdevs[i]为空。
             (2)chrdevs[i]的主设备号大于major。
             (3)chrdevs[i]的主设备号等于major,但是次设备号大于等于baseminor。
             注意:cp = &(*cp)->next,cp是char_device_struct **类型,(*cp)->next是一个char_device_struct *
                 类型,所以&(*cp)->next,就得到一个char_device_struct **,并且这时候由于是指针,所以
                 对cp赋值,就相当于对链表中的元素的next字段进行操作。
        (9)进行冲突检查,因为退出循环的情况可能造成设备号冲突(产生交集)。
            如果*cp不空,并且*cp的major与要申请的major相同,此时,如果(*cp)->baseminor < baseminor + minorct,
            就会发生冲突,因为和已经分配了的设备号冲突了。出错就跳转到ret = -EBUSY; goto out;
            
        (10)到这里,内核可以满足设备号的申请,将cd链接到链表中。
        (11)释放chrdevs_lock读写锁,开中断,开内核抢占。
        (12)返回加入链表的char_device_struct变量cd。
        (13)out出错退出
            a.释放chrdevs_lock读写锁,开中断,开内核抢占。
            b.释放char_device_struct变量cd,kfree。
            c.返回错误信息    
    
    
    3.__unregister_chrdev_region
    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);
    
        write_lock_irq(&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;
        }
        write_unlock_irq(&chrdevs_lock);
        return cd;
    }
    总结:
        (1)注意只有major,baseminor和minorct全都相同,才能标示一个 char_device_struct 变量。
        (2)在__unregister_chrdev_region之中,并不释放内存。
    
    三.总结
    
    1.首先,内核使用char_device_struct 结构表示一次字符设备号的申请(针对__register_chrdev_region来说)。
        其中单纯对于字符设备号管理而言,最重要的就是
        struct char_device_struct *next; // 指向散列冲突链表中的下一个元素的指针
        unsigned int major; // 主设备号
        unsigned int baseminor;  // 起始次设备号
        int minorct; // 设备编号的范围大小
    2.内核对字符设备号的集中管理体现在chrdevs数组中,它也是一个散列表,使用chrdevs_lock读写锁进行保护,
      操作的时候,需要关闭中断,关闭内核抢占。
        散列函数:major_to_index(major),单纯的进行 major%256
        使用链表处理冲突。
    3.对于字符设备号申请,有可能出现,申请范围很大超出256的情况,这时候,就会在一次register_chrdev_region调用中,
        产生多个char_device_struct的情况。其中只有最后一个minorct小于等于256,其余的全都等于256。
    4.alloc_chrdev_region 动态的分配设备号
    char_dev.c
    a.参数:
        dev:
            仅仅作为输出参数,成功分配后将保存已分配的第一个设备编号。
        baseminor:
            被请求的第一个次设备号,通常是0。
        count:
            所要分配的设备号的个数。
        name:
            和所分配的设备号范围相对应的设备名称。
    b.返回值:
        成功返回0,失败返回负的错误编码。
    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;
    }        
        通过以上分析很好理解。
    
    5.unregister_chrdev_region释放字符设备号
    char_dev.c
    a.参数:
        from:
            所要释放的第一个设备号。
        count:
            释放设备号的个数。
    b.返回值:
    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));
        }
    }
        通过对__unregister_chrdev_region的分析,很好理解这个函数。 

     

  • 相关阅读:
    HTTP 错误 404.2
    SQL Server 2008 R2如何开启数据库的远程连接(转)
    CSS中font-family:中文字体对应的英文名称
    15/18位身份证号码正则表达式(详细版)
    C#获取系统时间及时间格式
    C#正则表达式判断输入日期格式是否正确
    Linux查看机器负载
    模拟HTTP请求超时时间设置
    MySQL show命令的用法
    innodb事务隔离级别
  • 原文地址:https://www.cnblogs.com/androidstudio/p/5296530.html
Copyright © 2011-2022 走看看