Linux Char Device

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;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。