Chinaunix首页 | 论坛 | 博客
  • 博客访问: 439499
  • 博文数量: 123
  • 博客积分: 2686
  • 博客等级: 少校
  • 技术积分: 1349
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-23 22:11
文章分类
文章存档

2012年(3)

2011年(10)

2010年(100)

2009年(10)

我的朋友

分类: LINUX

2010-06-02 19:23:39

一、字符设备结构体

[cdev.h]

#ifndef _LINUX_CDEV_H
#define _LINUX_CDEV_H

#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>

struct file_operations;
struct inode;
struct module;

struct cdev {
    struct kobject kobj;/*继承struct kobject 结构体*/
    struct module *owner;/*所属模块*/
    const struct file_operations *ops;/*文件操作结构体*/
    struct list_head list;
    dev_t dev; /*设备号*/
    unsigned int count;
};
/*初始化cdev的成员,并建立cdev和file_operations之间的连接*/
void cdev_init(struct cdev *, const struct file_operations *);


/*动态申请一个cdev空间内存*/
struct cdev *cdev_alloc(void);

void cdev_put(struct cdev *p);

/*向系统添加、注册一个cdev*/
int cdev_add(struct cdev *, dev_t, unsigned);


/*向系统注销一个cdev*/

void cdev_del(struct cdev *);

int cdev_index(struct inode *inode);

void cd_forget(struct inode *);

extern struct backing_dev_info directly_mappable_cdev_bdi;

#endif



[char_dev.c ] cdev_init()

/**
 * cdev_init() - initialize a cdev structure
 * @cdev: the structure to initialize
 * @fops: the file_operations for this device
 *
 * Initializes @cdev, remembering @fops, making it ready to add to the
 * system with cdev_add().
 */

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);/*初始化kobject,以便kobject_add()调用*/
    cdev->ops = fops;
}


[ char_dev.c ] cdev_alloc()

/**
 * cdev_alloc() - allocate a cdev structure
 *
 * Allocates and returns a cdev structure, or NULL on failure.
 */

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;
}


[ char_dev.c ] cdev_add(),通常在驱动模块加载时调用

/**
 * cdev_add() - add a char device to the system
 * @p: the cdev structure for the device
 * @dev: the first device number for which this device is responsible
 * @count: the number of consecutive minor numbers corresponding to this
 * device
 *
 * cdev_add() adds the device represented by @p to the system, making it
 * live immediately. A negative error code is returned on failure.
 */

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);
}


[ char_dev.c ] cdev_del(),通常在驱动模块卸载时调用

/**
 * cdev_del() - remove a cdev from the system
 * @p: the cdev structure to be removed
 *
 * cdev_del() removes @p from the system, possibly freeing the structure
 * itself.
 */

void cdev_del(struct cdev *p)
{
    cdev_unmap(p->dev, p->count);
    kobject_put(&p->kobj);
}


二、分配和释放设备号

在调用cdev_add()函数向系统注册字符设备之前,应首先调用register_chrdev_region()或者alloc_chrdev_region()函数向系统申请设备号。

/**
 * register_chrdev_region() - register a range of device numbers
 * @from: the first in the desired range of device numbers; must include
 * the major number.
 * @count: the number of consecutive device numbers required
 * @name: the name of the device or driver.
 *
 * Return value is zero on success, a negative error code on failure.
 */

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

/*from -----要分配设备编号范围的起始值,经常设置为0;

  count ----所请求的连续设备编号的个数;

  name -----是和该设备范围关联的设备名称,它将出现在/proc/devices和sysfs中*/


而,alloc_chrdev_region()用于设备未知,向系统动态申请未被占用的设备号的情况。调用成功后,分配的设备号放在输出变量dev中。

/**
 * alloc_chrdev_region() - register a range of char device numbers
 * @dev: output parameter for first assigned number
 * @baseminor: first of the requested range of minor numbers
 * @count: the number of minor numbers required
 * @name: the name of the associated device or driver
 *
 * Allocates a range of char device numbers. The major number will be
 * chosen dynamically, and returned (along with the first minor number)
 * in @dev. Returns zero or a negative error code.
 */

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


三、Linux字符设备驱动程序的组成

1. 字符设备驱动模块加载与卸载函数
在字符设备驱动模块加载函数中应该实现设备号的申请和cdev的注册,而在卸载函数中应该实现设备号的释放和cdev的注销。因为cdev是所有字符设备的抽象,在编写具体的字符设备驱动程序时,针对的是具体的字符设备,具体的自字符设备肯定有着自己特征。所以,一般的做法是,继承cdev,加上私有数据,信号量等信息。

//设备结构体
struct xxx_dev
{
    struct cdev cdev;
    private_data;//私有数据
    semaprore;//信号量
}


//设备驱动模块加载函数

static int __init xxx_init(void)

{

    ...

    cdev_init(&xxx_dev.cdev,&xxx_fops);//初始化cdev

    xxx_dev.cdev.owner = THIS_MODDULE;

    //获取字符设备号

    if(xxx_major)

    {

        register_chrdev_region(xxx_dev_no,1,DEV_NAME);

    }

    else

    {

        alloc_chrdev_region(&xxx_dev_no,0,1,DEV_NAME);

    }

    ret=cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//注册设备

}


//设备驱动模块卸载函数

static void __exit xxx_exit(void)

{

   unregister_chrdev_region(xxx_dev_no,1);//释放占用的设备号

   cdev_del(&xxx_dev.cdev);//注销设备

   。。。

}

//设备驱动模块卸载函数  


2. 字符设备驱动的file_operations结构体成员函数
file_operation结构体中成员函数是字符设备驱动与内核的接口,是用户空间对linux进行系统调用的最终落实者。字符设备驱动程序中,具体实现这些函数,通常,比如file_operation中的read这个函数指针将指向这个具体的驱动程序中的函数xxx_read().

由于用户空间和内核空间的内存不能够直接互相访问,要借助函数copy_from_user()完成用户空间到内核空间的复制,copy_to_user()完成内核空间到用户空间的复制

3. 在字符设备驱动中,还需要定义一个file_operation的实例xxx_fops,并将具体设备驱动的函数赋值给file_operation的成员。 这个file_operation的实例将在函数cdev_init(&xxx_dev.cdev,&xxx_fops)函数中建立与cdev的连接。
阅读(1331) | 评论(0) | 转发(0) |
0

上一篇:对udev的理解

下一篇:对并发控制的理解

给主人留下些什么吧!~~