字符设备驱动是较为基础的linux设备驱动。

字符设备驱动的加载的步骤是:首先申请设备号register_chrdev_region() à初始化字符设备cdev_init()(结构体初始化,并建立字符设备和文件操作指针之间的连接)-> 添加字符设备驱动

1. 字符设备驱动结构体

struct cdev {

      structkobject kobj;

      structmodule *owner;

      conststruct file_operations *ops;

      structlist_head list;

      dev_tdev;

      unsignedint count;

};

内置的kobject

struct kobject {

      constchar              *name;

      structlist_head       entry;

      structkobject         *parent;

      structkset              *kset;

      structkobj_type     *ktype;

      structsysfs_dirent   *sd;

      structkref              kref;

      unsignedint state_initialized:1;

      unsignedint state_in_sysfs:1;

      unsignedint state_add_uevent_sent:1;

      unsignedint state_remove_uevent_sent:1;

      unsignedint uevent_suppress:1;

};


2.设备号的申请

__register_chrdev_region(unsigned intmajor, unsigned int baseminor,

                       int minorct, const char *name)

{

struct char_device_struct *cd, **cp;

      intret = 0;

      inti;


      cd= kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);//申请字符设备结构体

//填充设备号和设备名

cd->major = major;

      cd->baseminor= baseminor;

      cd->minorct= minorct;

      strlcpy(cd->name,name, sizeof(cd->name));


mutex_lock(&chrdevs_lock);


      /*temporary */

      if(major == 0) {

             for(i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {

                    if(chrdevs[i] == NULL)

                           break;

             }


             if(i == 0) {

                    ret= -EBUSY;

                    gotoout;

             }

             major= i;

             ret= major;

      }

。。。

cd->next = *cp;

*cp = cd;

}


static struct char_device_struct {

      structchar_device_struct *next;

      unsignedint major;

      unsignedint baseminor;

      intminorct;

      charname[64];

      structcdev *cdev;          /* will die */

} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];


设备号的申请本质上是填充这个全局指针数组的元素值。

字符设备指针数组,数组长度为255,每个成员都是一个特定主设备字符设备结构的链表。数组的offset即为主设备号。

如果要动态获取主设备号,则遍历这个字符设备指针数组,如果指针为NULL,就将该数组元素的标号返回作为主设备号。

cd->next = *cp;

*cp = cd;

这两条语句将申请的新的字符设备指针添加到全局指针数组中去。

这样形成的结果是

不同的主设备号放在不同的数组中。每个数组元素指向一个链表头。

主设备号相同,次设备号不同的设备存在于同一个链表中。


所以主设备号的取值为0-255


如果分配的主设备号和次设备号有效,则填充全局的字符设备指针数组。将新申请的设备结构体的指针赋值给对应的数组元素。


3. 字符设备初始化

void cdev_init(struct cdev *, const structfile_operations *);

初始化cdev成员,并建立和file_operations之间的连接。

void cdev_init(struct cdev *cdev, conststruct file_operations *fops)

{

      memset(cdev,0, sizeof *cdev);

      INIT_LIST_HEAD(&cdev->list);

      kobject_init(&cdev->kobj,&ktype_cdev_default);

      cdev->ops= fops; //文件操作结构体指针赋值

}


4.字符设备添加到系统中

int cdev_add(struct cdev *p, dev_t dev,unsigned count)

{

      p->dev= dev; //填充申请的设备号

      p->count= count; //设备个数

      returnkobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);//将字符设备添加到虚拟文件系统中

}


在对vfs初始化时就已经对cdev_map初始化了

void __init vfs_caches_init(unsigned longmempages)

struct kobj_map {

      structprobe {

             structprobe *next;

             dev_tdev;

             unsignedlong range;

             structmodule *owner;

             kobj_probe_t*get;

             int(*lock)(dev_t, void *);

             void*data;

      }*probes[255];

      structmutex *lock;

};

int kobj_map(struct kobj_map *domain, dev_tdev, unsigned long range,

           struct module *module, kobj_probe_t*probe,

           int (*lock)(dev_t, void *), void *data)

{

      unsignedn = MAJOR(dev + range - 1) - MAJOR(dev) + 1;

      unsignedindex = MAJOR(dev);

      unsignedi;

      structprobe *p;


      if(n > 255)

             n= 255;


      p= kmalloc(sizeof(struct probe) * n, GFP_KERNEL);


      if(p == NULL)

             return-ENOMEM;


      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++) {

             structprobe **s = &domain->probes[index % 255];

             while(*s && (*s)->range < range)

                    s= &(*s)->next;

             p->next= *s;

             *s= p;

      }

      mutex_unlock(domain->lock);

      return0;

}


4. 字符设备号的释放

static struct char_device_struct *

__unregister_chrdev_region(unsigned major,unsigned baseminor, int minorct)

{

      structchar_device_struct *cd = NULL, **cp;

      inti = major_to_index(major);


      mutex_lock(&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;

      }

      mutex_unlock(&chrdevs_lock);

      returncd;

}

红色的部分,删除了对应的字符设备指针节点。