Linux字符设备驱动--No.3

时间:2023-03-10 00:31:26
Linux字符设备驱动--No.3

字符驱动(按键)初始化函数分析:

 int charDrvInit(void)
 {

     devNum = MKDEV(reg_major, reg_minor);

     printk(KERN_EMERG"devNum is %d\r\n", devNum);
     if(OK == register_chrdev_region(devNum, subDevNum, DEVICE_NAME))//subdevnum:要申请的第一个次设备号
     {
         printk(KERN_EMERG"register_chrdev_region ok\r\n");
     }
     else
     {
         printk(KERN_EMERG"register_chrdev_region error\r\n");
         return ERROR;
     }
16
 /**
  * register_chrdev_region() - register a range of device numbers
  * @from: the first in the desired range of device numbers; must include
  *        the major number.
  * @count: the number of consecutive device numbers required
  * @name: the name of the device or driver.
  *
  * Return value is zero on success, a negative error code on failure.
  */
 int register_chrdev_region(dev_t from, unsigned count, const char *name) //count = 1
 {
     struct char_device_struct *cd;
 static struct char_device_struct {
     struct char_device_struct *next;
     unsigned int major;
     unsigned int baseminor;
     int minorct;
     ];
     struct cdev *cdev;        /* will die */
 } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

struct char_device_struct

     dev_t to = from + count;
     dev_t n, next;

     for (n = from; n < to; n = next) {
         next = MKDEV(MAJOR(n)+, );/*先得到下一个设备号(其实也是一个设备号,只不过此时的次设备号为0)并存储于next中*/
18         if (next > to) /*溢出的情况*/          /*判断在from的基础上再追加count个设备(dev_t to = from+count)是否已经溢出到下一个主设备号*/
             next = to;
/*           如果没有溢出(next小于to),那么整个for语句就只执行个一次__register_chrdev_region函数;
             否则当设备号溢出时,会把当前溢出的设备号范围划分为几个小范围,分别调用__register_chrdev_region函数。  */
 
 /*
  * Register a single major with a specified minor range.
  *
  * If major == 0 this functions will dynamically allocate a major and return
  * its number.
  *
  * If major > 0 this function will attempt to reserve the passed range of
  * minors and will return zero on success.
  *
  * Returns a -ve errno on failure.
  */
 static struct char_device_struct *
13 __register_chrdev_region(unsigned int major, unsigned int baseminor,
14                int minorct, const char *name)
 {
     struct char_device_struct *cd, **cp;
     ;
     int i;

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

     mutex_lock(&chrdevs_lock);

     /* temporary */
//此if语句目的在于如果主设备号为零,则自动分配主设备号(寻找设备号列表中的空的位置(从大到小))并返回
     ) {
         ; i > ; i--) {
             if (chrdevs[i] == NULL)
                 break;
         }

         ) {
             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);
 
 /* index in the above */
 static inline int major_to_index(unsigned major)
 {
     return major % CHRDEV_MAJOR_HASH_SIZE;
 }

//寻找新元素插入的位置

     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;//更新旧的最小值,值为当前元素的次设备号
         ;////更新旧的最大值,值为当前次设备号+要申请号的设备个数-1,-1是因为从0开始的次设备号
         int new_min = baseminor;//新的最小值为要添加的次设备号
         ;
 //新的最大值为要添加的次设备号+要申请号的设备个数-1,-1是因为次设备号从0开始
         /* 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);
 }

         if (IS_ERR(cd))
             goto fail;
     }
     ;
 fail:
     to = n;
     for (n = from; n < to; n = next) {
         next = MKDEV(MAJOR(n)+, );
         kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
     }
     return PTR_ERR(cd);
 }

     gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
     gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);

     gFile->open = butsOpen;
 //注册设备函数到file_operations结构体gFile

    //gDev->owner = THIS_MODULE;
     gFile->owner = THIS_MODULE;
     cdev_init(gDev, gFile);
 //在cdev结构体中添加指针指向file_operations结构体gFile
     cdev_add(gDev, devNum, );
 //建立设备号与cdev结构体联系
     printk(KERN_EMERG"button driver initial done...\r\n");
     ;
 }