二.真实的cdev 2.1 设备号 搞驱动的都应该知道的东西,在写gpio驱动时,往往会用到以下两个函数。 alloc_chrdev_region --自动分配设备号 register_chrdev_region --分配以设定的设备号。 上面两个函数的调用很简单,当时却没有深入去理解其实现的原理,只知道其采用了hash表,但是具体怎么实现的却不知道。在这里来好好理解一下。上面两个函数的核心是__register_chrdev_region;那来看源码的实现。 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 = 255 你看这个结构体会发现,其定义了一个指针数组,大小为255. 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) { --当调用alloc_chrdev_region时,传入的major为0,从表面上看调用alloc_chrdev_region会自动分配设备,但是有一个缺点就是其 分配的设备号只能在255内,而且并没有使用hash表,从而大大减少了linux所支持的设备号数。 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; } --以下以register_chrdev_region为例即传入major不为0。 cd->major = major; cd->baseminor = baseminor; cd->minorct = minorct; strncpy(cd->name,name, 64);
i = major_to_index(major); -- 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; 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; } }