全部博文(404)
分类: LINUX
2008-09-08 12:31:52
1. 主设备号和次设备号.
1.介绍
对字符设备的访问是通过文件系统内的设备文件进行的. 这些文件位于/dev.
设备通过设备号来标识:
主设备号: 标识设备对应的驱动程序.在linux里,允许多个驱动程序共用一个主设备号.
次设备号: 用于确定设备文所指的设备.
在内核中,用dev_t类型>保存设备号.定义在
2.4 kernel 采用16位设备号(8 bit 主,8 bit 次);
2.6 kernel 采用 32位设备号(12 bit 主,20 bit 次);
在驱动程序中访问设备号应用
MAJOR(dev_t dev);
MINOR(dev_t dev);
MKDEV(int major,int minor);
2. 分配.释放设备编号
在建立设备前,应先获的设备号.
分配:
#include
Int register_chrdev_region(dev_t first,unsigned int count,char *name)
First :要分配的设备编号范围的初始值(次设备号常设为0);
Count:连续编号范围.
Name:编号相关联的设备名称. (/proc/devices);
动态分配:
Int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
Firstminor : 通常为0;
*dev:存放返回的设备号;
释放:
Void unregist_chrdev_region(dev_t first,unsigned int count);
调用Documentation/devices.txt中可以找到已经分配的设备号.
3. 建立设备文件:
当设备驱动模块申请了主设备号,次设备号,并insmod 加载到内核中后.就可以在/dev下创建 设备了:
Mknod /dev/mychar c major minor
2. 字符设备的基本数据结构
3个基本的数据结构file,file_operations,inode 都定义在
1.file_operations 数据结构
结构中包含了若干涵数指针.用户空间调用的open,write 等涵数,最终会调用到结构中的指针所指向的涵数.
2.6内核初始化:
Struct file_operations my_fops = {
.owner = THIS_MODULE,
.llseek = my_llseek,
.read = my_read, .....
}
2.4内核初始化:
Struct file_operations my_fops = {
Owner: THIS_MODELE,
Llseek:my_llseek,
....
}
2.File 结构
File 是一个内核结构体,和用户open一个文件后返回的文件描述符fd相对应.内核在open时 建file结构,并传递给该文件上进行操作的所有涵数,直到close,kernel释放这个结构.
结构中主要成员有:
Mode_t f_mode; 通过FMODE_READ,FMODE_WRITE标示文件是否可读可写.
Loff_t f_pos; 当前读写位置.Loff_t为64位.
Unsigned int f_flags: 文件标志.如O_RDONLY,O_NONBLOCK.....
Struct file_operations *f_op; 与文件相关的操作.内核在执行open时对指针赋值.可以在驱动 open方法中根据次设备号赋予不同的f_op;
Void * private; 通常表示硬件设备结构体.
Struct denty *f_dentry; 文件对应的目录项结构.可通过filp->f_dentry->d_inode访问索引节点.
3.inode 结构 < linux/fs.h >
内核用inode结构描述一个实际的文件,可以是普通文件,也可以是设备文件.每个文件只有一 个inode结构,而和文件描述符对应的file结构可以有多个.这些file结构指向同一个inode结 构.
结构成员有:
Dev_t i_rdev; 设备文件的inode结构,包含真正的设备编号.
Struct cdev *i_cdev; 字符设备的内核结构.
下面的宏从inode获取设备号:
Unsigned int iminor(struct inode *inode);
Unsigned int imajor(struct inode *inode);
3.字符设备的注册和注销
内核中使用cdev 结构来表示一个字符设备。
注册步骤:
1. 在设备结构中加入cdev
Struct scull_dev {
….
Struct cdev cdev;
}
2.初始化
Void cdev_init(strcut cdev *cdev,struct file_operations *fops);
3. 设定cdev中的内容
Dev->cdev.owner = THIS_MODULE;
Dev->cdev.ops = &scull_fops;
4 向内核中添加设定好的cdev
Int cdev_add(struct cdev *dev,dev_t num,unsigned int count);
Num:设备对应的第一个编号。
Count :和设备关联的设备编号的数量。常取1
一旦cdev_add 返回,内核就认为设备可以用了。所以在调用之前完成设备的硬件初始化。
5 老式的注册和注销函数
2.4kerner
注册:
Int register_chrdev(unsigned int major,const char *name,struct file_operations *fops)
采用用户指定的主设备号major注册。Major 为0 时,向内核动态分配主设备号。0—255 作为次设备号,并为每个设备建立对应的cdev结构。
注销:
Int unregister_chrdev(unsigned int major,const char *name);
4.设备的open and release
1 open
驱动在open方法中完成对硬件(设备)的初始化工作,完成后,用户程序就可以通过write访问设备了。
完成的工作有:
检查设备的特定错误
如果设备首次打开,则对其初始化
如果有必要,更新f_op指针
分配并填写filp->private_data中的数据
Int (*open) ( struct inode * inode,struct file *filp):
在open 中通过inode 获得dev指针,并将其赋给file->private_data。如:(动态分配)
Struct scull_dev *dev;
Dev = contain_of(inode->i_cdev,struct scull_dev,cdev);
Filp->private_data = dev;
如果是静态分配的,则open or write 可以直接访问dev.
2 release
当file的计数器为0时。通过close 调用release ,释放 dev结构。否则用close 关闭。
5. read and write
read and write 是驱动中完成从用户空间copy 数据到内核,或是从内核到用户空间的工作。
Ssize_t read(struct file *filp,char __user *buff,size_f count,loff_t *offp);
Ssize_t write(struct file *filp,const char __user *buff, size_t count,loff_t *offp);
Buff : 用户空间的缓冲区指针。
Offp: 用户在文件中进行存取操作的位置。
在read and write 中,copy data 后,应更新offp.
内核和用户空间交换数据函数:
内核到用户:
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);
向用户空间copy data: 字节数由sizeof(*ptr)决定,成功返回0,负为错误。
Int put_user(datum,ptr);
从用户空间获得数据: 字节数由sizeof(*ptr)决定,
Int get_user(local,ptr)