字符设备驱动的另一种写法

字符设备驱动的另一种写法

在Linux2.6内核中,使用cdev结构体描述一个字符设备;

cdev结构体(include/linux/cdev.h)定义如下:

struct cdev {
    struct kobject kobj;               /* 内嵌的kobject对象 */
    struct module *owner;              /* 所属模块 */
    const struct file_operations *ops; /* 文件操作结构体 */
    struct list_head list;
    dev_t dev;                         /* 设备号 */
    unsigned int count;
};

cdev结构体中的dev成员定义了设备号,为32位,其中12位主设备号,20位次设备号;

使用下列宏可以从dev获得主设备号和次设备号,以及通过主设备号和次设备号生成dev_t,定义如下:

#define MINORBITS   20
#define MINORMASK   ((1U << MINORBITS) - 1)

#define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS)) /* 获取主设备号 */
#define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))  /* 获取次设备号 */
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))      /* 根据主次设备号生成dev*/

cdev结构体中的另一个重要成员file_operations定义了字符设备驱动提供给虚拟文件系统的接口函数;

Linux2.6内核提供了一组函数用于操作cdev结构体,定义如下:

/* 用于初始化cdev的成员,并建立cdev和file_operation之间的连接 */
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 *);

对于cdev_add()函数的调用通常发生在字符设备驱动模块加载函数中,向系统注册一个字符设备,在此之前应首先调用如下函数向系统申请设备号:

/* 用于已知起始设备的设备号的情况 */
int register_chrdev_region(dev_t from, unsigned count, const char *name);
或者:
/* 用于设备号未知,向系统动态申请未被占用的设备号的情况 */
/* 调用该函数成功后,会把得到的设备号放入第一个参数dev中 */
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name);

对于cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()应该被调用以释放原先申请的设备号;

具体使用如下:

/* 定义一个dev_t类型变量 */
dev_t dev_id;

static struct cdev xxx_cdev;
static int major;
static const struct file_operations xxx_fops = {
    .owner  = THIS_MODULE,
    .write  = xxx_write,
    .read   = xxx_read,
    .open   = xxx_open
};

/* 设备驱动模块加载函数 */
static int __init xxx_init(void)
{
    if (major) { /* 如果已知主设备号 */
        dev_id = MKDEV(major, 0);
        retval = register_chrdev_region(dev_id, 1, DEV_NAME);
    } else {
        retval = alloc_chrdev_region(&dev_id, 0, 1, DEV_NAME);
        major = MAJOR(dev_id);
    }
    cdev_init(&xxx_cdev, &xxx_fops);
    cdev_add(&xxx_cdev, dev_id, 1);
}

/* 设备驱动模块卸载函数 */
static void __exit xxx_exit(void)
{
    cdev_del(&xxx_cdev);
    unregister_chrdev_region(dev_id, 1);
}

猜你喜欢

转载自www.cnblogs.com/jasontian996/p/11519172.html