嵌入式核心及驅動開發之學習筆記(八) 阻塞模式+中斷實現讀取資料
阿新 • • 發佈:2018-11-27
目前已經學習兩種應用層IO模型的使用
非阻塞:立即返回結果,如果想得到期望的結果,要不停的呼叫這個方法(輪詢),非常耗費資源
阻塞:沒有得到真正的資料前,不返回結果。此時,程序進入阻塞(休眠)態,直到有資料喚醒程序,這個過程不耗資源。
PS:linux應用中,大部分的函式介面都是阻塞
驅動程式將程序進入休眠狀態的過程
1將當前程序加入到等待佇列頭中
2將當前程序狀態設定成TASK_INTERRUPTIBLE
3讓出排程--休眠
就是說,將程序加入一個佇列中。如果改變它的狀態成TASK_INTERRUPTIBLE,並讓CPU不再排程它,那這就是進入阻塞(休眠)態了。在linux驅動開發中,通過一個介面完成上述過程
wait_event_interruptible(wq, condition)
寫驅動程式的的步驟
1等待佇列頭
init_waitqueue_head(wait_queue_head_t *q);
2在需要等待(沒有資料)的時候,進行休眠
wait_event_interruptible(wait_queue_head_t wq, condition) // 內部會構建一個等待佇列項/節點wait_queue_t
3在一個合適的時候(有資料),會將程序喚醒
wake_up_interruptible(wait_queue_head_t *q)
驅動程式
在驅動模組載入時,也就是初始化函式key_drv_init中,加入 等待列隊頭。當應用程式呼叫read介面函式時,使用wait_event_interruptible函式,condition引數為1說明有資料就繼續執行,沒有則阻塞等待。wake_up_interruptible用來喚醒休眠中的程序。
//key_drv.c #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/sched.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/fcntl.h> irqreturn_t key_irq_handler(int irqno, void *devid); ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *); ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *); int key_drv_open (struct inode *, struct file *); int key_drv_close (struct inode *, struct file *); #define GPXCON_REG 0x11000C20 #define KEY_ENTER 28 const struct file_operations key_fops = { .open = key_drv_open, .read = key_drv_read, .write = key_drv_write, .release = key_drv_close, }; struct key_event{ int code; // 按鍵的型別 int value; // 狀態 }; struct key_desc{ unsigned int dev_major; struct class *cls; struct device *dev; int irqno; void *reg_base; int key_state; //表示是否有資料 struct key_event event; wait_queue_head_t wq_head; }; struct key_desc *key_dev; //static int irqno; int get_irqno_from_node(void) { //從裝置樹路徑,查詢節點 struct device_node *np = of_find_node_by_path("/key_int_node"); if(np){ printk("find node ok\n"); }else{ printk("find node failed\n"); } int irqno = irq_of_parse_and_map(np, 0); printk("irqno = %d\n", irqno); return irqno; } static int __init key_drv_init(void) { int ret; //物件例項化 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //申請主裝置號 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); //建立裝置結點 key_dev->cls = class_create(THIS_MODULE, "key_cls"); key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key0"); //硬體初始化 key_dev->irqno = get_irqno_from_node(); ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key3_eint10", NULL); if(ret != 0) { printk("request_irq error\n"); return ret; } key_dev->reg_base = ioremap(GPXCON_REG, 8); // 初始化等待佇列頭 init_waitqueue_head(&key_dev->wq_head); return 0; } static void __exit key_drv_exit(void) { iounmap(key_dev->reg_base); //去對映 free_irq(key_dev->irqno, NULL); //釋放中斷資源 device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); // class_destroy(key_dev->cls); // unregister_chrdev(key_dev->dev_major, "key_drv"); //登出主裝置號 kfree(key_dev); //釋放結構體記憶體 } irqreturn_t key_irq_handler(int irqno, void *devid) { printk("-------%s-------------\n", __FUNCTION__); int value = readl(key_dev->reg_base + 4) & (1<<2); if(value){// 1 printk("key3 up\n"); key_dev->event.code = KEY_ENTER; key_dev->event.value = 0; }else{// 0 printk("key3 pressed\n"); key_dev->event.code = KEY_ENTER; key_dev->event.value = 1; } // 表示有資料,需要去喚醒整個程序/等待佇列 wake_up_interruptible(&key_dev->wq_head); //同時設定標誌位 key_dev->key_state = 1; return IRQ_HANDLED; } ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos) { int ret; //2,在需要等待(沒有資料)的時候,進行休眠 wait_event_interruptible(key_dev->wq_head, key_dev->key_state); // 表示有資料 ret = copy_to_user(buf, &key_dev->event, count); if(ret > 0) { printk("copy_to_user error\n"); return -EFAULT; } // 清除key_dev->event的資料記錄 memset(&key_dev->event, 0, sizeof(key_dev->event)); key_dev->key_state = 0; return count; } ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos) { printk("-------%s-------------\n", __FUNCTION__); return 0; } int key_drv_open(struct inode *inode, struct file *filp) { printk("-------%s-------------\n", __FUNCTION__); return 0; } int key_drv_close (struct inode *inode, struct file *filp) { printk("-------%s-------------\n", __FUNCTION__); return 0; } module_init(key_drv_init); module_exit(key_drv_exit); MODULE_LICENSE("GPL");