linux驱动编写之poll机制

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;
}

猜你喜欢

转载自blog.csdn.net/qq_39541098/article/details/84502512