linux驱动编写之poll机制
1. poll情景描述:
以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,
应用程序使用read()函数来读取按键的键值。这样做的效果是:如果有按键按下
了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行;倘
若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生。
这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们
需要一个时间节点。倘若没有按键按下,那么超过多少时间之后,也要返回超时
错误信息,进程能够继续得到执行,而不是没有按键按下,就永远休眠。这种例
子其实还有很多,比方说两人相亲,男方等待女方给个确定相处的信,男方不可
能因为女方不给信,就永远等待下去,双方需要一个时间节点。这个时间节点,
就是说超过这个时间之后,不能再等了,程序还要继续运行,需要采取其他的行
动来解决问题。
2. 对于类似的场景,linux系统使用poll功能来解决这样的问题:
linux系统调用poll()函数时候,如果没有发生需要的事件,那么进程进入休
眠。如果在限定的时间内得到需要的事件,那么成功返回,如果没有则返回超时
错误信息。可见,等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高
CPU的效率。
3. poll()函数:
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
输入参数
fds 可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 请求的事件类型,监视驱动文件的事件掩码 */
short revents; /* 驱动文件实际返回的事件 */
} ;nfds 监测驱动文件的个数
timeout 超时时间,单位为ms
事件类型events 可以为下列值:
POLLIN 有数据可读
POLLRDNORM 有普通数据可读,等效与POLLIN
POLLPRI 有紧迫数据可读
POLLOUT 写数据不会导致阻塞
POLLER 指定的文件描述符发生错误
POLLHUP 指定的文件描述符挂起事件
POLLNVAL 无效的请求,打不开指定的文件描述符
返回值
有事件发生 返回revents域不为0的文件描述符个数(也就是说事件发生,或者错误报告)
超时 返回0;
失败 返回-1,并设置errno为错误类型
操作步骤
驱动:
1.定义两个变量
/* 定义一个等待队列,这个等待队列实际上是由中断驱动的,当中断发生时,会令挂接到这个等待队列的休眠进程唤醒 */static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
static volatile int ev_press = 0;
2. - 在file_operations多定义一个成员函数 例:.poll = forth_drv_poll,
- 定义这个函数
static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); /* 将进程挂接到button_waitq等待队列下 *//* 根据实际情况,标记事件类型 */
if (ev_press)
mask |= POLLIN | POLLRDNORM;/* 如果mask为0,那么证明没有请求事件发生;如果非零说明有时间发生 */
return mask;
}
3.在forth_drv_read判断是否休眠状态
ssize_t forth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != 1)
return -EINVAL;/* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press);/* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, 1);
ev_press = 0;
return 1;
}
4. 在中断函数里面唤醒进程
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval;
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); /* 唤醒休眠的进程 */
return IRQ_RETVAL(IRQ_HANDLED);
}
应用程序:
1.调用int poll(struct pollfd *fds, nfds_t nfds, int timeout)
2. 定义并初始化参数1 struct pollfd *fds
int main(int argc, char **argv)
{
int fd;
unsigned char key_val;
int ret;struct pollfd fds[1];
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}fds[0].fd = fd;
fds[0].events = POLLIN;
while (1)
{
ret = poll(fds, 1, 5000);
if (ret == 0)
{
printf("time out\n");
}
else
{
read(fd, &key_val, 1);
printf("key_val = 0x%x\n", key_val);
}
}
return 0;
}