驱动字符设备的基础
来源:互联网 发布:php 清空cookie 编辑:程序博客网 时间:2024/04/29 16:20
在linux2.6内核中使用cdev结构体描述一个字符设备,cdev结构体定义如下:
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_t成员定义了设备号,为32位,其中12为主设备号,20为次设备号,使用下列宏可以获得主设备号和次设备号
MAJOR(dev_t dev)
MINOR(dev_t dev)
而使用下列宏可以通过主设备号和次设备号生成dev_t
MKDEV(inf major,int minor)
linux2.6内核还提供了一组函数用以操作cdev结构体
void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);
void cd_forget(struct inode *);
cdev_init()函数用以初始化cdev并建立cdev成员和file_operations连接,源代码如下
void cdev_init(struct cdev *cdev, const struct file_operations *fops)//注意传进的cdev结构体指针
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;//将传进的文件操作结构体指针赋给cdev的ops
}
cdev_alloc()动态申请一个cdev内存,源代码如下
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);//动态分配cdev内存
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;//返回内存所在地址
}
cdev_add()用于注册设备cdev;cdev_dely()用于释放设备
下面要说一下的是分配和释放设备号函数
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)
register_chrdev_region()函数用于已知起始设备号的情况,而alloc_chrdev_region()用于设备号未知,是用来向系统动态分配未分配的设备号,函数成功后就会得到一个设备号存在第一个参数dev中
相反释放申请设备号函数如下
void unregister_chrdev_region(dev_t from, unsigned count)
下面的是file_operations结构体
file_operations结构体中的成员函数式字符设备驱动的主体内容,结构体如下:
struct file_operations {
struct module *owner;//拥有该结构体模块的指针,通常为THIS_MODULES
loff_t (*llseek) (struct file *, loff_t, int);//用来修改当前的读写位置
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//从设备中同步读写数据
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备发送数据
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//异步读取数据
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//异步写入数据
int (*readdir) (struct file *, void *, filldir_t);//用于读取目录
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);//执行I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);//打开文件
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);//关闭文件
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
llseek()函数用于修该一个文件当前的读写位置;
read()函数用来从设备中读取数据,成功时返回读取字节数
write()函数用来向设备发送数据,成功时返回写入字节数
ioctl()提供设备相关的控制命令
....
linux字符设备驱动组成
在字符设备模块加载函数中应该实现设备号申请和cdev的注册,而在卸载函数应实现设备号释放和cdev注销
工程师通常习惯为设备定于一个设备相关的结构体,其中包含所涉及的cdev,私有数据及信号量等信息,常见的设备结构体,模块加载和卸载函数形式如下
//设备结构体
struct xxx_dev{
struct cdev cdev;
......
};
struct xxx_dev xxx_dev;
//设备驱动模块加载函数
statrc int __init xxx_init(void)
{
.....
int err,devno = MKDEV(globalmem_major,index);
cdev_init(&xxx_dev.cdev,&gxxx_fops);//初始化cdev并建立cdev和file_operations的链接
xxx->cdev.owner = THIS_MODULE;
if(globalmem_major)
result = register_chrdev_region(devno, 1, "globalmem");//申请设备号
else{
result = alloc_chrdev_region(&devno,0,1,"globalmem");//动态申请设备号
globalmem_major = MAJOR(devno);//获得int的主设备号
ret = cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//注册设备xxx_dev_no是dev_t的设备号
...
}
//设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
unregister_chrdev_region(xxx_dev_no,1);//释放设备号
cdev_del(&xxx_dev,cdev);//注销设备(cdev)
}
字符设备中的file_operations结构体中的成员函数
file_operations结构体中成员函数式字符设备驱动与内核的接口,是用户空间对linux进行系统调用的最终落实者。大多数驱动read(),write(),ioctl()函数如下所示
static ssize_t xxx_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)
{//filp表示文件结构体指针,buf指针指向用户空间,size是要读的字节数,ppos指针是读的位置相对文件开头
...
copy_to_user(buf,...,...);//内核空间到用户空间
...
}
//写函数用户空间到内核空间
static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
{//filp表示文件结构体指针,buf指针指向用户空间,size是要读的字节数,ppos指针是写的位置相对文件开头
...
copy_from_user(...,buf,...);//用户空间到内核空间
...
}
static int xxx_ioctl(struct inode *inodeep,struct file *filp,unsigned int cmd,unsigned long arg)
{
...
switch (cmd){
case xxx_CMD1;
...
break;
case xxx_CMD2;
...
break;
default:
...
return -ENOTTY;
}
return 0;
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_t成员定义了设备号,为32位,其中12为主设备号,20为次设备号,使用下列宏可以获得主设备号和次设备号
MAJOR(dev_t dev)
MINOR(dev_t dev)
而使用下列宏可以通过主设备号和次设备号生成dev_t
MKDEV(inf major,int minor)
linux2.6内核还提供了一组函数用以操作cdev结构体
void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);
void cd_forget(struct inode *);
cdev_init()函数用以初始化cdev并建立cdev成员和file_operations连接,源代码如下
void cdev_init(struct cdev *cdev, const struct file_operations *fops)//注意传进的cdev结构体指针
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;//将传进的文件操作结构体指针赋给cdev的ops
}
cdev_alloc()动态申请一个cdev内存,源代码如下
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);//动态分配cdev内存
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;//返回内存所在地址
}
cdev_add()用于注册设备cdev;cdev_dely()用于释放设备
下面要说一下的是分配和释放设备号函数
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)
register_chrdev_region()函数用于已知起始设备号的情况,而alloc_chrdev_region()用于设备号未知,是用来向系统动态分配未分配的设备号,函数成功后就会得到一个设备号存在第一个参数dev中
相反释放申请设备号函数如下
void unregister_chrdev_region(dev_t from, unsigned count)
下面的是file_operations结构体
file_operations结构体中的成员函数式字符设备驱动的主体内容,结构体如下:
struct file_operations {
struct module *owner;//拥有该结构体模块的指针,通常为THIS_MODULES
loff_t (*llseek) (struct file *, loff_t, int);//用来修改当前的读写位置
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//从设备中同步读写数据
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备发送数据
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//异步读取数据
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//异步写入数据
int (*readdir) (struct file *, void *, filldir_t);//用于读取目录
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);//执行I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);//打开文件
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);//关闭文件
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
llseek()函数用于修该一个文件当前的读写位置;
read()函数用来从设备中读取数据,成功时返回读取字节数
write()函数用来向设备发送数据,成功时返回写入字节数
ioctl()提供设备相关的控制命令
....
linux字符设备驱动组成
在字符设备模块加载函数中应该实现设备号申请和cdev的注册,而在卸载函数应实现设备号释放和cdev注销
工程师通常习惯为设备定于一个设备相关的结构体,其中包含所涉及的cdev,私有数据及信号量等信息,常见的设备结构体,模块加载和卸载函数形式如下
//设备结构体
struct xxx_dev{
struct cdev cdev;
......
};
struct xxx_dev xxx_dev;
//设备驱动模块加载函数
statrc int __init xxx_init(void)
{
.....
int err,devno = MKDEV(globalmem_major,index);
cdev_init(&xxx_dev.cdev,&gxxx_fops);//初始化cdev并建立cdev和file_operations的链接
xxx->cdev.owner = THIS_MODULE;
if(globalmem_major)
result = register_chrdev_region(devno, 1, "globalmem");//申请设备号
else{
result = alloc_chrdev_region(&devno,0,1,"globalmem");//动态申请设备号
globalmem_major = MAJOR(devno);//获得int的主设备号
ret = cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//注册设备xxx_dev_no是dev_t的设备号
...
}
//设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
unregister_chrdev_region(xxx_dev_no,1);//释放设备号
cdev_del(&xxx_dev,cdev);//注销设备(cdev)
}
字符设备中的file_operations结构体中的成员函数
file_operations结构体中成员函数式字符设备驱动与内核的接口,是用户空间对linux进行系统调用的最终落实者。大多数驱动read(),write(),ioctl()函数如下所示
static ssize_t xxx_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)
{//filp表示文件结构体指针,buf指针指向用户空间,size是要读的字节数,ppos指针是读的位置相对文件开头
...
copy_to_user(buf,...,...);//内核空间到用户空间
...
}
//写函数用户空间到内核空间
static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
{//filp表示文件结构体指针,buf指针指向用户空间,size是要读的字节数,ppos指针是写的位置相对文件开头
...
copy_from_user(...,buf,...);//用户空间到内核空间
...
}
static int xxx_ioctl(struct inode *inodeep,struct file *filp,unsigned int cmd,unsigned long arg)
{
...
switch (cmd){
case xxx_CMD1;
...
break;
case xxx_CMD2;
...
break;
default:
...
return -ENOTTY;
}
return 0;
0 0
- 驱动字符设备的基础
- 字符设备驱动基础篇4——字符设备驱动读写接口的操作实践
- Linux实现字符设备驱动的基础步骤
- Linux设备驱动开发基础---字符设备驱动程序开发之基于中断的按键驱动
- 整理--Linux字符设备驱动开发基础
- Linux字符设备驱动开发基础
- 基本的字符设备驱动
- jz2440的字符设备驱动
- 字符设备驱动的改进
- [设备驱动] 最简单的内核设备驱动--字符驱动
- linux 驱动-----字符设备驱动的组成
- 字符设备驱动基础篇1——简单的驱动源码分析
- Linux设备驱动开发基础---字符设备驱动程序开发
- 简图记录-linux设备驱动-字符设备基础
- 以杂项设备驱动的方法注册字符设备驱动
- 字符设备驱动基础篇3——字符设备驱动工作原理
- 标准的字符设备驱动的模板
- 字符设备驱动高级篇3——自动创建字符设备驱动的设备文件
- Verilog数组表示及初始化
- 苹果低调处理"弯弯门" 新机重于早期版
- OutputStream(装饰模式)
- 最优化基础和机器学习优化
- Android如何支持多种屏幕
- 驱动字符设备的基础
- 抓包分析DLNA——(1)设备发现
- [leetcode]Reverse Integer
- 【模电】电阻
- linux沙箱(sandbox)技术_百度文库
- Ubuntu 14.04 登陆界面一闪 返回登陆界面
- DOM和SAX的比较
- android自定义对话框
- 通用数据采集平台,从架构到代码