6---Linux字符设备驱动结构

       字符设备驱动程序中完成的主要工作是初始化、添加和删除cdev结构体,申请和释放设备号,以及填充file_operations结构体中的操作函数,实现file_operations结构体中的read()、write()和ioctl()等函数是驱动设计的主体工作。

cdev结构体  用于描述字符设备

1 struct cdev{
3 struct kobject kobj; /* 内嵌的kobject对象*/
4 struct module *owner; /*所属模块*/
5 struct file_operations *ops; /*文件操作结构体,定义了字符设备驱动提供给虚拟文件系统的接口函数*/
6 struct list_head list;
7 dev_t dev; /*32 位设备号,其中高12 位为主设备号,低20 位为次设备号*/
8 unsigned int count;
9 };

从dev_t获得主设备号和次设备号

MAJOR(dev_t dev)
MINOR(dev_t dev)

通过主设备号和次设备号生成dev_t

MKDEV(int major, int minor)

操作cdev结构体的函数

void cdev_init(struct cdev *, struct file_operations *);
//初始化cdev 的成员,并建立cdev 和file_operations 之间的连接

struct cdev *cdev_alloc(void);
//动态申请一个cdev内存

int cdev_add(struct cdev *, dev_t, unsigned);
//向系统添加一个cdev,完成字符设备的注册。

void cdev_del(struct cdev *);
//向系统删除一个cdev,完成字符设备的注销。

调用cdev_add() 函数向系统注册字符设备之前,首先应该分配设备号,

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() 函数从系统注销字符设备之后,应释放原先申请的设备号

void unregister_chrdev_region(dev_t from, unsigned count);

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

1 //设备结构体
2 struct xxx_dev_t {
4 struct cdev cdev;
5 ...
6 } xxx_dev;
7 //设备驱动模块加载函数
8 static int _ _init xxx_init(void){
10 ...
11 cdev_init(&xxx_dev.cdev, &xxx_fops); //初始化cdev
12 xxx_dev.cdev.owner = THIS_MODULE;

13 //获取字符设备号
14 if (xxx_major){
16 register_chrdev_region(xxx_dev_no, 1, DEV_NAME);//静态分配设备号
17 }
18 else {
20 alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);//动态分配设备号
21 }
23 ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); //注册设备
24 ...
25 }
26 /*设备驱动模块卸载函数*/
27 static void _ _exit xxx_exit(void){
29 unregister_chrdev_region(xxx_dev_no, 1); //释放占用的设备号
30 cdev_del(&xxx_dev.cdev); //注销设备
31 ...
32 }

file_operations结构体

