Linux设备驱动(三)-file_operations结构体

字符设备驱动一旦注册完后就会进入file_operations结构体对设备操作进行定义。
file_operations:是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,把系统调用与驱动程序关联起来的关键数据结构,这个结构体的每个成员都对应着一个系统调用。
传统上, 一个 file_operations结构或者其一个指针称为 fops( 或者它的一些变体). 结构中的每个成员必须指向驱动中的函数, 这些函数实现一个特别的操作, 或者对于不支持的操作留置为 NULL. 当指定为 NULL 指针时内核的确切的行为是每个函数不同的。
file_operations中不少参数包含字串”__user”,这种注解是一种文档形式, 注意, 一个指针是一个不能被直接解引用的用户空间地址. 对于正常的编译”__user”没有效果, 但是它可被外部检查软件使用来找出对用户空间地址的错误使用。
下面看看file_operations中的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct file_operations {
	struct module *owner;
	//所属模块,新注册的设备必须由"THIS_MODULE"这个值来指定
	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);
	//仅用于读取目录,对于设备文件,该字段为NULL
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	//轮询函数,判断目前是否可以进行非阻塞的读取或者写入操作
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	//不使用BLK文件系统,将使用此钟函数指针代替ioctl
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	//在64位系统上,32位的ioctl调用将使用此函数指针代替
	int (*mmap) (struct file *, struct vm_area_struct *);
	//用于请教设备内存映射到进程地址空间
	int (*open) (struct inode *, struct file *);
	//打开函数
	int (*flush) (struct file *, fl_owner_t id);
	//发生在进程关闭设备文件描述符副本,执行并等待,若设置为NULL,内核将忽略用户应用程序的请求
	int (*release) (struct inode *, struct file *);
	//关闭函数
	int (*fsync) (struct file *, int datasync);
	//刷新等待处理的数据
	int (*aio_fsync) (struct kiocb *, int datasync);
	//异步的fsync
	int (*fasync) (int, struct file *, int);
	//通知设备FASYNC标志发生变化
	int (*lock) (struct file *, int, struct file_lock *);
	// 实现文件锁,设备驱动常不去实现此lock
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	//内核调用将其数据发送到对应文件,每次一个数据页,设备驱动通常将其设置为NULL
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	//允许模块检查船体给fcntl(F_SETEL...)调用的标志
	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);
	//readv和weritev:分散/聚集型的读写操作
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
};

一般比较常用的有read,write,open,llseek,unlocked_ioctl,poll和mmap。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int (*open)(struct inode *inode, struct file *filp);
功能:打开文件指针为filp,文件节点为inode的文件
 
int (*release) (struct inode *inode, struct file *filp);
功能:关闭文件指针为filp,文件节点为inode的文件
 
ssize_t (*read) (struct file *filp, char __user *buff, size_t count, loff_t *offp);
功能:把文件指针filp所指定文件的当前位置offp中读取count大小的数据到buff所指向的缓冲区
 
ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
功能:把buff所指向的缓冲区中count大小的数据写入到文件指针filp所指定文件的当前位置offp中
 
unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait);
功能:如果进程发生阻塞则则使用poll_wait()函数把指定的等待队列添加到wait中,并返回描述设备的掩码
掩码取值:
POLLIN		设备可读
POLLRDNORM	数据可读
POLLOUT	设备可写
POLLWRNORM	数据可写
 
loff_t (*llseek) (struct file *filp, loff_t p, int where);
功能:定位文件指针filp的起始地址where中目标偏移P的位置
where取值:
SEEK_SET	0	文件开头
SEEK_CUR	1	当前位置
SEEK_END	2	文件末尾

然活我们通过一个驱动列子来对比这些成员的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/gpio.h>
 
#define DEVICE_NAME "buttons"
 
static struct cdev *buttons_cdev;
 
static dev_t devno;
 
static volatile char keystatus[]= {'0', '0', '0', '0', '0', '0'};
 
static volatile int press = 0;
 
static DECLARE_WAIT_QUEUE_HEAD(buttons_wait);
 
struct buttons_irq_desc
{
	int irq;
	int pin;
	int pin_set;
	int number;
	char *name;
};
 
