linux字符设备
linux有一个全局的结构体数组,共255个元素,记录系统中的设备节点。
主设备号相同,次设备号不同的设备组成链表。
参考:http://edsionte.com/techblog/archives/1393
注册一个字符设备调用函数
register_chrdev(major, DEV_NAME, &fops) static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) { return __register_chrdev(major, 0, 256, name, fops); } //申请次设备号最小从0开始,申请256个次设备。 int __register_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name, const struct file_operations *fops) { struct char_device_struct *cd; struct cdev *cdev; int err = -ENOMEM; //从全局的字符设备结构体数组中获取可以使用的结构体char_device_struct cd = __register_chrdev_region(major, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd); cdev = cdev_alloc(); if (!cdev) goto out2; cdev->owner = fops->owner; cdev->ops = fops; kobject_set_name(&cdev->kobj, "%s", name); //将字符设备添加到系统中 err = cdev_add(cdev, MKDEV(cd->major, baseminor), count); if (err) goto out; cd->cdev = cdev; return major ? 0 : cd->major; out: kobject_put(&cdev->kobj); out2: kfree(__unregister_chrdev_region(cd->major, baseminor, count)); return err; } 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) { //如果设备号为0,那么遍历字符设备节点的数组,查找空闲的设备号,分配给设备 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; } cd->major = major; cd->baseminor = baseminor; cd->minorct = minorct; strlcpy(cd->name, name, sizeof(cd->name)); i = major_to_index(major); //遍历设备链表,将新的设备根据主设备号和次设备号添加到链表的合适位置 //偏移主设备号 for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major || ((*cp)->major == major && (((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor)))) break; //检查次设备号的范围是否有重叠,但是这个函数一开始就已经指定了此设备0-255(256个),
//所以肯定会重叠,这就要求,主设备号不能与已经存在的字符设备重叠,否则注册设备必然会失败
//其他的函数范围可能更小,就需要下面进行判断
/* 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; out: mutex_unlock(&chrdevs_lock); kfree(cd); return ERR_PTR(ret); }
另一种注册字符设备函数,过程与上面的类似。
struct cdev cdev;
dev_t devno;
int __init test_init(void)
{
int ret;
//静态分配
//指定主设备号和次设备号,并设备好的范围是1
//dev_t devno = MKDEV(globalmem_major, 0);
//ret = register_chrdev_region(&devno, 1, DEV_NAME);
//动态分配
ret = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
if(ret)
{
printk("alloc_chrdev_region failed!\n");
return -EAGAIN;
}
else
printk("major = %d\n", MAJOR(devno));
cdev_init(&cdev, &fops);
cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev, devno, 1);
if(ret)
{
//释放之前分配的设备号
unregister_chrdev_region(devno, 1);
printk("cdev_add failed!\n");
}
return ret;
}
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
struct char_device_struct *cd;
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;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name);
if (IS_ERR(cd))
goto fail;
}
return 0;
fail:
to = n;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
return PTR_ERR(cd);
}
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;
}
结构体关系如下所示