Linuxドライバー学習用のalloc_chrdev_regionのソースコード分析(2):__register_chrdev_region

Linuxドライバー学習用のalloc_chrdev_regionのソースコード分析(2):__register_chrdev_region

/*
major     : 主设备号
baseminor : 次设备号
minorct   : 次设备号数目
name      : 名称(标签)
*/
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;
        //在内核中申请内存空间,如果失败返回ERR_PTR(-ENOMEM)错误,ERR_PRT附:A.1
        // ENOMEM      Kernel memory allocation error
        cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
        if (cd == NULL)
                return ERR_PTR(-ENOMEM);

        mutex_lock(&chrdevs_lock);

        /* temporary */
        /*
          当形参中主设备号为0时,分配主设备号方式
        */
        if (major == 0) {
    
    
                for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
    
      //chardevs实现,见附A.2,
                        if (chrdevs[i] == NULL)   //当数组对应的下标元素值为空时,表示可以分配空间
                                 break;
                }

                if (i == 0) {
    
    //当主设备号都用完时执行如下错误
                        ret = -EBUSY;
                        goto out;
                }
                major = i;
                ret = major;
        }
        // struct char_device_struct *cd    设置对应的主设备号、次设备号、次设备号个数、名称(标签)
        cd->major = major;
        cd->baseminor = baseminor;
        cd->minorct = minorct;
        strlcpy(cd->name, name, sizeof(cd->name)); //函数实现见附A.4
        //以下代码主要用于处理当第一个形参不是 0 时,且当major大于CHRDEV_MAJOR_HASH_SIZE时,可能产生的错误
        i = major_to_index(major);  //用主设备号(major)和 CHRDEV_MAJOR_HASH_SIZE取模,实现代码见附:A.3        
        for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)     //查找下一个字符设备位置
                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;  // 当分配成功,返回cd,即:cd包含:内核分配的主设备号,形参中的次设备号、次设备号数量、名字(标签)
        
out:
        mutex_unlock(&chrdevs_lock);
        kfree(cd);
        return ERR_PTR(ret);
}



添付資料A.1

//功能实现:include/linux/err.h 
 static inline void * __must_check ERR_PTR(long error)
{
    
    
        return (void *) error;
}

付録A.2

//功能实现:fs/char_dev.c
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];
//宏定义位置:CHRDEV_MAJOR_HASH_SIZE
//linux/fs.h

添付資料A.3

//功能实现:fs/char_dev.c
/* index in the above */
static inline int major_to_index(unsigned major)
{
    
    
        return major % CHRDEV_MAJOR_HASH_SIZE;
} 

添付資料A.4

函数声明位置:include/linux/string.h
#include <linux/string.h>
/*
des  : 字符串的目的位置
src  :字符串的源位置
size : 拷贝的字节数
返回值 :没有意义,见实现代友:
*/
size_t strlcpy(char *des, const char *src, size_t size); //用法同strncpy();
//实现代码:
//vim lib/string.c 
size_t strlcpy(char *dest, const char *src, size_t size)
{
    
    
        size_t ret = strlen(src);  //获取源字符串大小

        if (size) {
    
    //当拷贝数据大于0时执行拷贝
        //当源字符串长度大于或等于要拷贝的字符串长度时:len = size -1,否则len  = ret
                size_t len = (ret >= size) ? size - 1 : ret;
                memcpy(dest, src, len);//见附B.1
                dest[len] = '\0';//添加字符串结束符,strlen计算时只计算了有效字符,没有计算结束标志'\0'
        }
        return ret;//无论结果如何,均返回源字符串长度,所以返回值没有意义
}
EXPORT_SYMBOL(strlcpy);

付録B.1

//memcpy(3)
#include <string.h>
 void *memcpy(void *dest, const void *src, size_t n);
 //The memcpy() function returns a pointer to dest.

おすすめ

転載: blog.csdn.net/weixin_47273317/article/details/107793428