static struct buttons_irq_desc buttons_irq[]=
{
	{IRQ_EINT8 ,S3C2410_GPG(0),S3C2410_GPG0_EINT8 ,0,"KEY0"},
	{IRQ_EINT11,S3C2410_GPG(3),S3C2410_GPG3_EINT11,1,"KEY1"},
	{IRQ_EINT13,S3C2410_GPG(5),S3C2410_GPG5_EINT13,2,"KEY2"},
	{IRQ_EINT14,S3C2410_GPG(6),S3C2410_GPG6_EINT14,3,"KEY3"},
	{IRQ_EINT15,S3C2410_GPG(7),S3C2410_GPG7_EINT15,4,"KEY4"},
	{IRQ_EINT19,S3C2410_GPG(11),S3C2410_GPG11_EINT19,5,"KEY5"},
};
 
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
	struct buttons_irq_desc *buttons_irq = (struct buttons_irq_desc *)dev_id;
	int down;
	down=!s3c2410_gpio_getpin(buttons_irq->pin);
	if(down!=(keystatus[buttons_irq->number] & 1))
	{
		keystatus[buttons_irq->number]='0'+down;
		press=1;
		wake_up_interruptible(&buttons_wait);
	}
	return IRQ_RETVAL(IRQ_HANDLED);
}
 
static int buttons_open(struct inode *inode,struct file *filp)
{
	int i;
	int err=0;
	for (i = 0; i < 6; i++)
	{
		if(buttons_irq[i].irq<0)
		{
			continue;
		}
		err=request_irq(buttons_irq[i].irq,buttons_interrupt,IRQ_TYPE_EDGE_BOTH,buttons_irq[i].name,(void *)&buttons_irq[i]);
		if(err)
			break;
	}
	if(err)
	{
		i--;
		for(; i>=0; i--)
		{
			if (buttons_irq[i].irq < 0)
			{
				continue;
			}
			disable_irq(buttons_irq[i].irq);
			free_irq(buttons_irq[i].irq,(void *)&buttons_irq[i]);
		}
		return -EBUSY;
	}
	press = 1;
	return 0;
}
 
static int buttons_read(struct file *filp,char __user *buff,size_t count, loff_t *offp)
{
	unsigned long err;
	if (!press)
	{
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		else
			wait_event_interruptible(buttons_wait, press);
	}
	press = 0;
	err = copy_to_user(buff, (const void *)keystatus, min(sizeof(keystatus), count));
	return err ? -EFAULT : min(sizeof(keystatus), count);
}
 
static unsigned int buttons_poll( struct file *filp, struct poll_table_struct *wait)
{
	unsigned int mask = 0;
	poll_wait(filp, &buttons_wait, wait);
	if (press)
		mask |= POLLIN | POLLRDNORM;
	return mask;
}
 
static int buttons_close(struct inode *inode, struct file *filp)
{
	int i;
	for (i = 0; i < 6; i++)
	{
		if (buttons_irq[i].irq < 0)
		{
			continue;
		}
		free_irq(buttons_irq[i].irq, (void *)&buttons_irq[i]);
	}
	return 0;
}
 
static struct file_operations buttons_fops=
{
	.owner   = THIS_MODULE,
	.read	= buttons_read,
	.open	= buttons_open,
	.release = buttons_close,
	.poll	=   buttons_poll,
};
 
static int __init buttons_init(void)
{
	int ret,err;
	int major_no,monir_no;
	ret=alloc_chrdev_region(&devno,0,1,DEVICE_NAME);
	if(ret)
		return ret;
	buttons_cdev=cdev_alloc();
	cdev_init(buttons_cdev,&buttons_fops);
	err=cdev_add(buttons_cdev,devno,1);
	if(err)
	{
		unregister_chrdev_region(devno,1);
		return -EFAULT;
	}
	major_no=MAJOR(devno);
	monir_no=MINOR(devno);
	printk(KERN_NOTICE "Major is: %d , Monir is: %d.\n", major_no, monir_no);
	printk(DEVICE_NAME "\tinitialized!\n");
	return 0;
}
 
static void __exit buttons_exit(void)
{
	cdev_del(buttons_cdev);
	unregister_chrdev_region(devno,1);
	printk(DEVICE_NAME "\tunloaded!\n");
}
 
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yayi");

发表评论

电子邮件地址不会被公开。 必填项已用*标注