linux 添加字符驱动,linux字符设备驱动

字符设备驱动是较为基础的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;

}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值