1 struct file_operations {
3 struct module *owner;
4 // 拥有该结构的模块的指针,一般为THIS_MODULES

5 loff_t(*llseek)(struct file *, loff_t, int);
6 // 用来修改文件当前的读写位置,并将新位置返回,在出错时,返回负值

7 ssize_t(*read)(struct file *, char _ _user *, size_t, loff_t*);
8 // 从设备中同步读取数据,成功时返回读取的字节数,出错时返回负值

9 ssize_t(*write)(struct file *, const char _ _user *, size_t,loff_t*);
10 // 向设备发送数据,成功时返回写入的字节数。如果此函数未被实现,调用时返回-EINVAL值

11 ssize_t(*aio_read)(struct kiocb *, char _ _user *, size_t, loff_t);
13 ssize_t(*aio_write)(struct kiocb *, const char _ _user *, size_t,loff_t);
14 // 初始化一个异步的读取/写入操作后,用户空间可调用aio_read()、aio_write()进行读写。

15 int(*readdir)(struct file *, void *, filldir_t);
16 // 仅用于读取目录,对于设备文件,设备节点不需要实现它,该字段为NULL,

17 unsigned int(*poll)(struct file *, struct poll_table_struct*);
18 // 轮询函数,判断目前是否可以进行非阻塞的读取或写入

19 int(*ioctl)(struct inode *, struct file *, unsigned int, unsignedlong);
20 // 执行设备I/O控制命令,调用成功时,返回非负值,防止设备中有内核不能识别的命令

21 long(*unlocked_ioctl)(struct file *, unsigned int, unsigned long);
22 // 不使用BLK文件系统,将使用此种函数指针代替ioctl

23 long(*compat_ioctl)(struct file *, unsigned int, unsigned long);
24 // 在64位系统上,32位的ioctl调用将使用此函数指针代替

25 int(*mmap)(struct file *, struct vm_area_struct*);
26 // 用于请求将设备内存映射到进程地址空间,针对于帧缓冲等设备

27 int(*open)(struct inode *, struct file*);
28 // 打开

29 int(*flush)(struct file*);

30 int(*release)(struct inode *, struct file*);
31 // 关闭

32 int(*synch)(struct file *, struct dentry *, int datasync);
33 // 刷新待处理的数据

34 int(*aio_fsync)(struct kiocb *, int datasync);
35 // 异步fsync

36 int(*fasync)(int, struct file *, int);
37 // 通知设备FASYNC标志发生变化

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

39 ssize_t(*readv)(struct file *, const struct iovec *, unsigned long,loff_t*);
40 ssize_t(*writev)(struct file *, const struct iovec *, unsigned long,loff_t*);
41 // readv和writev:分散/聚集型的读写操作

42 ssize_t(*sendfile)(struct file *, loff_t *, size_t, read_actor_t,void*);
43 // 通常为NULL

44 ssize_t(*sendpage)(struct file *, struct page *, int, size_t,loff_t *, int);
45 // 通常为NULL

46 unsigned long(*get_unmapped_area)(struct file *,unsigned long,unsigned long,
                                     unsigned long, unsigned long);
48 // 在进程地址空间找到一个将底层设备中的内存段映射的位置

49 int(*check_flags)(int);
50 // 允许模块检查传递给fcntl(F_SETEL...)调用的标志

51 int(*dir_notify)(struct file *filp, unsigned long arg);
52 // 仅对文件系统有效,驱动程序不必实现

53 int(*flock)(struct file *, int, struct file_lock*);
54 };

file_operations 结构体中成员函数是字符设备驱动与内核的接口,是用户空间对Linux 进行系统调用最终的落实者。在应用程序进行Linux 的open()、write()、read()、close()等系统调用时会调用file_operations结构体的成员函数。大多数字符设备驱动会实现read()、write()和ioctl()函数。

 /* 读设备*/
 ssize_t xxx_read(struct file *filp, char _ _user *buf, size_t count,loff_t*f_pos) {
 ...
copy_to_user(buf, .., ..);//内核空间与用户空间的内存不能直接互访,完成内核空间到用户空间的复制
 ...
 }
/* 写设备*/
 ssize_t xxx_write(struct file *filp, const char _ _user *buf, size_tcount, loff_t *f_pos){
 ...
copy_from_user(.., buf, ..);//内核空间与用户空间的内存不能直接互访,完成用户空间到内核空间的复制
 ...
 }
//在读写函数中,filp是文件结构体指针,buf是用户空间内存的地址,该地址在内核空间不能直接读写,
//count 是要读写的字节数,f_pos 是读写的位置相对于文件开头的偏移。


 /* ioctl函数*/
 int xxx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) {
 ...
switch (cmd){
 case XXX_CMD1:
 ...
 break;
 case XXX_CMD2:
 ...
 break;
 default:
 /* 不能支持的命令*/
 return - ENOTTY;
 }
 return 0;
 }

copy_from_user()和copy_to_user()的原型如下所示:

//如果完全复制成功,返回值为0。
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);

//如果要复制的内存是简单类型,如char、int、long
get_user(val, (int *) arg); //用户空间到内核空间, val是内核空间整型变量,arg是用户空间的地址
put_user(val, (int *) arg); //内核空间到用户空间, val是内核空间整型变量,arg是用户空间的地址

 字符设备驱动文件操作结构体模板

1 struct file_operations xxx_fops = {
3 .owner = THIS_MODULE,
4 .read = xxx_read,
5 .write = xxx_write,
6 .ioctl = xxx_ioctl,
7 ...
8 };

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值