LINUX按鍵驅動程序
《《混雜設備驅動模型》》
《混雜設設備的描述》
<混在設備的概念>
在linux系統中,存在一類字符設備,他們擁有相同的主設備號(10),但是次設備號不同,稱這類設備為混在設備(missdevice),所有的混雜設備形成一個鏈表,對設備進行訪問,根據次設備號在鏈表中查找相應的混雜設備。
註意:混雜設備是字符設備的一種。
<混雜設備的設備描述符>
struct miscdevice
{
int minor; /*次設備號*/
const char *name; /*設備名*/
const struct file_operrations *fops; /*文件操作*/
struct list_head list;
struct device *parent;
struct device *this _device ;
};
<設備註冊>
在linux系統中歐使用函數 misc_register() 函數來註冊一個混雜設備驅動。
函數原型:
int misc_register(struct miscdevice *misc)
<設備註銷>
函數原型:
int mic_deregister (struct miscdevice * misc)
《linux中斷處理》
<裸機中中斷處理流程>
中斷會有一個統一的入口: ldr pc,_irq ——》保存環境處理——》執行:
<linux中中斷處理流程分析>
在linux系統中也有一個中斷處理的同樣的入口:irq_svc ——》接著做相應的環境保存——》獲取產生中斷的中斷源(寄存器 INTPND)——》利用中斷號找到irq_disc[*]這個結構——》在action 中就有用戶事先填寫編寫好的處理函數
總結:驅動程序需要做哪些事?
1)實現中斷處理過程
2)將中斷處理程序註冊到linux系統中
<linux 中中斷處理程序>
1)註冊中斷
函數原型:
int
request_irq(unsigned int irq, void (*handler )(int ,void *,struct
pt_regs*),unsigned long flags,const char*devname, void *dev_id)
參數分析:
unsigned int irq: 中斷號
註意:中斷號和中斷類型區別
#define S3C2410_CPUIRQ_OFFSET (16)
#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
void (*handler )(int ,void * , struct pt_regs * ): 中斷處理程序
unsigned long flags :與中斷處理有關的各種的選項
例:
IRQF_DISABLEED (SA_INTERRUPT) :快速中斷處理程序,如果沒有該位,則表示慢速中斷處理程序
註意:快速中斷和慢速中斷的區別
快/慢速中斷主要區別在於:快速中斷保證中斷處理的原子性(不被打斷),而慢速中斷則不保證,換句話說就是開啟中斷標誌位 (屏蔽)(處理器 IF),在運行快速中斷程序是關閉的,英因此在服務該中斷時,不會別其他類型的中斷打斷;而調用慢速中斷處理時,其他類型的中斷可以得到服務。
IRQF_SHARED(SA_SHIRQ):表明該中斷號是設備共享的()多個設備可以共享多個中斷。
IRQF_TRIGGER_FALLING: 下降沿產生中斷
const char * devname:設備名
void * dev_id :共享中斷時使用
返回值:
返回0 表示成功
失敗:
返回一個非0 的錯誤值
註意:中斷處理程序是在中斷上下文中運行的,他的行為可能受某些限制:
1)不能使用可能引起阻塞的函數
2)不能使用可能引起調度的函數
2)中斷處理
一般處理流程:
檢查設備是否產生中斷——》清除中斷標誌(要不然下次中斷沒法響應)——》相應的硬件操作
3)註銷中斷
函數原型:
void free_irq(unsigned int irq,void *dev_id)
參數分析:
irq;中斷號
dev_id:對於共享中斷,一個中斷號,對應多個中斷程序,位了將加載的中斷處理程序卸載,將其中對每個中斷處理程序進行編號:dev_id
《中斷分層技術》
<中斷嵌套>
(1)慢速中中斷
當處理慢速中斷當過程中,中斷開關(IF)是關閉當的,即可以被其他中斷打斷,執行本中斷,直到處理完成,再返回執行執之前被打斷當中斷。
註意:如果執行中斷當過程中是被同類型當中斷打斷,此時linux系統是不會執行該中斷當。
(2)快速中斷
不可以被其他中斷打斷
<中斷分層技術>
背景:
中斷處理流程大致可以分為和和硬件有關的工作,和硬件無關當工作,linux系統中,將和硬件有關的工作被放到中斷處理程序中去做,其他的部分放到其他地方做。其目的是減少處理中斷處理的時間。使用以下三中方式處理和硬件無關部分處理。
(1)軟中斷
(2)tasklet
(3)工作隊列
由上圖可以看見,加入說這是一個3核當CPU,每一CPU後面都跟有一個隊列,將和硬件沒有關系的處理程序插入到這些鏈表中,linux內核會為每一個鏈表創建一個線程,系統會在CPU相對空閑的時候把掛著的線程處理一遍,當處理完一個線程該鏈表就會從該隊列中消失。
<使用工作隊列實現分層>
(1)如何實現工作隊列
struct workqueue_struct
{
struct cpu_workqueue_struct *cpu_wq;
struct list_head list;
const char *name; /*workqueue name */
int signalthread;
int freezeable /*freeze threads during suspend*/
int rt;
}
(2)如何描述一個工作
struct work_struct
{
atomic_long_t data;
struct list _head entry;
work_fun_t func;
}
(3)實現步驟
1)創建工作隊列
函數:
create_workqueue(“name”)
2)創建工作
函數:
INIT_WORK(work,fun)
3)提交工作(將工作掛載到工作隊列上)
函數:
queue_work(work_queue,work)
註意:要創建工作隊列,必須遵循GPL協議。
MOUDLE_LICENSE(“GPL”)
註意:但是在大多數情況下並不需要我們定義工作隊列,linux系統會有一個默認當工作隊列keventd_wq,所以只需要將工作掛載到該工作隊列上即可。
掛載函數:
schedule_work(work)
《按鍵去抖》
<背景分析>
在按動按鍵的時候,因硬件設計的缺陷,會導致信號出現尖刺,從而導致輸入信號不準確。
<處理按鍵抖動>
(1)硬件去抖
(2)軟件去抖
一般軟件去抖采用延時的方式:
1)for()循環
2)定時延時
在Linux系統中使用結構體數組:
struct timer_list
{
struct list_head entry;
unsigned long expires;
viod (*function )(unsigned long);
unsigned long data;
struct tvec_base *base;
}
expires:用來設定時間
function:這個函數指針指向定時結束後需要處理的工作。
來描述一個定時器。
註意:一般操作系統為處理的高效率,一般是不使用for()循環的。只能使用定時器。
<定時器處理流程>
(1)定義定時器變量
(2)初始化定時器
1)init_timer()
2)設置超時函數
(3)add_timer()用來向內核註冊定時器
(4)mod_timer()啟動定時器
<多按鍵驅動的優化>
相對於一個按鍵的驅動程序,需要多註冊一個中斷,註冊中斷就需要在對硬件操作部分多做一個引腳的功能配置。
註意:首先要實現相應的硬件驅動程序,才能在應用程序中做相應的系統調用。
<阻塞型驅動程序的設計>
(1)阻塞必要
當一個設備無法滿足用戶的讀寫請求的時候該怎麽辦?例如使用函數然而以後會有數據,或者一個進程企圖向一個設備寫入數據的時候,然而,此時設備沒有做好接受數據的準備,當上述情況發生時,驅動程序應當(缺省的)阻塞進程,使進程進入等待(睡眠)狀態,知道請求得到滿足。
(2)內核等待隊列
在實現阻塞驅動的過程中,也需要一個類似“候車室”的地方來安排阻塞隊列的休息,當喚醒條件成熟時,則可以從候車室中將進程喚醒,這個“候車室”就是等待隊列。
1)定義等待隊列
wait_queue_head_t my_queue
2)初始化等待隊列
init_waitqueue_head(&my_queue)
3)或者定義並初始化等待隊列
DECLARE_WAIT_QUEUE_HEAD(my_queue)
4)進入等待隊列
wait_event(queue,condition)
當condition(布爾表達式)為真時,立即返回,否者讓進程進入TASK_UNINTERRUPTIBLE 模式的睡眠,並掛載到queue參數指定的等待隊列上。
wait_event_interrupttible(queue,condition)
當condition(布爾表達式)為真時,立即返回,否者讓進程進入TASK_INTERRUPTIBLE的睡眠,並掛載在參數queue參數指定的隊列上。
5)從等待隊列中喚醒
wake_up(wait_queue_t *q)
從等待隊列q 中喚醒狀態為TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有進程
wake_up_interruptible(wait_queue_t *q)
從等待隊列中q中,喚醒狀態為TASK_INTERRUPTIBLE的進程。
(3)阻塞驅動優化
L
LINUX按鍵驅動程序