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