初入android驅動開發之字符設備(四-中斷)
上一篇講到android驅動開發中,應用是怎樣去操作底層硬件的整個流程,實現了按鍵控制led的亮滅。當然,這是一個非常easy的實例,只是略微演變一下,就能夠得到廣泛的應用。
如開發掃描頭,應用透過監聽上報的按鍵的鍵值,監聽到,則調用掃描頭的模塊。成功,則點亮LED燈,並把掃描頭解碼後的條碼信息。通過廣播的形式發出。又扯到其他地方,這裏主要說說中斷。
1. 中斷的一些概念
中斷,是什麽?
中斷。能夠看成是cpu對特殊事件的一種處理的機制,這類特殊事件一般指緊急事件或者說異常事件。非常easy的一個樣例,你拿你手機正在看視頻,來了一個電話。你接完電話,還是停在視頻。本來你的cpu正在運行看視頻這一系列的指令處理。但當接收到電話,會產生一個中斷,cpu依據優先級推斷。優先級高於當前則停止當前工作。並保存,然後運行中斷的處理函數,其中斷這一系列的事件處理完成以後。再運行保存在暫停隊列中的工作。這是一個外部中斷的樣例。
那麽中斷,是指 CPU 在運行程序的過程中,出現了某些突發事件時 CPU 必須暫停運行當前的程序,轉去處理突發事件,處理完成後 CPU 又返回原程序被中斷的位置並繼續運行。依據中斷的來源,中斷可分為內部中斷和外部中斷,內部中斷的中斷源來自 CPU內部(軟件中斷指令、溢出、除法錯誤等,比如,操作系統從用戶態切換到內核態需借助 CPU 內部的軟件中斷),外部中斷的中斷源來自 CPU 外部,由外設提出請求。
中斷,實現它的機制?
中斷。當外設發出一個中斷信號,cpu則依據中斷信號,來進行分析處理,依據中斷信號所對於的地址。去調用中斷處理函數。所以。中斷處理函數。是值該中斷產生後,cpu應該去緊急運行的事件。
那麽,這裏主要解說一下中斷處理函數的機制。
s5pv210是arm架構的芯片,當中斷的資源很的豐富,這裏有32個外部中斷和其余的gpio中斷。一般。實際開發中,中斷主要由外設發出。所以,這裏我們基本都是用的外部中斷。採用外部中斷的 CPU 通常為不同的中斷分配不同的中斷號,當檢測到某中斷號的中斷到來後,就自己主動跳轉到與該中斷號相應的地址運行。
不同中斷號的中斷有不同的入口地址。
中斷處理機制,,Linux 將中斷處理程序分解為兩個半部:頂半部(top half)和底半部(bottom half)。
在這兩者重要的差別,頂半部,不可被中斷,而底半部,能夠被新的中斷打開。那麽,這兩者之前的差別,就認為了它們各自獨特的特性。頂半部,不可被打斷,所以註定它的運行時間要很很的高速,所以一般它僅僅是簡單的讀取寄存器的中斷狀態並清楚中斷標誌,然後就把底半部處理程序掛究竟半部運行隊列中。而這樣,中斷處理的大部分工作就落究竟半部了。由於可被打斷,相對來說,時間就比較充足。運行一些耗時的任務。
底半部的三種方式:軟中斷、tasklet、工作隊列。
這裏有個博文鏈接。主要將三種機制以及之間的差異。http://blog.chinaunix.net/uid-20768928-id-5077401.html
中斷,當中關鍵的一些函數?
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
handler:發生中斷時首先要運行的硬中斷處理函數,這個函數能夠通過返回 IRQ_WAKE_THREADED喚醒中斷線程,也可返回IRQ_HANDLE不運行中斷線程
thread_fn : 中斷線程,類似於中斷下半部,若傳參為null,則和request_irq()一樣
qflags:中斷標誌。備註:IRQF_SHARED 共享中斷時,dev_id不能為空。由於釋放irq時要區分哪個共享中斷。
devname:中斷名
dev_id: 傳給中斷處理函數的參數。
2.簡單的實例:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/irq.h> #include <linux/input.h> #include <linux/platform_device.h> #include <linux/miscdevice.h> #include <mach/gpio.h> #include <linux/io.h> #include <mach/hardware.h> #include <linux/delay.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <linux/interrupt.h> #include <linux/gpio.h> #include <linux/wait.h> #include <linux/sched.h> #include <plat/gpio-core.h> #include <plat/gpio-cfg.h> #include <plat/gpio-cfg-helpers.h> static struct class *buttondrv_class; static struct device *buttondrv_class_dev; int major; volatile unsigned long *GPCCON; volatile unsigned long *GPCDAT; //static DECLARE_WAIT_QUEUE_HEAD(button_waitq); static unsigned char key_val; static volatile int ev_press = 0; struct pin_desc{ unsigned int pin; unsigned int key_val; }; struct pin_desc pins_desc[2] = { {S5PV210_GPH3(7), 0x01}, }; static irqreturn_t buttons_irq(int irq, void *dev_id) { printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>buttons_irq\n"); struct pin_desc *pindesc = (struct pin_desc *)dev_id; unsigned int pinval; pinval = gpio_get_value(pindesc->pin); printk("irq >>>>>>>>>>>>>>>>>>>>>>>>>>>>pinval =%d \n",pinval); if (pinval) { key_val = 0x80 | pindesc->key_val; printk("1111 >>>>>>>>>>>>>>>>>>>>>>>>>>>key_val =%d \n",key_val); } else { key_val = pindesc->key_val; printk("0000 >>>>>>>>>>>>>>>>>>>>>>>>>>>key_val =%d \n",key_val); } ev_press = 1; // wake_up_interruptible(&button_waitq); return IRQ_RETVAL(IRQ_HANDLED); } static int button_drv_open(struct inode *inode, struct file *file) { printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_open\n"); int ret=-1; s3c_gpio_setpull(S5PV210_GPH3(7), S3C_GPIO_PULL_NONE); ret = request_threaded_irq(gpio_to_irq(S5PV210_GPH3(7)), NULL, buttons_irq, IRQF_TRIGGER_RISING, "s2", &pins_desc[0]); printk("ret=%d irq=%d >>>>>>>>>>>>>>>>>>>>>>>>>\n ",ret,gpio_to_irq(S5PV210_GPH3(7))); return 0; } int button_drv_close(struct inode *inode, struct file *file) { free_irq(gpio_to_irq(S5PV210_GPH3(7)), &pins_desc[0]); return 0; } static int button_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_read\n"); if (count != 1) return -EINVAL; printk("read >>>>>>>>>>>>>>>>>>>>>>>>>>>key_val =%d \n",key_val); // wait_event_interruptible(button_waitq, ev_press); copy_to_user(buf, &key_val, 1); key_val=0; ev_press = 0; return 1; } static struct file_operations button_drv_fops = { .owner = THIS_MODULE, .open = button_drv_open, .read = button_drv_read, .release = button_drv_close, }; static int button_drv_init(void){ printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_init\n"); GPCCON = (volatile unsigned long *)ioremap(0xE0200C60, 8); GPCDAT= GPCCON + 1; if (!GPCCON) { return -EIO; } major = register_chrdev(0, "button_drv", &button_drv_fops); buttondrv_class = class_create(THIS_MODULE, "buttondrv"); buttondrv_class_dev = device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); return 0; } static void button_drv_exit(void){ printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_exit\n"); unregister_chrdev(major, "button_drv"); device_unregister(buttondrv_class_dev); class_destroy(buttondrv_class); iounmap(GPCCON); } module_init(button_drv_init); module_exit(button_drv_exit); MODULE_LICENSE("GPL");關於代碼一些簡單的說明:
static DECLARE_WAIT_QUEUE_HEAD(button_waitq)
wake_up_interruptible(&button_waitq)
wait_event_interruptible(button_waitq, ev_press)
這個是等待隊列的機制,當有中斷的時候。喚醒。把事件增加工作隊列中,處理完事件後。繼續休眠,直到下次中斷。
3.關於一些調試方法:
一般在編寫中斷的程序,最基本的是要看,gpio口的中斷號是否申請成功。這裏主要依據打印語句進行調試了。
若驅動程序不報錯誤了,則可進入android系統下,cat proc/interrupts ,可查看到你申請成功的中斷。
初入android驅動開發之字符設備(四-中斷)