在linux 2.6内核中,使用cdev结构体描述一个字符设备。定义如下:
struct cdev {
struct kobject kobj; //内嵌的kobject 对象
struct module *owner; //所属模块
struct file_operations *ops; //文件操作结构体
struct list_head list;
dev_t dev; //设备号
unsigned int count ;
};
dev_t 定义了设备号,为32位,其中12位主设备号,20位次设备号。使用如下宏可以获得主设备号和次设备号
MAJOR (dev_t dev )
MINOR (dev_t dev)
使用如下可以将两种设备号生成dev_t
MKDEV(int major,int minor)
linux 2.6 提供了一组函数操作cdev 结构体:
void cdev_init (struct cdev*;struct file_operations *);
//初始化cdev成员,并建立和file_operations之间的连接
struct cdev *cdev_alloc (void );//动态申请一个cdev 内存
void cdev_put (struct cdev *p);
int cdev_add(struct cdev *,dev_t ,unsigned); //向系统添加一个cdev
void cdev_del (struct cdev *);//从系统删除一个cdev
分配和释放设备号
在调用cdev_add()函数向系统注册字符设备之前,首先应该用register_chrdev_region()或者alloc_chrdev_region() 函数向系统申请设备号,原型如下:
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);
相反的,调用cdev_del 函数从系统注销字符设备之后,要使用unregister_chrdev_region() 释放设备号
void unregister_chrdev_region(dev_t from,unsigned count )
file_operations 结构体
file_operations 结构体中的成员函数式字符设备驱动程序的主体内容,这些函数实际会在应用程序进行linux的open ,write ,read ,close 等系统调用时,最终被调用。
file_operations中的几个重要的成员函数分析:
llseek()函数用来修改一个文件当前的读写位置,并返回新的位置
read()函数用来从设备中读取数据
write()函数向设备发送数据
readdir() 函数用于目录,设备节点不需要实现它
ioctl()提供设备相关控制命令的实现
mmap()函数将设备内存映射到进程内存中
open 和release 函数打开和关闭设备
poll()函数用于询问设备是否可被非阻塞的立即读写
aio_read()和aio_write()函数分别对与文件描述符对应的设备进行异步读,写操作。
由于内核空间和用户空间的内存不能直接互访,因此借助了函数copy_from_user() 完成用户空间到内核空间的拷贝,用copy_to_user()完成内核空间到用户空间的拷贝。
unsigned long copy_from_user(void *to,const void __user *from,unsigned long count );
unsigned long copy_to_user(void __user * to,const void *from,unsigned long count );