分类: 嵌入式
2015-05-25 20:34:43
fifth_drv字符驱动之异步通知
15年5月5月8日月8日8日日11:17:14
驱动程序代码:
1
#include
2
#include
3
#include
4
#include
5
#include
6
#include
7
#include
8
#include
9
#include
10
#include
11
#include
12
#include
13
14 static struct class *fifth_drv_class;
15 static struct class_device *fifth_drv_class_dev;
16
17 volatile unsigned long *gpfcon;
18 volatile unsigned long *gpfdat;
19
20 volatile unsigned long *gpgcon;
21 volatile unsigned long *gpgdat;
22
23 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
24 static volatile int ev_press = 0;
25
26 static struct fasync_struct *button_async;
27
28 struct pin_desc{
29 unsigned int pin;
30 unsigned int key_val;
31 };
32
33 static unsigned char key_val;
34
35 static struct pin_desc pins_desc[4] = {
36 {S3C2410_GPF0, 0X01},
37 {S3C2410_GPF2, 0X02},
38 {S3C2410_GPG3, 0X03},
39 {S3C2410_GPG11, 0X04},
40 };
41
42 static irqreturn_t buttons_irq(int irq, void *dev_id)
43 {
44 struct pin_desc * pindesc = (struct pin_desc *)dev_id;
45 unsigned int pinval;
46
47 pinval = s3c2410_gpio_getpin(pindesc->pin);
48
49 if(pinval)
50 {
51 key_val = 0x80 | pindesc->key_val;
52 }
53 else
54 {
55 key_val = pindesc->key_val;
56 }
57
58 ev_press = 1;
59 wake_up_interruptible(&button_waitq);
60
61 kill_fasync(&button_async, SIGIO, POLL_IN);
62
63 return IRQ_RETVAL(IRQ_HANDLED);
64 }
65
66 static int fifth_drv_open(struct inode *inode, struct file *file){
67
68 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
69 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
70 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
71 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
72 return 0;
73 }
74
75 ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){
76
77 if(size != 1)
78 return -EINVAL;
79
80 wait_event_interruptible(button_waitq, ev_press);
81
82 copy_to_user(buf, &key_val, 1);
83
84 ev_press = 0;
85
86 return 1;
87 }
88
89 int fifth_drv_close(struct inode *inode, struct file *file)
90 {
91 free_irq(IRQ_EINT0, &pins_desc[0]);
92 free_irq(IRQ_EINT2, &pins_desc[1]);
93 free_irq(IRQ_EINT11, &pins_desc[2]);
94 free_irq(IRQ_EINT19, &pins_desc[3]);
95 }
96
97 static unsigned fifth_drv_poll (struct file *file, poll_table *wait)
98 {
99 unsigned int mask = 0;
100 poll_wait(file, &button_waitq, wait);
101
102 if (ev_press)
103 mask |= POLLIN | POLLRDNORM;
104
105 return mask;
106 }
107
108 static int fifth_drv_fasync(int fd, struct file *filp, int on)
109 {
110 printk("dirve : fifth_drv_fasync\n");
111 return fasync_helper(fd, filp, on, &button_async);
112 }
113
114 static struct file_operations fifth_drv_fops = {
115 .owner = THIS_MODULE,
116 .open = fifth_drv_open,
117 .read = fifth_drv_read,
118 .release = fifth_drv_close,
119 .poll = fifth_drv_poll,
120 .fasync = fifth_drv_fasync,
121 };
122
123 int major;
124 static int __init fifth_drv_init(void){
125 major = register_chrdev(0, "fifth_drv", &fifth_drv_fops);
126
127 fifth_drv_class = class_create(THIS_MODULE, "fifth_drv");
128 fifth_drv_class_dev = class_device_create(fifth_drv_class, NULL, MKDEV(major, 0), NULL, "buttons");
129
130 gpfcon = (unsigned long *)ioremap(0x56000050, 16);
131 gpfdat = gpfcon + 1;
132
133 gpgcon = (unsigned long *)ioremap(0x56000060, 16);
134 gpgdat = gpgcon + 1;
135 return 0;
136 }
137
138 static void __exit fifth_drv_exit(void){
139 unregister_chrdev(major, "fifth_drv");
140
141 class_device_unregister(fifth_drv_class_dev);
142 class_destroy(fifth_drv_class);
143
144 iounmap(gpfcon);
145 iounmap(gpgcon);
146
147 return 0;
148 }
149
150 module_init(fifth_drv_init);
151 module_exit(fifth_drv_exit);
152
153 MODULE_LICENSE("GPL");
测试程序:
1
#include
2
#include
3
#include
4
#include
5
#include
6
#include
7
#include
8
#include
9
#include
10
11 int fd;
12
13 void my_signal_fun(int signum)
14 {
15 unsigned char key_val;
16 read(fd, &key_val, 1);
17 printf("key_val: 0x%x\n", key_val);
18 }
19
20 int main (int argc, char **argv)
21 {
22 unsigned char key_val;
23 int ret;
24 int Oflags;
25
26 signal(SIGIO, my_signal_fun);
27
28 fd = open("/dev/buttons",O_RDWR);
29
30 if (fd < 0)
31 printf("cannot open!\n");
32
33 fcntl(fd, F_SETOWN, getpid());
34 Oflags = fcntl(fd, F_GETFL);
35 fcntl(fd, F_SETFL, Oflags | FASYNC);
36
37 while (1)
38 {
39 sleep(1000);
40 }
41 return 0;
42 }
通过使用异步通知,应用程序可以在数据可用时接受到一个信号,而不需要不停地使用轮询来关注数据。那么,怎么发信号?需要(1)应用程序注册信号处理函数(2)谁发?驱动发(3)发给谁?发送给应用程序,但是驱动怎么知道发送给哪一个应用程序,这时候需要应用程序告诉驱动它的PID。(4)怎么发?使用内核提供的函数kill_fasync()。
从应用程序的角度考虑:
首先,指定一个进程作为文件的“属主(owner)”,即把进程的PID告诉驱动,让内核知道该通知哪一个进程,这时候需要用到fcntl函数调用F_SETOWN命令实现,在电脑上man fcntl可以查看fcntl函数的相关信息。
第二:为了真正启用异步通知机制,应用程序必须在设备中设置FASYNC标志,这时候需要用到fcntl函数调用F_SETFL命令实现。
fcntl(fd, F_SETOWN, getpid()); //把设备的PID 号告诉驱动
Oflags = fcntl(fd, F_GETFL); //读出原来的oflags
fcntl(fd, F_SETFL, Oflags | FASYNC); //修改这个oflags
其中 Oflags是file结构体中的一个参数。
第三:在应用程序中注册信号函数,signal(SIGIO,处理函数),可以用man signal查看函数的使用方法。
signal(SIGIO, my_signal_fun);
其中 my_signal_fun为信号处理函数,如下所示:
void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}
刚开始的时候,写了一个signal函数测试一下,使用kill -10 PID 来发送信号,结果如下所示:
从驱动程序的角度考虑:
(1)先定义 struct fasync_struct这个结构体,其中包含进程PID,但是没有初始化。
(2)当一个文件的FASYNC 标志被修改时,调用fasync_helper函数去初始化 fasync_struct这个结构体,初始化以后,中断函数就可以使用kill_fasync了。
在驱动程序中如下方式调用fasync_helper函数:
static int fifth_drv_fasync(int fd, struct file *filp, int on)
{
printk("dirve : fifth_drv_fasync\n");
return fasync_helper(fd, filp, on, &button_async);
}
(3)数据到达时,可以使用kill_fasync通知注册的进程,我们的目标是在按下按键的时候,驱动去通知应用,所以,这个通知函数就在中断程序static irqreturn_t buttons_irq(int irq, void *dev_id)中,
kill_fasync(&button_async, SIGIO, POLL_IN); 。函数原型如下所示,它的第一个参数是定义的结构体,第二个参数是要发送的信号,一般是SIGIO表示I/O口有数据可供读写,第三个参数是带宽,这个参数几乎总是 POLL_IN。
驱动程序要调用的两个函数原型如下:
int fasync_helper (int fd, struct file *filp, int mode, struct fasync_struct ** fa);
void kill_fasync (struct fasync_struct ** fa, int sig, int band);
总结一下这个驱动程序跟测试程序的流程:
(1)首先安装驱动程序,insmod fifth_drv.ko
(2)然后,执行测试程序,这时候,测试程序注册了信号函数,通知驱动程序它的PID,设置了O_flags,同时,驱动程序监测到测试程序的FASYNC标志被修改,就调用.fasync = fifth_drv_fasync,这个函数,打印出来“dirve : fifth_drv_fasync ”这句话,并且调用fasync_helper函数去初始化 fasync_struct这个结构体。
(3)按下按键, 发生中断,就会执行buttons_irq函数,在 buttons_irq函数中,驱动通过执行kill_fasync(&button_async, SIGIO, POLL_IN);发送给应用程序。
(4)应用程序接收到 SIGIO信号,调用 my_signal_fun函数,在 my_signal_fun函数中去读取按键值,并且打印出来,我们就看到屏幕上输出按键值信息。