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

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章