Overview
cdev
char device
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
cdev_init()
initialize a cdev sturcture,把user的fops传递给cdev的成员变量。
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
cdev_alloc()
Allocates a cdev structure,初始化kobject成员
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
cdev_add()
Add a char device to the system,赋值dev和count,并且调用kobj_map()
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
kobject_get(p->kobj.parent);
return 0;
}
下面来看下kobj_map是干什么的?
struct probes是一个255个struct probe指针变量。
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; //获取传进来的dev占用多少个主设备号,一般情况下值为1
unsigned index = MAJOR(dev);
unsigned i;
struct probe *p;
p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);//分配一个probe变量,并对成员进行初始化
for (i = 0; i < n; i++, p++) {
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;
p->range = range;
p->data = data;
}
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++) {
struct probe **s = &domain->probes[index % 255]; //找到同一主设备号在domain中的位置
while (*s && (*s)->range < range)
s = &(*s)->next;
p->next = *s; //p->next=NULL;
*s = p; //probes[index] = p;
}
mutex_unlock(domain->lock);
return 0;
}
kobj_map做的事情就是分配一个probe,初始化,并插入到全局的cdev_map中,这样将来file open的时候,可以根据major/minor number找到对应的cdev pointer。
cdev_map_init
void __init chrdev_init(void)
{
cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
}
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
{
struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
int i;
base->dev = 1;
base->range = ~0;
base->get = base_probe;
for (i = 0; i < 255; i++)
p->probes[i] = base;
p->lock = lock;
return p;
}
How to alloc char device major/minor number
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]; //255
alloc_chrdev_region()
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.
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);
*dev = MKDEV(cd->major, cd->baseminor);
return 0;
}
找到一个没有使用major number,并通过dev返回
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);
mutex_lock(&chrdevs_lock);
/* temporary */
if (major == 0) {
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
major = i;
}
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
cd->next = *cp;
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
}
class
A class is a higher-level view of a device that abstracts out low-level implementation details. Drivers may see a SCSI disk or an ATA disk, but, at the class level, they are all simply disks. Classes allow user space to work with devices based on what they do, rather than how they are connected or how they work.
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
const struct attribute_group **dev_groups;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
...
struct subsys_private *p;
};
class_create()
class_create - create a struct class structure
This is used to create a struct class pointer that can then be used in calls to device_create().
struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)
{
struct class *cls;
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release;
retval = __class_register(cls, key);
return cls;
}
class_register()
int __class_register(struct class *cls, struct lock_class_key *key)
{
struct subsys_private *cp;
int error;
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
INIT_LIST_HEAD(&cp->interfaces);
kset_init(&cp->glue_dirs);
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
/* set the default /sys/dev directory for devices of this class */
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;
cp->subsys.kobj.kset = class_kset; //parent is /sys/class
cp->subsys.kobj.ktype = &class_ktype;
cp->class = cls;
cls->p = cp;
error = kset_register(&cp->subsys); //create folder in /sys/class/
error = add_class_attrs(class_get(cls));
class_put(cls);
return error;
}
class_kset
int __init classes_init(void)
{
class_kset = kset_create_and_add("class", NULL, NULL);//create "class" directory in sys file system
return 0;
}
device_create()
Creates a device and registers it with sysfs.
static struct device * device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata,
const struct attribute_group **groups,
const char *fmt, va_list args)
{
struct device *dev = NULL;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); //kzalloc a struct device strucuture
device_initialize(dev);//initialize a device strucuture
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->groups = groups;
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
retval = device_add(dev);// add device in sysfs and call driver probe if has bus and matched
return dev;
}