一.以查询方式实现
1.写出驱动框架
1.1 仿照其他程序加一些必要的头文件
1.2 构造一个结构体file_operations
1.3 根据file_operations的所选项写出所需的函数,并构建出来
1.4 入口函数、出口函数的注册和卸载
1.5 修饰入口函数和出口函数
1.6 给sysfs提供更多的信息,并有udev机制自动创建/dev/xxx设备节点
2.硬件操作
2.1 sch原理图
2.2 2440手册
2.3 编程:物理地址到虚拟地址的映射
1 /*
2 *以查询的方式实现按键驱动程序
3 */
4
5 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include <linux/fs.h>
8 #include <linux/init.h>
9 #include <linux/delay.h>
10 #include <asm/uaccess.h>
11 #include <asm/irq.h>
12 #include <asm/io.h>
13 #include <asm/arch/regs-gpio.h>
14 #include <asm/hardware.h>
15
16
17 static struct class *g_ptSeconddrvCls;
18 static struct class_device *g_ptSeconddrvClsDev;
19
20 volatile unsigned long *gpfcon;
21 volatile unsigned long *gpfdat;
22
23 volatile unsigned long *gpgcon;
24 volatile unsigned long *gpgdat;
25
26 static int seconddrv_open(struct inode *inode, struct file *file)
27 {
28 /* 配置GPF0,2为输入引脚 */
29 *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
30
31 /* 配置GPG3,11为输入引脚 */
32 *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
33
34 return 0;
35 }
36
37 static ssize_t seconddrv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
38 {
39 /* 返回4个引脚的电平 */
40 unsigned char key_vals[4];
41 int regval;
42
43 if (size != sizeof(key_vals))
44 return -EINVAL;
45
46 /* 读GPF0,2 */
47 regval = *gpfdat;
48 key_vals[0] = (regval & (1<<0)) ? 1 : 0;
49 key_vals[1] = (regval & (1<<2)) ? 1 : 0;
50
51
52 /* 读GPG3,11 */
53 regval = *gpgdat;
54 key_vals[2] = (regval & (1<<3)) ? 1 : 0;
55 key_vals[3] = (regval & (1<<11)) ? 1 : 0;
56
57 copy_to_user(buf, key_vals, sizeof(key_vals));
58
59 return sizeof(key_vals);
60 }
61
62 static struct file_operations seconddrv_fops = {
63 .owner = THIS_MODULE,
64 .open = seconddrv_open,
65 .read = seconddrv_read,
66 };
67
68
69 int g_iMajor;
70 static int seconddrv_init(void)
71 {
72 g_iMajor = register_chrdev(0, "second_drv", &seconddrv_fops);
73
74 g_ptSeconddrvCls = class_create(THIS_MODULE, "second_drv");
75
76 g_ptSeconddrvClsDev = class_device_create(g_ptSeconddrvCls, NULL, MKDEV(g_iMajor, 0), NULL, "buttons"); /* /dev/buttons */
77
78 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
79 gpfdat = gpfcon + 1;
80
81 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
82 gpgdat = gpgcon + 1;
83
84 return 0;
85 }
86
87 static int seconddrv_exit(void)
88 {
89 unregister_chrdev(g_iMajor, "second_drv");
90 class_device_unregister(g_ptSeconddrvClsDev);
91 class_destroy(g_ptSeconddrvCls);
92 iounmap(gpfcon);
93 iounmap(gpgcon);
94 return 0;
95 }
96
97 module_init(seconddrv_init);
98
99 module_exit(seconddrv_exit);
100 MODULE_LICENSE("GPL");
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5
6 /* 2nddrvtest
7 */
8 int main(int argc, char **argv)
9 {
10 int fd;
11 unsigned char key_vals[4];
12 int cnt = 0;
13
14 fd = open("/dev/buttons", O_RDWR);
15 if (fd < 0)
16 {
17 printf("can't open!
");
18 }
19
20 while (1)
21 {
22 read(fd, key_vals, sizeof(key_vals));
23 if (!key_vals[0] || !key_vals[1] || !key_vals[2] || !key_vals[3])
24 {
25 printf("%04d key pressed: %d %d %d %d
", cnt++, key_vals[0], key_vals[1], key_vals[2], key_vals[3]);
26 }
27 }
28
29 return 0;
30 }
二、使用中断方式实现按键驱动程序
使用查询方式实现时,程序一直占用CPU资源,CPU利用率低。
改进的办法:使用中断的方式实现
1.linux中断异常体系分析
1.1 异常向量的设置trap_init()
trap_init被用来设置各种异常的处理向量,trap_init函数将异常向量复制到0xffff0000处
trap_init
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
vector_irq
vector_stub(宏)//计算返回地址,保存一些寄存器
__irq_usr //根据被中断的工作模式进入相应的某个跳转分支->进入管理模式
usr_entry(宏)//保存一些寄存器的值
.maro irq_handler(宏)
asm_do_IRQ //异常处理函数
1.2 异常处理函数asm_do_IRQ
asm_do_IRQ
定义了一个结构体数组desc[NR_IRQS]
irq_enter
desc_handle_irq:desc->handle_irq
1.3 结构数组desc[NR_IRQS]的构建
s3c24xx_init_irq
set_irq_chip //chip=s3c_irq_eint0t4,可设置触发方式,使能中断,禁止中断等底层芯片相关的操作
set_irq_flags
set_irq_handler(irqno, handle_edge_irq)//handle_irq=handle_edge_irq
__set_irq_handler
desc_handle_irq
中断处理函数handle_edge_irq
handle_edge_irq
desc->chip->ack(irq)//清中断
handle_IRQ_event//取出action链表中的成员,执行action->handler
1.4 用户注册中断处理函数的过程request_irq
request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
//用户通过request_irq向内核注册中断处理函数:a.分配了irqaction结构b.调用setup_irq函数
setup_irq(irq,action)//将新建的irqaction结构链入irq_desc[irq]结构的action链表中P405
desc->chip->settype//定义引脚为中断引脚
desc->chip->startup/enable//引脚使能
1.5 free_irq(irq,dev_id)//卸载中断服务程序
a.出链
b.禁止中断
1.6 总结:handle_irq是这个中断的处理函数入口,发生中断时,总入口函数asm_do_IRQ函数将根据中断号调用相应irq_desc数组项的handle_irq。handle_irq使用chip结构体中的函数来清除、屏蔽或者重新enable中断,还一一调用用户在action链表中注册的中断处理函数。
2.编写代码
1 /*
2 *用中断方式实现按键驱动程序
3 */
4
5 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include <linux/fs.h>
8 #include <linux/init.h>
9 #include <linux/delay.h>
10 #include <linux/irq.h>
11 #include <asm/uaccess.h>
12 #include <asm/irq.h>
13 #include <asm/io.h>
14 #include <asm/arch/regs-gpio.h>
15 #include <asm/hardware.h>
16
17
18 static struct class *g_ptThirddrvCls;
19 static struct class_device *g_ptThirddrvClsDev;
20
21 volatile unsigned long *gpfcon;
22 volatile unsigned long *gpfdat;
23
24 volatile unsigned long *gpgcon;
25 volatile unsigned long *gpgdat;
26
27 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
28
29 /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
30 static volatile int ev_press = 0;
31
32 struct pin_desc{
33 unsigned int pin;
34 unsigned int key_val;
35 };
36
37 static unsigned char key_val;
38
39 struct pin_desc pins_desc[4] = {
40 {S3C2410_GPF0, 0x01},
41 {S3C2410_GPF2, 0x02},
42 {S3C2410_GPG3, 0x03},
43 {S3C2410_GPG11, 0x04},
44 };
45
46
47 /*
48 * 确定按键值
49 */
50 static irqreturn_t buttons_irq(int irq, void *dev_id)
51 {
52 struct pin_desc * pindesc = (struct pin_desc *)dev_id;
53 unsigned int pinval;
54
55 pinval = s3c2410_gpio_getpin(pindesc->pin);
56
57 if (pinval)
58 {
59 /* 松开 */
60 key_val = 0x80 | pindesc->key_val;
61 }
62 else
63 {
64 /* 按下 */
65 key_val = pindesc->key_val;
66 }
67
68 ev_press = 1; /* 表示中断发生了 */
69 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
70
71
72 return IRQ_RETVAL(IRQ_HANDLED);
73 }
74
75 static int thirddrv_open(struct inode *inode, struct file *file)
76 {
77 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
78 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
79 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
80 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
81
82 return 0;
83 }
84
85 static ssize_t thirddrv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
86 {
87
88 if (size != 1)
89 return -EINVAL;
90
91 /* 如果没有按键动作, 休眠 */
92 wait_event_interruptible(button_waitq, ev_press);
93
94 /* 如果有按键动作, 返回键值 */
95 copy_to_user(buf, &key_val, 1);
96 ev_press = 0;
97
98 return 1;
99 }
100
101 static int thirddrv_close (struct inode * inode, struct file * file)
102 {
103 free_irq(IRQ_EINT0,&pins_desc[0]);
104 free_irq(IRQ_EINT2,&pins_desc[1]);
105 free_irq(IRQ_EINT11,&pins_desc[2]);
106 free_irq(IRQ_EINT19,&pins_desc[3]);
107
108 return 0;
109 }
110
111 static struct file_operations thirddrv_fops = {
112 .owner = THIS_MODULE,
113 .open = thirddrv_open,
114 .read = thirddrv_read,
115 .release = thirddrv_close,
116 };
117
118
119 int g_iMajor;
120 static int thirddrv_init(void)
121 {
122 g_iMajor = register_chrdev(0, "third_drv", &thirddrv_fops);
123
124 g_ptThirddrvCls = class_create(THIS_MODULE, "third_drv");
125
126 g_ptThirddrvClsDev = class_device_create(g_ptThirddrvCls, NULL, MKDEV(g_iMajor, 0), NULL, "buttons"); /* /dev/buttons */
127
128 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
129 gpfdat = gpfcon + 1;
130
131 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
132 gpgdat = gpgcon + 1;
133
134 return 0;
135 }
136
137 static int thirddrv_exit(void)
138 {
139 unregister_chrdev(g_iMajor, "third_drv");
140 class_device_unregister(g_ptThirddrvClsDev);
141 class_destroy(g_ptThirddrvCls);
142 iounmap(gpfcon);
143 iounmap(gpgcon);
144 return 0;
145 }
146
147 module_init(thirddrv_init);
148
149 module_exit(thirddrv_exit);
150 MODULE_LICENSE("GPL");
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <unistd.h>
6
7 /* thirddrvtest
8 */
9 int main(int argc, char **argv)
10 {
11 int fd;
12 unsigned char key_val;
13
14 fd = open("/dev/buttons", O_RDWR);
15 if (fd < 0)
16 {
17 printf("can't open!
");
18 }
19
20 while (1)
21 {
22 //read(fd, &key_val, 1);
23 //printf("key_val = 0x%x
", key_val);
24 sleep(5);
25 }
26
27 return 0;
28 }
三、在上述基础上添加poll机制
虽然我们实现了中断式的按键驱动,但是我们发觉,测试程序是一个无限的循环。在实际应用中并不存在这样的情况,所以我们要进一步优化——poll机制。
1.poll机制的分析
1.1 函数原型
int poll(struct pollfd *fds,nfds_t nfds, int timeout)
//Poll机制会判断fds中的文件是否可读,如果可读则会立即返回,返回的值就是可读fd的数量,如果不可读,那么就进程就会
休眠timeout这么长的时间,然后再来判断是否有文件可读,如果有,返回fd的数量,如果没有,则返回0.
1.2 poll机制在内核实现分析:
应用程序调用poll
sys_poll
do_sys_poll
poll_initwait(&table)//table->pt->qproc = qproc = __pollwait
do_poll(nfds, head, &table, timeout)
for (;;) {
if (do_pollfd(pfd, pt)) { //return mask;mask=file->f_op->poll(file,pwait)
count++;
pt = NULL;
}
if (count || !*timeout || signal_pending(current))
break;//break的条件是:count非0,超时,有信号在等待处理
__timeout = schedule_timeout(__timeout)//休眠
}
1.3 总结
①poll > sys_poll > do_sys_poll >poll_initwait,poll_initwait函数注册一下回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数。
②接下来执行file->f_op->poll,即我们驱动程序里自己实现的poll函数它会调用poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的;它还判断一下设备是否就绪。
③如果设备未就绪,do_sys_poll里会让进程休眠一定时间,这个时间是应用提供的“超时时间”
④进程被唤醒的条件有2:一是上面说的“一定时间”到了,二是被驱动程序唤醒。驱动程序发现条件就绪时,就把“某个队列”上挂着的进程唤醒,这个队列,就是前面通过poll_wait把本进程挂过去的队列。
⑤如果驱动程序没有去唤醒进程,那么schedule_timeout(__timeout)超时后,会重复2、3动作1
次,然后返回。
2.写代码
①在测试程序中加入以下代码
while (1)
{
ret = poll(fds, 1, 5000);
if (ret == 0)
{
printf("time out
");
}
int main()
{
int ret;
struct pollfd fds[1]
}
②在file_operations中增加一行
.poll = forth_drv_poll
③写函数forth_drv_poll
forth_drv_poll
poll_wait
p->qproc(file,wait_address,p)//相当于调用了__pollwait(file,&button_waitq,p)
//把当前进程挂到button_waitq队列里去
④在增加如下代码
if (ev_press)
mask |= POLLIN | POLLRDNORM;
1 /*
2 *使用poll 机制实现按键驱动程序
3 */
4
5 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include <linux/fs.h>
8 #include <linux/init.h>
9 #include <linux/delay.h>
10 #include <linux/irq.h>
11 #include <asm/uaccess.h>
12 #include <asm/irq.h>
13 #include <asm/io.h>
14 #include <asm/arch/regs-gpio.h>
15 #include <asm/hardware.h>
16
17
18 static struct class *g_ptForthdrvCls;
19 static struct class_device *g_ptForthdrvClsDev;
20
21 volatile unsigned long *gpfcon;
22 volatile unsigned long *gpfdat;
23
24 volatile unsigned long *gpgcon;
25 volatile unsigned long *gpgdat;
26
27 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
28
29 /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
30 static volatile int ev_press = 0;
31
32 struct pin_desc{
33 unsigned int pin;
34 unsigned int key_val;
35 };
36
37 static unsigned char key_val;
38
39 struct pin_desc pins_desc[4] = {
40 {S3C2410_GPF0, 0x01},
41 {S3C2410_GPF2, 0x02},
42 {S3C2410_GPG3, 0x03},
43 {S3C2410_GPG11, 0x04},
44 };
45
46
47 /*
48 * 确定按键值
49 */
50 static irqreturn_t buttons_irq(int irq, void *dev_id)
51 {
52 struct pin_desc * pindesc = (struct pin_desc *)dev_id;
53 unsigned int pinval;
54
55 pinval = s3c2410_gpio_getpin(pindesc->pin);
56
57 if (pinval)
58 {
59 /* 松开 */
60 key_val = 0x80 | pindesc->key_val;
61 }
62 else
63 {
64 /* 按下 */
65 key_val = pindesc->key_val;
66 }
67
68 ev_press = 1; /* 表示中断发生了 */
69 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
70
71
72 return IRQ_RETVAL(IRQ_HANDLED);
73 }
74
75 static int forthdrv_open(struct inode *inode, struct file *file)
76 {
77 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
78 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
79 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
80 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
81
82 return 0;
83 }
84
85 static ssize_t forthdrv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
86 {
87
88 if (size != 1)
89 return -EINVAL;
90
91 /* 如果没有按键动作, 休眠 */
92 wait_event_interruptible(button_waitq, ev_press);
93
94 /* 如果有按键动作, 返回键值 */
95 copy_to_user(buf, &key_val, 1);
96 ev_press = 0;
97
98 return 1;
99 }
100
101 static int forthdrv_close (struct inode * inode, struct file * file)
102 {
103 free_irq(IRQ_EINT0,&pins_desc[0]);
104 free_irq(IRQ_EINT2,&pins_desc[1]);
105 free_irq(IRQ_EINT11,&pins_desc[2]);
106 free_irq(IRQ_EINT19,&pins_desc[3]);
107
108 return 0;
109 }
110
111 static unsigned int forthdrv_poll(struct file *file, struct poll_table_struct *wait)
112 {
113 unsigned int mask = 0;
114 poll_wait(file, &button_waitq, wait);
115
116 if (ev_press)
117 mask |= POLLIN | POLLRDNORM;
118
119 return mask;
120 }
121
122 static struct file_operations forthdrv_fops = {
123 .owner = THIS_MODULE,
124 .open = forthdrv_open,
125 .read = forthdrv_read,
126 .release = forthdrv_close,
127 .poll = forthdrv_poll,
128 };
129
130
131 int g_iMajor;
132 static int forthdrv_init(void)
133 {
134 g_iMajor = register_chrdev(0, "forth_drv", &forthdrv_fops);
135
136 g_ptForthdrvCls = class_create(THIS_MODULE, "forth_drv");
137
138 g_ptForthdrvClsDev = class_device_create(g_ptForthdrvCls, NULL, MKDEV(g_iMajor, 0), NULL, "buttons"); /* /dev/buttons */
139
140 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
141 gpfdat = gpfcon + 1;
142
143 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
144 gpgdat = gpgcon + 1;
145
146 return 0;
147 }
148
149 static int forthdrv_exit(void)
150 {
151 unregister_chrdev(g_iMajor, "forth_drv");
152 class_device_unregister(g_ptForthdrvClsDev);
153 class_destroy(g_ptForthdrvCls);
154 iounmap(gpfcon);
155 iounmap(gpgcon);
156 return 0;
157 }
158
159 module_init(forthdrv_init);
160
161 module_exit(forthdrv_exit);
162 MODULE_LICENSE("GPL");
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <poll.h>
6 /* forthdrvtest
7 */
8 int main(int argc, char **argv)
9 {
10 int fd;
11 unsigned char key_val;
12 int ret;
13
14 struct pollfd fds[1];
15
16 fd = open("/dev/buttons", O_RDWR);
17 if (fd < 0)
18 {
19 printf("can't open!
");
20 }
21
22 fds[0].fd = fd;
23 fds[0].events = POLLIN;
24 while (1)
25 {
26 ret = poll(fds, 1, 5000);
27 if (ret == 0)
28 {
29 printf("time out
");
30 }
31 else
32 {
33 read(fd, &key_val, 1);
34 printf("key_val = 0x%x
", key_val);
35 }
36 }
37 return 0;
38 }
四、异步通知机制
之前的的驱动都是应用程序主动去读取驱动程序传来的数据;有没有一直机制,就是一旦有数据,驱动程序主动通知应用程序,让应用程序来获取数据。这种机制就是异步通知。
1.异步通知的简介
异步通知是file_operations中的一个设备方法,定义为
int (*fsync) (struct file *, struct dentry *, int datasync)
1.1 四个要点
要点:
注册信号处理函数:由应用程序注册信号处理函数
谁发信号?——驱动
发给谁?——应用程序的进程ID(如何获取应用程序的进程ID?——fasync_helper函数)
怎么发?——kill_fasync函数(在中断服务程序实现)
1.2 总结
为了使设备支持异步通知机制,驱动程序中涉及以下3项工作:
①支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。(不过此项工作已由内核完成,设备驱动无须处理)
②支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
驱动中应该实现fasync()函数。
③在设备资源可获得时,调用kill_fasync()函数激发相应的信号
应用程序:
fcntl(fd, F_SETOWN, getpid()); // 告诉内核,发给谁
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC); // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct
1 /*
2 *异步通知机制实现按键驱动程序
3 */
4
5 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include <linux/fs.h>
8 #include <linux/init.h>
9 #include <linux/delay.h>
10 #include <linux/irq.h>
11 #include <asm/uaccess.h>
12 #include <asm/irq.h>
13 #include <asm/io.h>
14 #include <asm/arch/regs-gpio.h>
15 #include <asm/hardware.h>
16 #include <linux/poll.h>
17
18
19 static struct class *g_ptFifthdrvCls;
20 static struct class_device *g_ptFifthdrvClsDev;
21
22 volatile unsigned long *gpfcon;
23 volatile unsigned long *gpfdat;
24
25 volatile unsigned long *gpgcon;
26 volatile unsigned long *gpgdat;
27
28 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
29
30 /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
31 static volatile int ev_press = 0;
32 static struct fasync_struct *fifthdrv_async;
33
34 struct pin_desc{
35 unsigned int pin;
36 unsigned int key_val;
37 };
38
39 static unsigned char key_val;
40
41 struct pin_desc pins_desc[4] = {
42 {S3C2410_GPF0, 0x01},
43 {S3C2410_GPF2, 0x02},
44 {S3C2410_GPG3, 0x03},
45 {S3C2410_GPG11, 0x04},
46 };
47
48
49 /*
50 * 确定按键值
51 */
52 static irqreturn_t buttons_irq(int irq, void *dev_id)
53 {
54 struct pin_desc * pindesc = (struct pin_desc *)dev_id;
55 unsigned int pinval;
56
57 pinval = s3c2410_gpio_getpin(pindesc->pin);
58
59 if (pinval)
60 {
61 /* 松开 */
62 key_val = 0x80 | pindesc->key_val;
63 }
64 else
65 {
66 /* 按下 */
67 key_val = pindesc->key_val;
68 }
69
70 ev_press = 1; /* 表示中断发生了 */
71 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
72
73 kill_fasync(&fifthdrv_async,SIGIO, POLL_IN);
74 return IRQ_RETVAL(IRQ_HANDLED);
75 }
76
77 static int fifthdrv_open(struct inode *inode, struct file *file)
78 {
79 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
80 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
81 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
82 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
83
84 return 0;
85 }
86
87 static ssize_t fifthdrv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
88 {
89
90 if (size != 1)
91 return -EINVAL;
92
93 /* 如果没有按键动作, 休眠 */
94 wait_event_interruptible(button_waitq, ev_press);
95
96 /* 如果有按键动作, 返回键值 */
97 copy_to_user(buf, &key_val, 1);
98 ev_press = 0;
99
100 return 1;
101 }
102
103 static int fifthdrv_close (struct inode * inode, struct file * file)
104 {
105 free_irq(IRQ_EINT0,&pins_desc[0]);
106 free_irq(IRQ_EINT2,&pins_desc[1]);
107 free_irq(IRQ_EINT11,&pins_desc[2]);
108 free_irq(IRQ_EINT19,&pins_desc[3]);
109
110 return 0;
111 }
112
113 static unsigned int fifthdrv_poll(struct file *file, struct poll_table_struct *wait)
114 {
115 unsigned int mask = 0;
116 poll_wait(file, &button_waitq, wait);
117
118 if (ev_press)
119 mask |= POLLIN | POLLRDNORM;
120
121 return mask;
122 }
123
124 static int fifthdrv_fasync(int fd, struct file *file, int on)
125 {
126 printk("driver: fifthdrv_fasync
");
127 return fasync_helper(fd, file, on, &fifthdrv_async);
128 }
129
130
131 static struct file_operations fifthdrv_fops = {
132 .owner = THIS_MODULE,
133 .open = fifthdrv_open,
134 .read = fifthdrv_read,
135 .release = fifthdrv_close,
136 .poll = fifthdrv_poll,
137 .fasync = fifthdrv_fasync,
138 };
139
140
141 int g_iMajor;
142 static int fifthdrv_init(void)
143 {
144 g_iMajor = register_chrdev(0, "fifth_drv", &fifthdrv_fops);
145
146 g_ptFifthdrvCls = class_create(THIS_MODULE, "fifth_drv");
147
148 g_ptFifthdrvClsDev = class_device_create(g_ptFifthdrvCls, NULL, MKDEV(g_iMajor, 0), NULL, "buttons"); /* /dev/buttons */
149
150 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
151 gpfdat = gpfcon + 1;
152
153 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
154 gpgdat = gpgcon + 1;
155
156 return 0;
157 }
158
159 static int fifthdrv_exit(void)
160 {
161 unregister_chrdev(g_iMajor, "fifth_drv");
162 class_device_unregister(g_ptFifthdrvClsDev);
163 class_destroy(g_ptFifthdrvCls);
164 iounmap(gpfcon);
165 iounmap(gpgcon);
166 return 0;
167 }
168
169 module_init(fifthdrv_init);
170
171 module_exit(fifthdrv_exit);
172 MODULE_LICENSE("GPL");
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <poll.h>
6 #include <signal.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10
11
12 /* fifthdrvtest
13 */
14 int fd;
15
16 void my_signal_fun(int signum)
17 {
18 unsigned char key_val;
19 read(fd, &key_val, 1);
20 printf("key_val: 0x%x
", key_val);
21 }
22
23 int main(int argc, char **argv)
24 {
25 unsigned char key_val;
26 int ret;
27 int Oflags;
28
29 signal(SIGIO, my_signal_fun);
30
31 fd = open("/dev/buttons", O_RDWR);
32 if (fd < 0)
33 {
34 printf("can't open!
");
35 }
36
37 fcntl(fd, F_SETOWN, getpid());
38
39 Oflags = fcntl(fd, F_GETFL);
40
41 fcntl(fd, F_SETFL, Oflags | FASYNC);
42
43
44 while (1)
45 {
46 sleep(1000);
47 }
48
49 return 0;
50 }
五、同步,互斥,阻塞
linux内核是多线程的内核,当有多个应用程序来访问同一个驱动程序时必将出错,这是系统所不允许的。所以,同一时刻只能有一个应用程序打开驱动程序。
实现原理:
static int canopen = 1;
在open函数内添加如下代码
if(--canopen != 0){
canopen++;
return -EBUSY;
}
在close函数内添加如下代码
canopen++;
但是,linux是多任务系统,进行某一操作的同时有可能被切换,从而导致两进程都打开了设备。
解决方案:
1.把上述步骤设置为原子操作。
open函数:
static atomic_t canopen = ATOMIC_INIT(1);
if(!atomic_desc_and_test(&canopen)){
atomic_inc(canopen);
return -EBUSY;
}
close函数:
atomic_inc(canopen);
2.使用信号量实现。
定义信号量init_MUTEX(button_lock)
获得信号量down(&button_lock)
释放信号量up(&button_lock)
3.阻塞
open函数添加以下代码:
if (file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
/* 获取信号量 */
down(&button_lock);
}
在read函数添加以下代码:
if (file->f_flags & O_NONBLOCK)
{
if (!ev_press)
return -EAGAIN;
}
else
{
/* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press);
}
测试方法:
当多次执行./sixth_test &时,S即睡眠状态,D即僵死状态
1 /* 2 *阻塞方式实现按键驱动程序 3 */ 4 5 #include <linux/module.h> 6 #include <linux/kernel.h> 7 #include <linux/fs.h> 8 #include <linux/init.h> 9 #include <linux/delay.h> 10 #include <linux/irq.h> 11 #include <asm/uaccess.h> 12 #include <asm/irq.h> 13 #include <asm/io.h> 14 #include <asm/arch/regs-gpio.h> 15 #include <asm/hardware.h> 16 #include <linux/poll.h> 17 18 19 static struct class *g_ptFifthdrvCls; 20 static struct class_device *g_ptFifthdrvClsDev; 21 22 volatile unsigned long *gpfcon; 23 volatile unsigned long *gpfdat; 24 25 volatile unsigned long *gpgcon; 26 volatile unsigned long *gpgdat; 27 28 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 29 30 /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */ 31 static volatile int ev_press = 0; 32 static struct fasync_struct *fifthdrv_async; 33 34 struct pin_desc{ 35 unsigned int pin; 36 unsigned int key_val; 37 }; 38 39 static unsigned char key_val; 40 41 struct pin_desc pins_desc[4] = { 42 {S3C2410_GPF0, 0x01}, 43 {S3C2410_GPF2, 0x02}, 44 {S3C2410_GPG3, 0x03}, 45 {S3C2410_GPG11, 0x04}, 46 }; 47 48 static DECLARE_MUTEX(button_lock); 49 /* 50 * 确定按键值 51 */ 52 static irqreturn_t buttons_irq(int irq, void *dev_id) 53 { 54 struct pin_desc * pindesc = (struct pin_desc *)dev_id; 55 unsigned int pinval; 56 57 pinval = s3c2410_gpio_getpin(pindesc->pin); 58 59 if (pinval) 60 { 61 /* 松开 */ 62 key_val = 0x80 | pindesc->key_val; 63 } 64 else 65 { 66 /* 按下 */ 67 key_val = pindesc->key_val; 68 } 69 70 ev_press = 1; /* 表示中断发生了 */ 71 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ 72 73 kill_fasync(&fifthdrv_async,SIGIO, POLL_IN); 74 return IRQ_RETVAL(IRQ_HANDLED); 75 } 76 77 static int fifthdrv_open(struct inode *inode, struct file *file) 78 { 79 80 #if 0 81 if (!atomic_dec_and_test(&canopen)) 82 { 83 atomic_inc(&canopen); 84 return -EBUSY; 85 } 86 #endif 87 88 if (file->f_flags & O_NONBLOCK) 89 { 90 if (down_trylock(&button_lock)) 91 return -EBUSY; 92 } 93 else 94 { 95 /* 获取信号量 */ 96 down(&button_lock); 97 } 98 99 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 100 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); 101 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); 102 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); 103 104 return 0; 105 } 106 107 static ssize_t fifthdrv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 108 { 109 110 if (size != 1) 111 return -EINVAL; 112 113 if (file->f_flags & O_NONBLOCK) 114 { 115 if (!ev_press) 116 return -EAGAIN; 117 } 118 else 119 { 120 /* 如果没有按键动作, 休眠 */ 121 wait_event_interruptible(button_waitq, ev_press); 122 } 123 124 /* 如果有按键动作, 返回键值 */ 125 copy_to_user(buf, &key_val, 1); 126 ev_press = 0; 127 128 return 1; 129 } 130 131 static int fifthdrv_close (struct inode * inode, struct file * file) 132 { 133 free_irq(IRQ_EINT0,&pins_desc[0]); 134 free_irq(IRQ_EINT2,&pins_desc[1]); 135 free_irq(IRQ_EINT11,&pins_desc[2]); 136 free_irq(IRQ_EINT19,&pins_desc[3]); 137 up(&button_lock); 138 return 0; 139 } 140 141 static unsigned int fifthdrv_poll(struct file *file, struct poll_table_struct *wait) 142 { 143 unsigned int mask = 0; 144 poll_wait(file, &button_waitq, wait); 145 146 if (ev_press) 147 mask |= POLLIN | POLLRDNORM; 148 149 return mask; 150 } 151 152 static int fifthdrv_fasync(int fd, struct file *file, int on) 153 { 154 printk("driver: fifthdrv_fasync "); 155 return fasync_helper(fd, file, on, &fifthdrv_async); 156 } 157 158 159 static struct file_operations fifthdrv_fops = { 160 .owner = THIS_MODULE, 161 .open = fifthdrv_open, 162 .read = fifthdrv_read, 163 .release = fifthdrv_close, 164 .poll = fifthdrv_poll, 165 .fasync = fifthdrv_fasync, 166 }; 167 168 169 int g_iMajor; 170 static int fifthdrv_init(void) 171 { 172 g_iMajor = register_chrdev(0, "fifth_drv", &fifthdrv_fops); 173 174 g_ptFifthdrvCls = class_create(THIS_MODULE, "fifth_drv"); 175 176 g_ptFifthdrvClsDev = class_device_create(g_ptFifthdrvCls, NULL, MKDEV(g_iMajor, 0), NULL, "buttons"); /* /dev/buttons */ 177 178 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); 179 gpfdat = gpfcon + 1; 180 181 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); 182 gpgdat = gpgcon + 1; 183 184 return 0; 185 } 186 187 static int fifthdrv_exit(void) 188 { 189 unregister_chrdev(g_iMajor, "fifth_drv"); 190 class_device_unregister(g_ptFifthdrvClsDev); 191 class_destroy(g_ptFifthdrvCls); 192 iounmap(gpfcon); 193 iounmap(gpgcon); 194 return 0; 195 } 196 197 module_init(fifthdrv_init); 198 199 module_exit(fifthdrv_exit); 200 MODULE_LICENSE("GPL");
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 #include <poll.h> 6 #include <signal.h> 7 #include <sys/types.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 11 12 /* sixthdrvtest 13 */ 14 int fd; 15 16 void my_signal_fun(int signum) 17 { 18 unsigned char key_val; 19 read(fd, &key_val, 1); 20 printf("key_val: 0x%x ", key_val); 21 } 22 23 int main(int argc, char **argv) 24 { 25 unsigned char key_val; 26 int ret; 27 int Oflags; 28 29 //signal(SIGIO, my_signal_fun); 30 31 fd = open("/dev/buttons", O_RDWR | O_NONBLOCK); 32 if (fd < 0) 33 { 34 printf("can't open! "); 35 return -1; 36 } 37 38 //fcntl(fd, F_SETOWN, getpid()); 39 40 //Oflags = fcntl(fd, F_GETFL); 41 42 //fcntl(fd, F_SETFL, Oflags | FASYNC); 43 44 45 while (1) 46 { 47 ret = read(fd, &key_val, 1); 48 printf("key_val: 0x%x, ret = %d ", key_val, ret); 49 sleep(5); 50 } 51 52 return 0; 53 }
最后科普一下,异步通知和阻塞的区别:
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起(休眠)。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
异步关注的是消息通信机制,当一个异步过程调用发出后,调用者不会立刻得到结果,
而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用(主动通知调用者)。
异步通知是相对与同步通知来说的。
这里阻塞与非阻塞与是否同步异步无关
比如:你问朋友问题,如果你是阻塞式调用,你会一直把自己“挂起”,直到得到问题有没有解决的结果,
如果是非阻塞式调用,你不管朋友有没有告诉你,你先去忙其他事,当然你也要偶尔看一下朋友有没有返回结果。
异步通知:朋友看到你问的问题,直接告诉你他想一下,(此时没有返回结果给你),当他想到了,会主动通知你
六、定时器防抖动
1.按键去抖动的方法
a.硬件电路去抖动
b.软件延时去抖动,有两种方式,其中一种是什么事都不做,死循环;另一种是利用定时器延时。
本程序采用的的定时器延时的方式。
2.定时器延时的两个要素:超时时间,处理函数。
3.定时器相关的处理函数
a.初始化定时器init_timer
b.处理函数timer_list.function
c.向内核注册一个定时器结构体add_timer
d.修改定时器时间并启动定时器mod_timer
4.mod_timer(&timer, jiffies+HZ/100)的定时时间为10ms,为什么?
HZ为100,所以HZ/100 = 1,变成了jiffies+1,而jiffies的单位为10ms,所以每次定时的时间自然就为10ms了。
1 /* 2 *定时器去抖动 3 */ 4 5 #include <linux/module.h> 6 #include <linux/kernel.h> 7 #include <linux/fs.h> 8 #include <linux/init.h> 9 #include <linux/delay.h> 10 #include <linux/irq.h> 11 #include <asm/uaccess.h> 12 #include <asm/irq.h> 13 #include <asm/io.h> 14 #include <asm/arch/regs-gpio.h> 15 #include <asm/hardware.h> 16 #include <linux/poll.h> 17 18 19 static struct timer_list timer; 20 21 static struct pin_desc *irq_pd; 22 23 static struct class *g_pbuttonsdrvCls; 24 static struct class_device *g_pbuttonsdrvClsDev; 25 26 volatile unsigned long *gpfcon; 27 volatile unsigned long *gpfdat; 28 29 volatile unsigned long *gpgcon; 30 volatile unsigned long *gpgdat; 31 32 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 33 34 /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */ 35 static volatile int ev_press = 0; 36 static struct fasync_struct *buttonsdrv_async; 37 38 struct pin_desc{ 39 unsigned int pin; 40 unsigned int key_val; 41 }; 42 43 static unsigned char key_val; 44 45 struct pin_desc pins_desc[4] = { 46 {S3C2410_GPF0, 0x01}, 47 {S3C2410_GPF2, 0x02}, 48 {S3C2410_GPG3, 0x03}, 49 {S3C2410_GPG11, 0x04}, 50 }; 51 52 static DECLARE_MUTEX(button_lock); 53 /* 54 * 确定按键值 55 */ 56 static irqreturn_t buttons_irq(int irq, void *dev_id) 57 { 58 /* 10ms后启动定时器 */ 59 irq_pd = (struct pin_desc *)dev_id; 60 mod_timer(&timer, jiffies+HZ/100); 61 return IRQ_RETVAL(IRQ_HANDLED); 62 } 63 64 static int buttonsdrv_open(struct inode *inode, struct file *file) 65 { 66 67 #if 0 68 if (!atomic_dec_and_test(&canopen)) 69 { 70 atomic_inc(&canopen); 71 return -EBUSY; 72 } 73 #endif 74 75 if (file->f_flags & O_NONBLOCK) 76 { 77 if (down_trylock(&button_lock)) 78 return -EBUSY; 79 } 80 else 81 { 82 /* 获取信号量 */ 83 down(&button_lock); 84 } 85 86 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 87 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); 88 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); 89 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); 90 91 return 0; 92 } 93 94 static ssize_t buttonsdrv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 95 { 96 97 if (size != 1) 98 return -EINVAL; 99 100 if (file->f_flags & O_NONBLOCK) 101 { 102 if (!ev_press) 103 return -EAGAIN; 104 } 105 else 106 { 107 /* 如果没有按键动作, 休眠 */ 108 wait_event_interruptible(button_waitq, ev_press); 109 } 110 111 /* 如果有按键动作, 返回键值 */ 112 copy_to_user(buf, &key_val, 1); 113 ev_press = 0; 114 115 return 1; 116 } 117 118 static int buttonsdrv_close (struct inode * inode, struct file * file) 119 { 120 free_irq(IRQ_EINT0,&pins_desc[0]); 121 free_irq(IRQ_EINT2,&pins_desc[1]); 122 free_irq(IRQ_EINT11,&pins_desc[2]); 123 free_irq(IRQ_EINT19,&pins_desc[3]); 124 up(&button_lock); 125 return 0; 126 } 127 128 static unsigned int buttonsdrv_poll(struct file *file, struct poll_table_struct *wait) 129 { 130 unsigned int mask = 0; 131 poll_wait(file, &button_waitq, wait); 132 133 if (ev_press) 134 mask |= POLLIN | POLLRDNORM; 135 136 return mask; 137 } 138 139 static int buttonsdrv_fasync(int fd, struct file *file, int on) 140 { 141 printk("driver: buttonsdrv_fasync "); 142 return fasync_helper(fd, file, on, &buttonsdrv_async); 143 } 144 145 146 static void buttons_timer_func(unsigned long data) 147 { 148 struct pin_desc * pindesc = irq_pd; 149 unsigned int pinval; 150 151 pinval = s3c2410_gpio_getpin(pindesc->pin); 152 153 if (pinval) 154 { 155 /* 松开 */ 156 key_val = 0x80 | pindesc->key_val; 157 } 158 else 159 { 160 /* 按下 */ 161 key_val = pindesc->key_val; 162 } 163 164 ev_press = 1; /* 表示中断发生了 */ 165 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ 166 167 kill_fasync(&buttonsdrv_async,SIGIO, POLL_IN); 168 // return IRQ_RETVAL(IRQ_HANDLED); 169 } 170 171 static struct file_operations buttonsdrv_fops = { 172 .owner = THIS_MODULE, 173 .open = buttonsdrv_open, 174 .read = buttonsdrv_read, 175 .release = buttonsdrv_close, 176 .poll = buttonsdrv_poll, 177 .fasync = buttonsdrv_fasync, 178 }; 179 180 181 int g_iMajor; 182 static int buttonsdrv_init(void) 183 { 184 185 init_timer(&timer); 186 // timer.expires = jiffies + media_tbl[dev->if_port].wait; 187 timer.function = &buttons_timer_func; /* timer handler */ 188 add_timer(&timer); 189 190 191 g_iMajor = register_chrdev(0, "buttons_drv", &buttonsdrv_fops); 192 193 g_pbuttonsdrvCls = class_create(THIS_MODULE, "buttons_drv"); 194 195 g_pbuttonsdrvClsDev = class_device_create(g_pbuttonsdrvCls, NULL, MKDEV(g_iMajor, 0), NULL, "buttons"); /* /dev/buttons */ 196 197 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); 198 gpfdat = gpfcon + 1; 199 200 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); 201 gpgdat = gpgcon + 1; 202 203 return 0; 204 } 205 206 static int buttonsdrv_exit(void) 207 { 208 unregister_chrdev(g_iMajor, "buttons_drv"); 209 class_device_unregister(g_pbuttonsdrvClsDev); 210 class_destroy(g_pbuttonsdrvCls); 211 iounmap(gpfcon); 212 iounmap(gpgcon); 213 return 0; 214 } 215 216 module_init(buttonsdrv_init); 217 218 module_exit(buttonsdrv_exit); 219 MODULE_LICENSE("GPL");
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 #include <poll.h> 6 #include <signal.h> 7 #include <sys/types.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 11 12 /* sixthdrvtest 13 */ 14 int fd; 15 16 void my_signal_fun(int signum) 17 { 18 unsigned char key_val; 19 read(fd, &key_val, 1); 20 printf("key_val: 0x%x ", key_val); 21 } 22 23 int main(int argc, char **argv) 24 { 25 unsigned char key_val; 26 int ret; 27 int Oflags; 28 29 //signal(SIGIO, my_signal_fun); 30 31 fd = open("/dev/input/event0", O_RDWR); 32 if (fd < 0) 33 { 34 printf("can't open! "); 35 return -1; 36 } 37 38 //fcntl(fd, F_SETOWN, getpid()); 39 40 //Oflags = fcntl(fd, F_GETFL); 41 42 //fcntl(fd, F_SETFL, Oflags | FASYNC); 43 44 45 while (1) 46 { 47 ret = read(fd, &key_val, 1); 48 printf("key_val: 0x%x, ret = %d ", key_val, ret); 49 //sleep(5); 50 } 51 52 return 0; 53 }
修改于2017-01-08 21:05:56