linux设备驱动学习(3) 字符设备驱动程序
来源:互联网 发布:php eval 安全 编辑:程序博客网 时间:2024/06/05 10:33
主设备号,次设备号
主设备号表示设备对应的驱动程序;次设备号由内核使用,用于正确确定设备文件所指的设备。
内核用dev_t类型(<linux/types.h>)来保存设备编号,dev_t是一个32位的数,12位表示主设备号,20为表示次设备号。
在实际使用中,是通过<linux/kdev_t.h>中定义的宏来转换格式。
MINOR(dev_t dev) 主设备号、次设备号-->(dev_t) MKDEV(int major,int minor)
分配和释放设备编号
#include<linux/fs.h>
静态指定int register_chrdev_region(dev_t first,unsigned int count,char *name);动态分配int alloc_chrdev_region(dev *dev,unsigned int firstminor,unsigned int count,char *name);释放设备号void unregister_chrdev_region(dev_t first,unsigned int count);分配之设备号的最佳方式是:默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。
以下是在scull.c中用来获取主设备好的代码:
if(scull_major)
{
dev=MKDEV(scull_major,scull_minor);
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 not get scull major %d",scull_major);
return result;
}
在这部分中,比较重要的是在用函数获取设备编号后,其中的参数name是和该编号范围关联的设备名称,它将出现在/proc/devices和sysfs中。
看到这里,就可以理解为什么mdev和udev可以动态、自动地生成当前系统需要的设备文件。udev就是通过读取sysfs下的信息来识别硬件设备的.
一些重要的数据结构
file_operations,file,inode
大多数设备驱动程序都会用到的三个重要数据结构。file_operations结构保存了字符驱动程序的方法,struct file表示一个打开的文件,而struct inode表示一个磁盘上的文件。
字符设备的注册
1.获取一个cdev设备
struct cdev *my_dev = cdev_alloc();
my_cdev->ops = &my_fops;
2.初始化分配到的结构
void cdev_init(struct cdev *cdev,struct file_operation *fops);
另外还有一个struct cdev的字段需要初始化。和file_operations结构类似,struct cdev也有一个所有者字段,应被设置为THIS_MODULE。
cdev.owner = THIS_MODULE
3.cdev设置好之后,最后的步骤是通过下面的调用告诉内核该结构的信息:
int cdev_add(struct cdev *dev,dev_t num,unsigend int count);
dev是cdev结构,num是该设备对应的第一个设备 编号,count是应该和设备关联的设备编号的数量。
4.移除一个字符设备
void cdev_del(struct cdev *dev);
以下为scull中的设备注册
struct scull_dev{
struct scull_qset *data;
int quantum;
int qset;
unsigned long size;
unsigend int access_key;
struct semaphore sem;
struct cdev cdev;
}
static void scull_setup_cdev(struct scull_dev *dev,int index)
{
int err,devno=MKDEV(scull_major,scull_minor+index);
cdev_init(&dev->cdev,&scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cev,devno,1);
/*faile gracefully if need be*/
if(err)
printk(KERN_NOTICE "error %d adding scull %d",err,index);
}
open和release
open方法
open方法提供给驱动程序以初始化的能力,从而为以后的操作完成初始化做准备。在大部分驱动程序中,open应完成以下工作:
1.检查设备特定的错误(诸如设备未就绪,或类似的硬件问题)
2.如果设备是首次打开,则对其进行初始化
3.如有必要,更新f_op指针
4.分配并填写置于filp->private_data里的数据结构
open方法原型:int (*open)(struct inode *inode,struct file *filp)
其中的inode参数在其i_cdev字段中包含了我们所需要的信息,即我们先前设置的cdev结构。唯一的问题是,我们通常不需要cdev结构本身,而是希望得到包含cdev结构的scull_dev结构。c语言可帮助程序员通过一些技巧完成这类转换,但不应滥用这类技巧。幸运的是,在这种情况下我们已经实现了这类技巧,它通过定义在<linux/kernel.h>中的container_of宏实现:
container_of(pointer,container_type,container_field);
这个宏要一个container_field字段的指针,该字段包含在container_type类型的结构中,然后返回包含该字段的结构指针。
在scull_open中,这个宏用来找到适当的设备:
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev,cdev);
filp->private_data = dev; /*for other methods*/
而根据scull的实际情况,他的open函数只要完成第四步(将初始化过的struct scull_dev dev的指针传递到filp->private_data里,以备后用)就好了,所以open函数很简单。但是其中用到了定义在<linux/kernel.h>中的container_of宏,源码如下:
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr -offsetof(type,member));})
其实从源码可以看出,其作用就是:通过指针ptr,获得包含ptr所指向数据(是member结构体)的type结构体的指针。即是用指针得到另外一个指针。
release方法提供释放内存,关闭设备的功能。应完成的工作如下:
(1)释放由open分配的、保存在file->private_data中的所有内容;
(2)在最后一次关闭操作时关闭设备。
由于前面定义了scull是一个全局且持久的内存区,所以他的release什么都不做。
read和write
read和write方法的主要作用就是实现内核与用户空间之间的数据拷贝。因为Linux的内核空间和用户空间隔离的,所以要实现数据拷贝就必须使用在<asm/uaccess.h>中定义的:
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,
unsigned long count);
而值得一提的是以上两个函数和
#define __copy_from_user(to,from,n) (memcpy(to,(void __force *)from, n), 0)
#define __copy_to_user(to,from,n) (memcpy((void __force *)to, from, n), 0)
之间的关系:通过源码可知,前者调用后者,但前者在调用前对用户空间指针进行了检查。
至于read和write 的具体函数比较简单,就在实验中验证好了。
- linux设备驱动学习(3) 字符设备驱动程序
- Linux设备驱动程序学习(1)--字符设备驱动
- 【原创】《Linux设备驱动程序》学习之循序渐进 --- 字符设备驱动
- Linux设备驱动--简单字符设备驱动程序
- Linux设备驱动--字符设备驱动程序1
- Linux设备驱动--字符设备驱动程序2
- Linux设备驱动程序学习(3)-字符设备驱动程序
- linux设备驱动学习(5) 高级字符驱动程序操作--ioctl
- Linux驱动学习(二)——字符设备驱动程序入门 .
- Linux驱动学习(四)——高级字符设备驱动程序
- Linux设备驱动程序(第三版)学习之字符驱动(二)
- Linux字符设备驱动程序开发(1)-使用字符设备驱动
- 浅谈linux驱动之-字符设备驱动程序
- Linux驱动程序开发 004- 字符设备驱动
- Linux驱动程序开发 - 字符设备驱动
- Linux设备驱动程序学习(1)-字符设备驱动程序
- Linux设备驱动程序学习(1)-字符设备驱动程序
- Linux设备驱动程序学习(1)-字符设备驱动程序
- 从程序员到技术总监,分享10年开发经验
- Delphi中多线程用Synchronize实现VCL数据同步显示
- Spring FilterBean代理
- Fedora15 安装 ftp
- 重新定位windows窗口程序的标准输出
- linux设备驱动学习(3) 字符设备驱动程序
- sikuli 安装运行 集成进MyEclipse 新手教程
- 品牌与广告
- (未完)C++异常处理
- CentOS + Nginx + PHP + MySQL 环境搭建
- IF 没有 IF (由“不使用IF等条件判断,输出两个数中大的那个”产生的一些随想)
- ubuntu增加swap分区
- Android中级进阶FAQ
- 数据存储过程有点