字符驱动(按键)初始化函数分析:
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"); ; }