/** * alloc_chrdev_region() - register a range of char device numbers * @dev: output parameter for first assigned number * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: the name of the associated device or driver * * Allocates a range of char device numbers. The major number will be * chosen dynamically, and returned (along with the first minor number) * in @dev. Returns zero or a negative error code. */
/** * unregister_chrdev_region() - return a range of device numbers * @from: the first in the range of numbers to unregister * @count: the number of device numbers to unregister * * This function will unregister a range of @count device numbers, * starting with @from. The caller should normally be the one who * allocated those numbers in the first place... */
//fs/char_dev.c voidunregister_chrdev_region(dev_t from, unsigned count) { dev_t to = from + count; dev_t n, next; for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0);//注意这里的next的计算,一般这个for循环都是执行一次 if (next > to) next = to; kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); } }
staticstructchar_device_struct * __unregister_chrdev_region(unsignedmajor, unsignedbaseminor, intminorct) { structchar_device_struct *cd = NULL, **cp; int i = major_to_index(major); down(&chrdevs_lock); for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major == major && (*cp)->baseminor == baseminor && (*cp)->minorct == minorct) break; if (*cp) { cd = *cp; *cp = cd->next; } up(&chrdevs_lock); return cd; }
/* * Keep mostly read-only and often accessed (especially for * the RCU path lookup and 'stat' data) fields at the beginning * of the 'struct inode' */ //include/linux/fs.h //inode节点,此处只列出了我们关心的一些成员 structinode { umode_t i_mode; unsignedshort i_opflags; kuid_t i_uid; kgid_t i_gid; unsignedint i_flags;
dev_t i_rdev;//设备主次设备号
union { structhlist_headi_dentry; structrcu_headi_rcu; };
conststructfile_operations *i_fop;/* former ->i_op->default_file_ops */ structfile_lock *i_flock; structaddress_spacei_data;
/* * Called every time a character special file is opened */
//fs/char_dev.c staticintchrdev_open(struct inode *inode, struct file *filp) { structcdev *p; structcdev *new = NULL; int ret = 0;
spin_lock(&cdev_lock); p = inode->i_cdev;//p即为设备对象 if (!p) { //第一次打开,还没有建立关联 structkobject *kobj; int idx; spin_unlock(&cdev_lock); kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);//在cdev_map中查找对应的设备 if (!kobj) return -ENXIO; //此时已经找到了,new即为cdev对象 new = container_of(kobj, struct cdev, kobj); spin_lock(&cdev_lock); /* Check i_cdev again in case somebody beat us to it while we dropped the lock. */ p = inode->i_cdev; if (!p) { //将找到的cdev设备对象保存在inode中,下次就无需再查找了 inode->i_cdev = p = new; list_add(&inode->i_devices, &p->list); new = NULL; } elseif (!cdev_get(p)) ret = -ENXIO; } elseif (!cdev_get(p)) ret = -ENXIO; spin_unlock(&cdev_lock); cdev_put(new); if (ret) return ret;
ret = -ENXIO; //将cdev设备对象中保存的fops结构体(这个就是我们驱动中实现的各种方法了)保存到file结构体中 filp->f_op = fops_get(p->ops); if (!filp->f_op) goto out_cdev_put;
if (filp->f_op->open) { //调用我们驱动中的open方法 ret = filp->f_op->open(inode, filp); if (ret) goto out_cdev_put; }
return0;
out_cdev_put: cdev_put(p); return ret; }
//driver/base/map.c struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index) { structkobject *kobj; structprobe *p; unsignedlong best = ~0UL;
retry: mutex_lock(domain->lock); //根据主设备号定位probe数组,然后遍历链表 for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) { structkobject *(*probe)(dev_t, int *, void *); structmodule *owner; void *data;
//判断你要寻找的设备号和已经保存的设备的关系 if (p->dev > dev || p->dev + p->range - 1 < dev) continue; if (p->range - 1 >= best) break; if (!try_module_get(p->owner)) continue;
//此时代表已经找到对应的设备了 owner = p->owner; data = p->data;//这个就是cdev设备对象 probe = p->get; best = p->range - 1; *index = dev - p->dev;//dev_t设备号 if (p->lock && p->lock(dev, data) < 0) { module_put(owner); continue; } mutex_unlock(domain->lock); kobj = probe(dev, index, data);//这个函数本质就是返回cdev结构体中的kobj成员,下面给出了 /* Currently ->owner protects _only_ ->probe() itself. */ module_put(owner); if (kobj) return kobj;//返回cdev设备对象的kobj成员 goto retry; } mutex_unlock(domain->lock); returnNULL; }
那么还有一个方式即为filp->private_data = dev; /* for other methods */,就是把刚刚用container_of获取的我们的结构体保存在file结构体中,那么之后的方法(write,read,llsek等)都能够通过filp->private_data获取到我们的结构体