字符设备驱动是较为基础的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;
}
红色的部分,删除了对应的字符设备指针节点。