裝置驅動的中斷事件處理如下圖所示,他與普通驅動的不同之處在於多了個資料緩衝區,驅動程式對上層提供的read/write方法並不直接完成硬體的資料操作,中斷處理常式也是相對獨立,他們通過緩衝區交換資料。而資料緩衝區的資料可通過FIFO進行讀和寫的操作,但每次只能進行讀或者寫的操作,兩者不可同時進行,這就又涉及到了並發請求。並發請求類似訊號量中的PV操作,對資料緩衝區的讀寫就相當於之前的生產者和消費者到倉庫的存取。
中斷事件涉及的函數
標頭檔
#include <asm/irq.h>
申請中斷函數,成功返回0
int request_irq(unsigned int irq,
void(*handler)(int,void*,struct pt_regs*),
unsigned long irqflag,
const char *devname, void *dev_id);
//irq:中斷號
// irqflag:
// SA_INTERRUPT,快速中斷,執行handler函數時不能被打斷
// SA_SHIRQ,共用中斷, ,執行handler函數時能被打斷
// SA_SAMPLE_RANDOM,中斷可能被用來產生隨機數
// dev_id: 在共用中斷時用於區分不同的中斷處理常式
// devname:中斷裝置名稱,在中斷下運行cat /dev/interrupt 可查看到
釋放中斷函數
void free_irq(unsigned int irq, void *dev_id);
#include <asm/irq.h>
使能中斷函數
void enable_irq(unsigned int irq);
禁止中斷函數
void disable_irq(unsigned int irq);
S3C2410使能GPIO的外部中斷功能
int set_external_irq(int irq, int edge, int pullup);
// edge:
// EXT_LOWLEVEL,
// EXT_HIGHLEVEL,
// EXT_FALLING_EDGE,
// EXT_RISING_EDGE,
// EXT_BOTH_EDGES
中斷處理常式樣本
void handler(int irq, void *dev_id, struct pt_regs *regs)
{
// 中斷處理
}
在初始化時申請並初始化中斷
set_external_irq(IRQ_EINT0, EXT_FALLING_EDGE, 0);
request_irq(IRQ_EINT0,handler,SA_INTERRUPT,"KEY",NULL);
注意:要把使能外部中斷函數set_external_irq放在插斷要求函數request_irq之前,否則,會有小麻煩。
並發請求涉及的函數
使用訊號量控制並發請求
#include <asm/semaphore.h>
定義訊號量變數
struct semaphore bufflock;
初始化訊號量
void sema_init(struct semaphore *sem, int);
擷取(等待)訊號量(不可打斷)(相當於V操作)
void down(struct semaphore *sem);
擷取(等待)訊號量(可被打斷)
int down_interruptible(struct semaphore *sem);
釋放訊號量(相當於P操作)
void up(struct semaphore *sem);
FIFO隊列
由於隊列具有先進先出的功能,所以它能符合充當資料緩衝區的功能。我們可以自己寫一個FIFO函數實現讀和寫的功能,也可調用/kernel/kernel/fifo.c 的函數。如果要調用/kernel/kernel/fifo.c,編譯時間要注意,可用命令進行:/usr/local/arm/2.95.3/bin/arm-linux-gcc -c -o kfifo.o kfifo.c -I/home/su/kernel/include -D__KERNEL__ -DMODULE -DEXPORT_SYMTAB –DMODVERSIONS。當我們完成編譯後,要先載入kfifo.o,再載入我們自己寫的驅動,否則會出錯。以下以調用/kernel/kernel/fifo.c為例。
#include <linux/kfifo.h>
首先需要定義一個自旋鎖,該鎖由kfifo維護,我們只需要定義它即可
static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED;
然後定義一個kfifo指標, 注意, 這裡定義指標即可,不用分配空間
static struct kfifo *buffer;
使用kfifo_alloc可以建立一個BUFFER_SIZE大小的fifo, 所有空間由kfifo自動分配
#define BUFFER_SIZE 256
buffer = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL, &buffer_lock);
使用kfifo_put可以將資料放入kfifo內
kfifo_put(buffer, &key, sizeof(key));
使用kfifo_len可以檢查fifo內的可用資料
if(kfifo_len(buffer) >= sizeof(key))
使用kfifo_get可以從fifo內取出資料
kfifo_get(buffer, &key, sizeof(key));
最後退出時,釋放buffer的記憶體空間
kfifo_free(buffer);
附:基於淩陽SPCE3200實驗箱的4x4鍵盤的驅動(中斷法實現)原始碼
這裡有兩個需要注意的地方:
1、標頭檔的擺放順序:#include <linux/kfifo.h>要放在 #include <asm/irq.h>之後,否則編譯出錯,因為asm/irq.h 裡面定義了一些linux/kfifo.h 要用到的宏,若先寫#include <linux/kfifo.h> ,載入驅動時,編譯器會把裡頭的宏當成函數處理,但又找不到相應的函數,所以出錯。這樣的標頭檔出錯,出錯非常隱蔽,解決的辦法唯有一步一步跟錯尋找。
2、該驅動的等待訊號量函數應使用可打斷類型的int down_interruptible(struct semaphore *sem),否則運行應用程式時,按下ctrl+c程式沒法正常退出,還需按下實驗箱上的任意一個按鍵。因為該中斷是可被打斷類型的,所以能接受響應外部的另外的中斷,即能退出程式。
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/init.h> /* __init __exit */
#include <linux/types.h> /* size_t */
#include <linux/fs.h> /* file_operation */
//#include <linux/errno.h> /* Error number */
#include <linux/delay.h> /* udelay */
#include <linux/timer.h>
#include <asm/uaccess.h> /* copy_to_user, copy_from_user */
#include <asm/hardware.h>
#include <asm/semaphore.h>
#include <asm/irq.h>
#include <asm/arch/S3C2410.h>
#include <linux/kfifo.h>
#define DRIVER_NAME "key"
#ifdef DEBUG
#define PRINTK(fmt, arg...) printk(KERN_NOTICE fmt, ##arg)
#else
#define PRINTK(fmt, arg...)
#endif
#define BUFFER_SIZE 256
static int keyDriver_Major = 0; /* Driver Major Number */
static int keyNum;
struct semaphore bufflock;
static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED;