LINUX系統中斷處理結構及中斷函式的實現
阿新 • • 發佈:2019-02-04
中斷系統流程解析:
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
handle_IRQ(irq, regs);
generic_handle_irq(irq);/*Garmen:進行一般的中斷處理*/
struct irq_desc *desc = irq_to_desc(irq); /*#define irq_to_desc(irq) (&irq_desc[irq]) Garmen:他是以irq為下標的一個全域性陣列項*/
generic_handle_irq_desc(irq, desc);
desc->handle_irq(irq, desc); /*Garmen : 那麼究竟是誰呼叫handle_irq ??? 下面進行分析*/
---> 問:所以是誰呼叫handle_irq ?
答:進行搜尋handle_irq然後進入kernel\irq\Chip.c
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);/*Garmen : 以irq為索引,找到一項*/
desc->handle_irq = handle; /*Garmen : 這裡進行設定,把上面以irq為索引找到的那項的handle_irq指定為傳進來的引數handle*/
然後我們再搜尋__irq_set_handler被誰呼叫:
irq_set_handler(unsigned int irq, irq_flow_handler_t handle)
__irq_set_handler(irq, handle, 0, NULL);
而且我們在linux-3.4.2\arch\arm\plat-s3c24xx\Irq.c 發現:
void __init s3c24xx_init_irq(void)
/*Garmen: 其實下面這些就相當於初始化函式*/
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++)
irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,handle_edge_irq); /*Garmen : 再跟進去*/
/*Garmen:irqno對應的就是上面irq_desc的irq索引
*然後你會驚人的發現這個handle_edge_irq 就是handle就是上面desc->handle_irq = handle 中斷處理函式
*/
irq_set_chip_and_handler_name(irq, chip,handle, NULL);
irq_set_chip(irq, chip);
desc->irq_data.chip = chip; /*Garmen:chip函式也是一樣,有跟進來的irq索引和chip*/
__irq_set_handler(irq, handle, 0, name); /*Garmen : 於是我們驚人的發現這個函式被呼叫了*/
再研究一下handle_edge_irq
handle_edge_irq:
/* Start handling the irq */
desc->irq_data.chip->irq_ack(&desc->irq_data);
handle_irq_event(desc);
struct irqaction *action = desc->action; /*Garmen : 取出desc中action成員*/
ret = handle_irq_event_percpu(desc, action); /*Garemn : 執行action相關函式*/
小結:問:按下按鍵之後怎麼處理
答:1、進入異常模式:
b vector_irq + 偏移值
2、呼叫列表,比如呼叫到irq_usr,保持現場什麼的工作
3、呼叫asm_do_irq
4、呼叫irq_desc[irq]->handle_irq /*Garmen: 以中斷號為下標,取出一項處理函式*/
5、上面的handle_irq 就是一箇中斷處理函式 , 好比如handle_edge_irq
裡面做了什麼事情呢?參考上面handle_edge_irq的研究
irq_desc結構體定義及其說明:他是一個全域性陣列
①:chip是一些晶片底層硬體相關的操作(比如響應中斷、清中斷等等),在linux3.4.2核心。他在irq_data這個數組裡面,這裡沒有列出來
②:handle_irq是中斷處理函式,將aciton連結串列中的成員一一取出來,然後執行action->handler
/**
* struct irq_desc - interrupt descriptor
* @irq_data: per irq and chip data passed down to chip functions
* @timer_rand_state: pointer to timer rand state struct
* @kstat_irqs: irq stats per cpu
* @handle_irq: highlevel irq-events handler
* @preflow_handler: handler called before the flow handler (currently used by sparc)
* @action: the irq action chain
* @status: status information
* @core_internal_state__do_not_mess_with_it: core internal status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity_hint: hint to user space for preferred irq affinity
* @affinity_notify: context for notification of affinity changes
* @pending_mask: pending rebalanced interrupts
* @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
struct irq_data irq_data;
struct timer_rand_state *timer_rand_state;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
struct irqaction
{
irq_handler_t handler;
unsigned long flags;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
分析request_irq函式 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) return request_threaded_irq(irq, handler, NULL, flags, name, dev); 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) struct irqaction *action; struct irq_desc *desc; action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); /*Garmen : 分配設定了action結構*/ if (!action) return -ENOMEM; action->handler = handler; action->thread_fn = thread_fn; action->flags = irqflags; action->name = devname; action->dev_id = dev_id; desc = irq_to_desc(irq); /*Garmen : 以中斷號為下標找到了這個desc全域性陣列項,將這個desc傳遞給下面的setup_irq */ retval = __setup_irq(irq, desc, action); /*Garmen : 然後呼叫__setup_irq 設定中斷*/ struct irqaction *old, **old_ptr; /*定義old指標做判斷*/ old_ptr = &desc->action; old = *old_ptr /*Garmen : 你知道什麼是共享中斷嗎?*/ ... /*Garmen : 對action做一系列判斷,比如是否是共享中斷啊等等,假如不是,就將我們傳進來的action加入aciton連結串列(具體看上面的圖) *然後desc->chip->settype 將引腳配置中斷引腳,還有那些flag什麼進行定義 *然後desc->->startup/enable 使能引腳 */ ... 插一小部分相關操作 開啟裝置: exec 5</dev/buttons /*Garmen : 開啟這個裝置,把它定位到5去*/ 檢視裝置: ps知道當前裝置 -sh是771 然後 ls -l /proc/771/fd 關閉裝置: exec 5<&- 刪除程序:kill -0 PID號 退出程序:kill -9 PID號 1、中斷申請函式: request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 函式原型: int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) 第一個引數:unsigned int irq 可例 : IRQ_EINT0 在Irq.h進行巨集定義(include\asm-arm) 在Irq.c 的s3c24xx_init_irq函式中有進行初始化; 中斷號是去Irq.h 將該巨集展開得到的一個數字 第二個引數:irq_handler_t handler 可例 : buttons_irq 是一個處理函式 第三個引數:unsigned long irqflags 可例:IRQT_BOTHEDGE 雙邊緣觸發 在Irq.h進行巨集定義 在Irq.c 的s3c_irqext_type中進行初始化 第四個引數:隨意 第五個引數:void *dev_id 它是void型的指標。所以不必顧慮太多; 它只不過是用來在free_irq解除安裝時候,與fre_irq的引數結合在一起,確定解除安裝哪一個irq_action 結構 小結:當發生IRQ_EINT0這個中斷的時候,就去呼叫這個buttons_irq中斷處理函式, 這個函式static irqreturn_t buttons_irq(int irq, void *dev_id),有兩個引數 第一個引數是中斷號等於IRQ_EINT0 ,第二個引數是dev_id 等於pins_desc[0] 敲黑板: 後面引入這樣的一個方法:將irq、*name、pin、key_val寫成一個結構體dev_id 例如 定義一個pin_desc結構體原型: struct pin_desc{ int irq; char *name; unsigned int pin; unsigned int key_val; }; 編寫我們自己想要的結構體: struct pin_desc pins_desc[4] = { {IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L}, {IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S}, {IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER}, {IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT}, }; 我們的註冊函式就可以這麼寫了: for (i = 0; i < 4; i++) { request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]); } 裡面所做的事情: ①:分配一個irqaction ②:把這個結構體放入irq_des[irq] <==action連結串列 ③:設定引腳 ④:中斷使能 2、中斷清除函式 free_irq(irq, dev_id) 函式原型:void free_irq(unsigned int irq, void *dev_id) 裡面所做的事情 ①:出鏈 ②:禁止中斷 3、系統函式:s3c2410_gpio_getpin 如何使用: 直接拿GPIO口來判斷就行! if (s3c2410_gpio_getpin (s3c2410_GPG10) == 0) 對暫存器的讀取和判斷 4、休眠函式! 休眠函式: 函式原型: #define wait_event_interruptible(wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) \ __wait_event_interruptible(wq, condition, __ret); \ __ret; \ }) 裡面所做的事情: 先判斷condition,假如condition為0的話,就呼叫__wait_event_interruptible(wq, condition, __ret); 讓應用程式進行休眠; 假如!condition,不等於0的話,函式就跳過wait_event_interruptible,向下執行__wait_event_interruptible了!!! 所以我們運用該休眠函式的時候,要在wait_event_interruptible 後面,將condition標誌位置1; 例函: /* 中斷事件標誌, 中斷服務程式將它置1,forth_drv_read將它清0 */ wait_event_interruptible(button_waitq, ev_press); 還要定義static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 意思是:定義一個等待佇列頭結構體; 喚醒函式: ev_press = 1; Garmen: 表示中斷髮生了 wake_up_interruptible(&button_waitq); Garmen::喚醒休眠的程序 參考驅動函式: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static struct class *thirddrv_class; static struct class_device *thirddrv_class_dev; volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; volatile unsigned long *gpgcon; volatile unsigned long *gpgdat; /*Garmen : 中斷佇列頭*/ static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中斷事件標誌, 中斷服務程式將它置1,third_drv_read將它清0 */ static volatile int ev_press = 0; struct pin_desc{ unsigned int pin; unsigned int key_val; }; /* 鍵值: 按下時, 0x01, 0x02, 0x03, 0x04 */ /* 鍵值: 鬆開時, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; struct pin_desc pins_desc[4] = { {S3C2410_GPF0, 0x01}, {S3C2410_GPF2, 0x02}, {S3C2410_GPG3, 0x03}, {S3C2410_GPG11, 0x04}, }; /* * 確定按鍵值 */ 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); } static int third_drv_open(struct inode *inode, struct file *file) { /* 配置GPF0,2為輸入引腳 */ /* 配置GPG3,11為輸入引腳 */ request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0; } ssize_t third_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); /*Garmen : 同時將休眠標誌位至0 這樣又可以繼續休眠*/ ev_press = 0; return 1; } int third_drv_close(struct inode *inode, struct file *file) { free_irq(IRQ_EINT0, &pins_desc[0]); free_irq(IRQ_EINT2, &pins_desc[1]); free_irq(IRQ_EINT11, &pins_desc[2]); free_irq(IRQ_EINT19, &pins_desc[3]); return 0; } static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, /* 這是一個巨集,推向編譯模組時自動建立的__this_module變數 */ .open = third_drv_open, .read = third_drv_read, .release = third_drv_close, }; int major; static int third_drv_init(void) { major = register_chrdev(0, "third_drv", &sencod_drv_fops); thirddrv_class = class_create(THIS_MODULE, "third_drv"); thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); gpgdat = gpgcon + 1; return 0; } static void third_drv_exit(void) { unregister_chrdev(major, "third_drv"); class_device_unregister(thirddrv_class_dev); class_destroy(thirddrv_class); iounmap(gpfcon); iounmap(gpgcon); return 0; } module_init(third_drv_init); module_exit(third_drv_exit); MODULE_LICENSE("GPL"); 在測試程式中呼叫: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* thirddrvtest */ int main(int argc, char **argv) { int fd; unsigned char key_val; fd = open("/dev/buttons", O_RDWR); if (fd < 0) { printf("can't open!\n"); } while (1) { read(fd, &key_val, 1); printf("key_val = 0x%x\n", key_val); } return 0; }
分析request_irq函式 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) return request_threaded_irq(irq, handler, NULL, flags, name, dev); 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) struct irqaction *action; struct irq_desc *desc; action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); /*Garmen : 分配設定了action結構*/ if (!action) return -ENOMEM; action->handler = handler; action->thread_fn = thread_fn; action->flags = irqflags; action->name = devname; action->dev_id = dev_id; desc = irq_to_desc(irq); /*Garmen : 以中斷號為下標找到了這個desc全域性陣列項,將這個desc傳遞給下面的setup_irq */ retval = __setup_irq(irq, desc, action); /*Garmen : 然後呼叫__setup_irq 設定中斷*/ struct irqaction *old, **old_ptr; /*定義old指標做判斷*/ old_ptr = &desc->action; old = *old_ptr /*Garmen : 你知道什麼是共享中斷嗎?*/ ... /*Garmen : 對action做一系列判斷,比如是否是共享中斷啊等等,假如不是,就將我們傳進來的action加入aciton連結串列(具體看上面的圖) *然後desc->chip->settype 將引腳配置中斷引腳,還有那些flag什麼進行定義 *然後desc->->startup/enable 使能引腳 */ ... 插一小部分相關操作 開啟裝置: exec 5</dev/buttons /*Garmen : 開啟這個裝置,把它定位到5去*/ 檢視裝置: ps知道當前裝置 -sh是771 然後 ls -l /proc/771/fd 關閉裝置: exec 5<&- 刪除程序:kill -0 PID號 退出程序:kill -9 PID號 1、中斷申請函式: request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 函式原型: int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) 第一個引數:unsigned int irq 可例 : IRQ_EINT0 在Irq.h進行巨集定義(include\asm-arm) 在Irq.c 的s3c24xx_init_irq函式中有進行初始化; 中斷號是去Irq.h 將該巨集展開得到的一個數字 第二個引數:irq_handler_t handler 可例 : buttons_irq 是一個處理函式 第三個引數:unsigned long irqflags 可例:IRQT_BOTHEDGE 雙邊緣觸發 在Irq.h進行巨集定義 在Irq.c 的s3c_irqext_type中進行初始化 第四個引數:隨意 第五個引數:void *dev_id 它是void型的指標。所以不必顧慮太多; 它只不過是用來在free_irq解除安裝時候,與fre_irq的引數結合在一起,確定解除安裝哪一個irq_action 結構 小結:當發生IRQ_EINT0這個中斷的時候,就去呼叫這個buttons_irq中斷處理函式, 這個函式static irqreturn_t buttons_irq(int irq, void *dev_id),有兩個引數 第一個引數是中斷號等於IRQ_EINT0 ,第二個引數是dev_id 等於pins_desc[0] 敲黑板: 後面引入這樣的一個方法:將irq、*name、pin、key_val寫成一個結構體dev_id 例如 定義一個pin_desc結構體原型: struct pin_desc{ int irq; char *name; unsigned int pin; unsigned int key_val; }; 編寫我們自己想要的結構體: struct pin_desc pins_desc[4] = { {IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L}, {IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S}, {IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER}, {IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT}, }; 我們的註冊函式就可以這麼寫了: for (i = 0; i < 4; i++) { request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]); } 裡面所做的事情: ①:分配一個irqaction ②:把這個結構體放入irq_des[irq] <==action連結串列 ③:設定引腳 ④:中斷使能 2、中斷清除函式 free_irq(irq, dev_id) 函式原型:void free_irq(unsigned int irq, void *dev_id) 裡面所做的事情 ①:出鏈 ②:禁止中斷 3、系統函式:s3c2410_gpio_getpin 如何使用: 直接拿GPIO口來判斷就行! if (s3c2410_gpio_getpin (s3c2410_GPG10) == 0) 對暫存器的讀取和判斷 4、休眠函式! 休眠函式: 函式原型: #define wait_event_interruptible(wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) \ __wait_event_interruptible(wq, condition, __ret); \ __ret; \ }) 裡面所做的事情: 先判斷condition,假如condition為0的話,就呼叫__wait_event_interruptible(wq, condition, __ret); 讓應用程式進行休眠; 假如!condition,不等於0的話,函式就跳過wait_event_interruptible,向下執行__wait_event_interruptible了!!! 所以我們運用該休眠函式的時候,要在wait_event_interruptible 後面,將condition標誌位置1; 例函: /* 中斷事件標誌, 中斷服務程式將它置1,forth_drv_read將它清0 */ wait_event_interruptible(button_waitq, ev_press); 還要定義static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 意思是:定義一個等待佇列頭結構體; 喚醒函式: ev_press = 1; Garmen: 表示中斷髮生了 wake_up_interruptible(&button_waitq); Garmen::喚醒休眠的程序 參考驅動函式: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static struct class *thirddrv_class; static struct class_device *thirddrv_class_dev; volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; volatile unsigned long *gpgcon; volatile unsigned long *gpgdat; /*Garmen : 中斷佇列頭*/ static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中斷事件標誌, 中斷服務程式將它置1,third_drv_read將它清0 */ static volatile int ev_press = 0; struct pin_desc{ unsigned int pin; unsigned int key_val; }; /* 鍵值: 按下時, 0x01, 0x02, 0x03, 0x04 */ /* 鍵值: 鬆開時, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; struct pin_desc pins_desc[4] = { {S3C2410_GPF0, 0x01}, {S3C2410_GPF2, 0x02}, {S3C2410_GPG3, 0x03}, {S3C2410_GPG11, 0x04}, }; /* * 確定按鍵值 */ 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); } static int third_drv_open(struct inode *inode, struct file *file) { /* 配置GPF0,2為輸入引腳 */ /* 配置GPG3,11為輸入引腳 */ request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0; } ssize_t third_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); /*Garmen : 同時將休眠標誌位至0 這樣又可以繼續休眠*/ ev_press = 0; return 1; } int third_drv_close(struct inode *inode, struct file *file) { free_irq(IRQ_EINT0, &pins_desc[0]); free_irq(IRQ_EINT2, &pins_desc[1]); free_irq(IRQ_EINT11, &pins_desc[2]); free_irq(IRQ_EINT19, &pins_desc[3]); return 0; } static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, /* 這是一個巨集,推向編譯模組時自動建立的__this_module變數 */ .open = third_drv_open, .read = third_drv_read, .release = third_drv_close, }; int major; static int third_drv_init(void) { major = register_chrdev(0, "third_drv", &sencod_drv_fops); thirddrv_class = class_create(THIS_MODULE, "third_drv"); thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); gpgdat = gpgcon + 1; return 0; } static void third_drv_exit(void) { unregister_chrdev(major, "third_drv"); class_device_unregister(thirddrv_class_dev); class_destroy(thirddrv_class); iounmap(gpfcon); iounmap(gpgcon); return 0; } module_init(third_drv_init); module_exit(third_drv_exit); MODULE_LICENSE("GPL"); 在測試程式中呼叫: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* thirddrvtest */ int main(int argc, char **argv) { int fd; unsigned char key_val; fd = open("/dev/buttons", O_RDWR); if (fd < 0) { printf("can't open!\n"); } while (1) { read(fd, &key_val, 1); printf("key_val = 0x%x\n", key_val); } return 0; }