shouchengcheng
just do it

上层应用程序通过对节点的操作,对设备进行控制。这个节点的接口就是由字符设备驱动提供,向前面的HelloWorld驱动是没有上层操作的接口的,它仅仅只有模块加载接口和退出接口。所以也不需要对应的节点。

struct file_operations这个结构体它在linux/fs.h中定义,用来存储驱动内核模块提供的对 设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的 事务的函数的地址。

驱动内核模块是不需要实现每个函数的。

 

在内核中没一个设备都有一个cdev结构变量,具体我参考的是这个:http://blog.sina.com.cn/s/blog_63f31f3401016fda.html

 

cdev有两种初始化方式:静态和动态

静态内存定义初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;

动态内存定义初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;

两种使用方式的功能是一样的,只是使用的内存区不一样,一般视实际的数据结构需求而定。

下面贴出了两个函数的代码,以具体看一下它们之间的差异。
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;
}

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_init() 还多赋了一个 cdev->ops 的值。

初始化 cdev 后,需要把它添加到系统中去。为此可以调用 cdev_add() 函数。传入 cdev 结构的指针,起始设备编号,以及设备编号范围。
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);
}

关于 kobj_map() 函数就不展开了,我只是大致讲一下它的原理。内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。

当一个字符设备驱动不再需要的时候(比如模块卸载),就可以用 cdev_del() 函数来释放 cdev 占用的内存。
void cdev_del(struct cdev *p)
{
   cdev_unmap(p->dev, p->count);
   kobject_put(&p->kobj);
}
其中 cdev_unmap() 调用 kobj_unmap() 来释放 cdev_map 散列表中的对象。kobject_put() 释放 cdev 结构本身。

设备号分配

#define NAME "name"
int major = xxx;
int result;
dev_t devno = MKDEV(MAJOR,0);
if(MAJOR)
    result = register_chrdev_region(devno,1,NAME);//静态
else
{
  result = alloc_chrdev_region(&devno,0,10,NAME);//动态
  major = MAJOR(devno);    
}

记录以备忘

 

附加:

还可以用misc设备。在书中它是把led字符驱动程序,注册成misc设备,这个步骤也很简单

首先要定义好struct miscdevice misc这个结构体

这个结果体主要初始化minor,name,fops这三个值

.minor = MISC_DYNAMIC_MINOR 这个宏是内核定义好了的

然后在注册时调用misc_register(&misc)注册

misc_deregister(&misc)释放

感觉misc比上面的要方便很多。

 

这是网上关于misc设备的说明

http://www.zhihu.com/question/21508904

posted on 2014-04-04 15:26  shouchengcheng  阅读(281)  评论(0编辑  收藏  举报