以按键检测驱动程序为例
1.查询方式 缺点是需要消耗较多CPU资源
2.中断方式 应用的read函数调用驱动程序的的read函数,驱动程序的read函数会调用wait_event_interruptible()等待按键中断的发生.
中断发生之前会处于睡眠状态,中断发生后会唤醒该read函数,将按键值传回应用层.睡眠状态不会消耗cpu资源
3.poll方式
在方式2 的基础上加上poll函数
while (1)
{
//read(fd, &keyvalue, 1);
ret = poll(fds, 1, 5000);
if (ret == 0)
{
printf("time out\n");
}
else
{
read(fd, &keyvalue, 1);
printf("keyvalue = 0x%x\n", keyvalue);
}
//sleep(5);
}
应用层的poll会调用驱动程序中的poll函数,
static unsigned s3c24xx_key_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 不会立即休眠
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
驱动中的poll函数被中断程序唤醒,从而应用程序能够调用read函数读取按键键值.
与方式2不同的是poll可以设置超时时间,超时时间到了就会退出,不会像方式2那样永远等待下去
4.异步通知的方式
应用层代码如下
void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}
int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags;
signal(SIGIO, my_signal_fun);//
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
fcntl(fd, F_SETOWN, getpid());//把自己的进程ID 告诉驱动程序
Oflags = fcntl(fd, F_GETFL); //get flag
fcntl(fd, F_SETFL, Oflags | FASYNC);// 应用调用这个函数改变Oflags时,驱动程序中的.fasync= fifth_drv_fasync就会被调用
while (1)
{
sleep(1000);
}
return 0;
}
驱动代码如下
fasync_helper作用就是初始化fasync这个东西,包括分配内存和设置属性,最后在驱动的release里把fasync_helper初始化的东西free掉。
static int chardev_fasync (int fd, struct file *filp, int on)
{
printk("driver: fifth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);//初始化 button_async结构体
}
static int chardev_release(struct inode *inodp, struct file *filp)
{
return chardev_fasync(-1, filp, 0);
}
fasync是为了使驱动的读写和应用程序的读写分开,使得应用程序可以在驱动读写的时候去做别的事。
应用程序通过fcntl给自己的SIGIO信号安装自己的响应函数,
按键中断通过kill_fasync(&async, SIGIO, POLL_IN); 发SIGIO信号给应用程序,应用程序中的my_signal_fun()函数就会被调用。
fasync_helper作用就是初始化fasync这个东西,包括分配内存和设置属性,最后在驱动的release里把fasync_helper初始化的东西free掉。
这个异步处理方式的特点是应用程序不需要等待和睡眠,可以去处理其他事情,按键来了会自动响应.
5.设备资源是有限的,一个app程序打开一个设备后没有release前,应该阻止第二个app打开该设备
可以在驱动程序的open函数中用原子操作变量的方式进行保护,如atomic_inc_and_test()
也可以通过信号量的方式
static int char_drv_open(struct inode *inode, struct file *file)
{
#if 0
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
#endif
if (file->f_flags & O_NONBLOCK) //非阻塞方式会立即返回
{
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
/* 获取信号量 */
down(&button_lock);
}
}
6,此外还需要利用定时器防止按键抖动造成的误操作.中断发生时重置定时器
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms后启动定时器 */
//系统10ms产生一次中断,jiffies的值10ms增加一次,一秒钟增加100次
//jiffies+1就是设置10ms后时钟函数会响应
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+1);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int sixth_drv_init(void)
{
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
//buttons_timer.expires = 0;
add_timer(&buttons_timer);
return 0;
}
static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;
if (!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin);
if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
}
ev_press = 1; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
kill_fasync (&button_async, SIGIO, POLL_IN);
}
原来的中断函数要做的事情放到了buttons_timer_function()函数中.中断函数现在只用来设置timer