您的位置:首页 > 运维架构 > Linux

【Linux设备驱动】字符设备驱动

2016-05-31 16:53 507 查看

★关于设备号

◇什么是设备号?它有什么作用?

设备号是一个整形数字,它起到连接设备文件与设备驱动的作用。

◇它具体是怎样进行连接的?

在内核中,字符设备被保存在一个kobj_map结构的cdev_map变量中。通过kobj_map函数将设备号(主次设备号)与字符设备结构体cdev保存到cdev_map中。这个过程实现在cdev_add()函数中,代码如下:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}在cdev_add()函数中最后一行调用的kobj_map()函数实现了上述过程,现在来看一看这个函数代码:
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;//设备个数
unsigned index = MAJOR(dev);//主设备号
unsigned i;
struct probe *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;//cdev结构体存放位置
}
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++) {//s是存放cdev_map结构体中probes[index%255]的地址
struct probe **s = &domain->probes[index % 255];//主设备号为index的驱动程序
while (*s && (*s)->range < range)//将主设备号相同的设备放到一个链表中 s = &(*s)->next; p->next = *s; *s = p;
}
mutex_unlock(domain->lock);
return 0;
}
其中有一些代码已经加了注释。这里最难理解的地方就是将主设备号相同的字符设备放到同一个链表的这几行代码。
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++) {//s是存放cdev_map结构体中probes[index%255]的地址
struct probe **s = &domain->probes[index % 255];//主设备号为index的驱动程序
while (*s && (*s)->range < range)//将主设备号相同的设备放到一个链表中 s = &(*s)->next; p->next = *s; *s = p;
}
mutex_unlock(domain->lock);其中domain在程序中便是cdev_map,这里将cdev_map锁住,防止多进程对cdev_map操作。
struct probe **s = &domain->probes[index % 255]
就是确定主设备号为index的驱动程序。
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;
};在这个结构体中struct probe *probes[255];这个结构体指针数组就是用来存放主设备号字符驱动的地址的,其中主设备号为0~254

其中这255个数组成员都会指向一个struct probe的结构体,而这个结构体代表的就是主设备号不同的字符设备驱动。
while (*s && (*s)->range < range)//将主设备号相同的设备放到一个链表中
s = &(*s)->next;
p->next = *s;
*s = p;
这段代码就是将之前申请的p指针指向的struct probe结构体加载到主设备号相同的链表中去。
kobj_map()这个函数主要的作用将加载到内核的字符设备cdev与设备号dev存入申请的struct
probe结构体p中;然后将p指针指向的struct probe结构体加载到同为主设备号下的一个链表中。这样当打开一个字符设备文件时,就可以通过设备号找到相应的字符设备,并且调用它的file_operations结构体中的函数。

◇申请设备号的方式有两种:静态申请和动态申请。

 所谓静态申请就是直接指定主设备号,然后使用函数

int register_chrdev_region(dev_t from, unsigned count, const char *name)

申请。动态申请则不指定设备号,

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)

让系统自动分配设备号使用。设备号可以分为主设备号与次设备号。可以通过下面的宏实现:

MAJOR(dev_t dev)

MINJOR(dev_t dev)

★申请注册设备

linux系统中使用cdev结构来表示一个字符设备,注册一个字符设备步骤:

◇申请设备号

◇分配并初始化一个cdev结构体

分配并初始化一个cdev结构体有两种方式:cdev_alloc()、cdev_init()

cdev_alloc()用于动态申请一个cdev结构体

cdev_init()用于初始化cdev成员,建立cdev与file_operations之间的联系

◇将此结构体加入内核中

cdev_add(struct cdev*p,int count)将此结构体加入到内核中

★注销设备

首先注销设备号,

unregister_chrdev_region(xxx_dev_no,1)

然后注销设备

cdev_del(&xxx_dev.cdev)

★Linux字符设备驱动的组成

◇字符设备驱动模块加载与卸载函数

◇字符设备驱动的file_operations结构体中成员函数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: