您的位置:首页 > 其它

字符设备驱动程序框架

2014-05-12 00:00 267 查看
1, 设备号的内部表示形式

类型:dev_t 32=12(主设备号) + 20(次设备号)

相关宏:<linux/kdev_t.h>

MAJOR(dev_t dev)

MINOR(dev_t dev)

MKDEV(int major, int minor);

2. 分配和释放设备号

相关函数:

//静态分配设备号

int register_chrdev_region(dev_t first, unsigned int count,

char *name);

//动态分配设备号

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,

unsigned int count, char *name);

void unregister_chrdev_region(dev_t first, unsigned int count);

3.获取设备号的通常写法

if (scull_major) {

dev = MKDEV(scull_major, scull_minor);

result = register_chrdev_region(dev, scull_nr_devs, "scull");

} else {

result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,

"scull");

scull_major = MAJOR(dev);

}

if (result < 0) {

printk(KERN_WARNING "scull: can't get major %d\n", scull_major);

return result;

}

4.一些重要的数据结构

struct file_operations

{

//用于防止一个正在使用的模块被卸载,通常值为THIS_MODULE

struct module *owner;

//seek

loff_t (*llseek) (struct file *, loff_t, int);

//read

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);

//write

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *);

//readdir

int (*readdir) (struct file *, void *, filldir_t);

//poll

unsigned int (*poll) (struct file *, struct poll_table_struct *);

//ioctl

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

//mmap

int (*mmap) (struct file *, struct vm_area_struct *);

//open

int (*open) (struct inode *, struct file *);

//flush

int (*flush) (struct file *);

//release

int (*release) (struct inode *, struct file *);

//fsync

int (*fsync) (struct file *, struct dentry *, int);

int (*aio_fsync)(struct kiocb *, int);

int (*fasync) (int, struct file *, int);

//lock

int (*lock) (struct file *, int, struct file_lock *);

//readv writev

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

//sendfile

ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);

//sendpage

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *,

int);

//get_unmapped_area

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned

long, unsigned long, unsigned long);

//check_flags

int (*check_flags)(int)

//dir_notify

int (*dir_notify)(struct file *, unsigned long);

...

};

//代表打开的文件

struct file

{

//读写权限

mode_t f_mode;

//文件读写位置

loff_t f_pos;

//文件标志(O_RDONLY, O_NONBLOCK, O_SYNC)

unsigned int f_flags;

//文件操作

struct file_operations *f_op;

//设备文件的私有数据

void *private_data;

//与文件相关的目录,

struct dentry *f_dentry;

...

};

struct inode

{

//对于设备文件来说,此域表示真实的设备号

dev_t i_rdev;

//当引结点指向一个字符设备时,代表内核内部结构的字符设备

struct cdev *i_cdev;

};

//从I结点中获取次设备号

unsigned int iminor(struct inode *inode);

// 从I结点中获取主设备号

unsigned int imajor(struct inode *inode);

5. 注册字符设备

头文件:<linux/cdev.h>

cdev 结构体的定义:

struct cdev

{

struct kobject kobj; /*内嵌的kobject对象*/

struct module *owner; /*所属模块*/

struct file_operations *ops; /*相关的文件操作*/

struct list_head list;

dev_t dev; /*设备模块*/

unsigned int count;

};

分配和初始化字符设备相关结构

方法1(将cdev作为单独的一个结构):

struct cdev *my_cdev = cdev_alloc( ); //分配设备空间

my_cdev->ops = &my_fops;

//设备初始化

void cdev_init(struct cdev *cdev, struct file_operations *fops);

//添加设备

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

//删除设备

void cdev_del(struct cdev *dev);

方法2(将cdev作为自定义设备结构的一个成员):

struct mydev

{



struct cdev;

}

设备分配初始化以及删除操作类似

PS: 2.6内核以前的字符设备注册方法

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

int unregister_chrdev(unsigned int major, const char *name);

6. 字符设备驱动程序模板

字符设备驱动模块加载和卸载函数模板

//设备结构体

struct xxx_dev_t

{

struct cdev cdev;



};

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

static int __init xxx_init(void)

{



//初始化cdev

cdev_init(&xxx_dev.cdev, &xxx_fops);

//获取字符设备号

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



}

字符设备常用I/O操作函数模板

//读设备

ssize_t xxx_read(struct file *filep, char __user *buf, size_t count, loff_t *f_pos)

{



copy_to_user(buf, …, …);



}

//写设备

ssize_t xxx_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos)

{



copy_from_user(…, buf, …);



}

//ioctl函数

int xxx_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)

{



switch(cmd)

{

case XXX_CMD1:



break;

case XXX_CMD2:



break;

default:

//不能支持的命令

return –ENOTTY;

}

return 0;

}

用户空间与内核空间的数据传输

//内核空间到用户空间数据的复制

unsigned long copy_to_user(void __user *to, const void *from,

unsigned long count);

//用户空间到内核空间的复制

unsigned long copy_from_user(void *to, const void __user *from,

unsinged long count);

上述函数均返回不能被复制的字节数,因此,如果完全复制成功,返回值为0.

如果要复制的内在是简单类型,如char, int,long等,则可以使用简单的put_user()和get_user()函数。

如:

int val;



get_user(val, (int*)arg);



put_user(val, (int*)arg);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息