<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 鍵盤(pán)驅動(dòng)開(kāi)發(fā)

鍵盤(pán)驅動(dòng)開(kāi)發(fā)

——
作者: 時(shí)間:2007-03-13 來(lái)源: 收藏

鍵盤(pán)在所有的之中最為簡(jiǎn)單的一種,但它卻包含了的基本框架,對以后繼續深入學(xué)習其他復雜的大有裨益,以下便為你逐步剖析驅動(dòng)的開(kāi)發(fā)。采用的是查詢(xún)方式。
一.內核模塊的注冊和撤銷(xiāo)
  在加載模塊的時(shí)候,首先運行的是內核模塊的注冊函數。它的功能包括內核注冊設備以及變量的初始化。
static int head,tail;
int _init Keypad_init(void)
{
  int result;
  result=register_chrdev(KEY_LED_MAJOR,KEY_LED_NAME,&Keypad_fops);
 Keypad_clear();
 init_waitqueue_head(&queue);
 prink("%s %s initialized.n",KEY_LED_NAME,KEY_LED_VERSION);//不能用prinf
 return 0;
}
module_init(Keypad_init);//加載模塊
void _exit Keypad_cleanup(void)
{
  del_timer(&timer);
  unregister_chrdev(KEY_LED_MAJOR,KEY_LED_NAME);
  prink("Keypad driver removed n");
}
module_exit(Keypad_cleanup);//卸載該模塊
二.虛擬文件系統與硬件驅動(dòng)的接口
static struct file_operations Keypad_fops={
 open:Keypad_open,
 read:Keypad_read,
 poll:Keypad_poll,
 fasync:Keypad_fasync,
 release:Keypad_release,
};
該接口定義完之后一些便是對這幾個(gè)具體函數的實(shí)現了!現在我們一起進(jìn)入下一步吧,是不是覺(jué)得其實(shí)沒(méi)什么難度的呢?別那么早開(kāi)心著(zhù)呢?這幾個(gè)函數的實(shí)現時(shí)候,涉及到很多技術(shù),包括內核定時(shí)器,等待隊列的具體實(shí)現(阻塞方式),異步方式的具體實(shí)現技巧,循環(huán)隊列??吹竭@么多技術(shù)你是否感到很興奮呢?以下本人將以通俗的方式為你講解,希望你能理解。
三.設備的打開(kāi)操作接口函數具體實(shí)現(Keypad_open)
設備打開(kāi)一般包括兩大操作,一是完成設備的初始化,二是設備引用計數器加1
static int Keypad_open(struct inode *inode,struct file *filp)
{
 read_xy();
 try_module_get(THIS_MODULE);//此函數為linux 2.6內核增加的,不同于2.4內核,功能是計數器的值加1
 return 0;
}
static void read_xy(void)
{
 new_data();//獲取鍵值函數
 keypad_starttimer();//開(kāi)啟內核定時(shí)器,在固定周期時(shí)間內獲取鍵盤(pán)新的變化
}
以下實(shí)現鍵盤(pán)鍵值獲取函數read_xy()
主要是從KEY_CS(對應的讀入地址,之前可以根據具體的硬件設備定義,比如#define kEY_CS(*

(volatile unsigned short *)(0xf820000))此處應該根據具體的不同而不同!
將讀入的鍵值存入buf[]緩存中,環(huán)形緩沖的寫(xiě)指針是head,讀指針是tail,前面已經(jīng)定義過(guò)了
////////////////////////////////鍵盤(pán)事件的數據結構定義/////////////////////////////////
typedef struct{
 ulong status;//按鍵的值
 ulong click;//是否有按鍵按下,1表示有,0表示沒(méi)有
}KEY_EVENT 
static KEY_EVENT cur_data,buf[BUFSIZE];//BUFSIZE為宏定義,用于定義環(huán)形緩沖的大小
static void new_data(void)
{
 if((KEY_CS & 0xff)!=0xff) //從KEY_CS地址讀入數據,若有一個(gè)為0則表示有一個(gè)按鍵被按下了(此處硬件電路為低電平有效)
 {
    switch(KEY_CS & 0xff){
       case ~KEY0 & 0xff:
           cur_data.status=1;///////1被按下
           break;
      
       case ~KEY1 & 0xff:
           cur_data.status=2;//2被按下
           break;
       /////////其他一樣添加,懂嗎??
     }
     cur_data.click=1;
   }
   else if(KEY_CS & 0xff==0xff){
     cur_data.click=0;
    cur_data.status=0;
   }
   if(head!=tail){////////循環(huán)隊列緩沖區的應用在此開(kāi)始了^_^
     int last=head--;
     if(last<0)////////若已經(jīng)到了對首之前,則跳到隊尾,以實(shí)現循環(huán)隊列
      last=BUFSIZE-1;
   }
   //////按鍵信息存入循環(huán)隊列緩沖區中
   buf[head]=cur_data;
  if(++head==BUFSIZE)
    head=0;
  if(head==tail && tail++=BUFSIZE)
   tail=0;
  if(fasync)
   kill_fasync(&fasyc,SIGIO,POLL_IN);
  wake_up_interruptible(&queue);
}

接下來(lái)我們介紹其他幾個(gè)文件接口函數的實(shí)現
四.先介紹關(guān)閉函數keypad_release(),為什么先介紹它呢?道理很簡(jiǎn)單,應該它比較簡(jiǎn)單,先讓大家做下熱身運動(dòng),在介紹完這個(gè)之后,繼續會(huì )介紹一個(gè)比較復雜的函數.
  關(guān)閉操作主要實(shí)現的是:關(guān)閉設備異步通知,設備計數器減1,刪除定時(shí)器信號中斷
static int Keypad_release(struct inode *inode,struct)
{
  Keypad_fasync(-1,filp,0);
 module_put(THIS_MODULE);
 del_timer(&timer);
 return 0;
}
五.設備讀取操作接口函數實(shí)現Keypad_read()
  主要作用是從緩沖區讀取鍵值,通過(guò)調用get_data()實(shí)現,通過(guò)copy_to_user()函數將鍵值復制到用戶(hù)的數據區中
static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l)
{
  DECLEARE_WAITQUEUE(wait,current);//聲明等待隊列,將當前進(jìn)程加入到等待隊列中
  KEY_EVENT t;
  ulong out_buf[2];
  if(head==tail)//當前循環(huán)隊列中沒(méi)有數據可以讀取
  {
    if(filp->f_flags & O_NONBLOCK)//假如用戶(hù)采用的是非堵塞方式讀取
       return _EAGAIN;
    add_wait_queue(&queue,&wait);//將當前進(jìn)程加入等待隊列
    current->state=TASK_INTERRUPTIBLE;//設置當前進(jìn)程的狀態(tài)
    while((head==tail)&&!signal_pending(current))//假若還沒(méi)有數據到循環(huán)隊列并且當前進(jìn)程沒(méi)有受到信號
    {
       shedule();//進(jìn)程調度
       current->state=TASK_INTERRUPTIBLE;
    }
    current->state=TASK_RUNNING;
    remove_wait_queue(&queue,&wait);
    if(head==tail)
      return count;
    t=get_data();//調用get_data()函數,得到緩沖區中的數據,下面將給予詳細的 介紹
    out_buf[0]=t.status;
    out_buf[1]=t.click;
    copy_to_user(buf,&out_buf,sizeof(out_buf));//將得到的鍵值拷貝到用戶(hù)數據區
    return count;
   
  }
}
很自然我們就應該要介紹get_data()函數的實(shí)現了,該函數的功能就是從我們定義的循環(huán)隊列緩沖區中讀出我們要的鍵值,所以其實(shí)很簡(jiǎn)單的如果理解循環(huán)隊列的原理,在此不多加解釋?zhuān)蠹覒摼邆湟话愕臄祿Y構相關(guān)的知識吧
static KEY_EVENT get_data(void)
{
   int last=tail
   if(++tail==BUFSIZE)
    tail=0;
   return buf[last];
}
上面如果你看得懂得話(huà),那么可以進(jìn)入下面的學(xué)習了,主要介紹的是內核定時(shí)器的使用,利用等待隊列實(shí)現阻塞型I/O,poll系統調用,異步通知方式,介紹完之后,我將給出一個(gè)應用實(shí)例,對于有使用過(guò)文件操作系統調用的來(lái)說(shuō),對我們所寫(xiě)的鍵盤(pán)驅動(dòng)來(lái)說(shuō),他們基本上是一樣的。廢話(huà)少說(shuō),我們馬上開(kāi)始我們精彩的驅動(dòng)開(kāi)發(fā)!
六.內核定時(shí)器的使用
  在該驅動(dòng)中,我們假設對鍵盤(pán)的獲取是以0.2s為周期執行。源代碼如下
static struct timer_list timer;///////我們定義的定時(shí)器,也許你會(huì )問(wèn)timer_list是什么來(lái)的,其實(shí)一看名稱(chēng)就應該就知道了,而為什么要用到list那么多定時(shí)器呢?其實(shí)在linux中還有很多相同的定義,比如說(shuō)信號,我們定義的也是信號集,你可以定義該list是一個(gè)元素的,也可以是多個(gè)的。所以對于timer_list就可以這樣描述:在未來(lái)某一個(gè)特定時(shí)刻執行某一系列特定任務(wù)的功能。下面我們還會(huì )給出內核中timer_list的具體描述,
 static int Keypad_starttimer(void)
{
   init_timer(&timer);//初始化定時(shí)器結構
   timer.function=Keypad_timer;//超時(shí)服務(wù)程序
   timer.expires=jiffies+20;//當前時(shí)刻加0.2s
   add_timer(&timer);
   return 0;
}
///超時(shí)服務(wù)程序
static void Keypad_timer(unsigned long data)
{
  read_xy();
}
/////////接下來(lái)說(shuō)下timer-list這個(gè)數據結構,如果你不感興趣的話(huà)可以跳過(guò),該結構在

includelinuxtimer.h中定義
struct timer_list
{
   struct list_head entry;
   unsigned long expries;
  spinlock_t lock;
  unsigned long magic;
  void (*function)(unsigned long);
  unsigner long data;
  struct tvec_t_base_s *base;
}
七.利用等待隊列實(shí)現阻塞型IO
   在用戶(hù)程序執行讀操作的時(shí)候有可能尚且沒(méi)有數據可以讀取,為此需要讓read操作等待,直到有數據可以讀取,這就是阻塞型io,阻塞型io可以通過(guò)使用進(jìn)程休眠方法實(shí)現。在無(wú)數據可以讀取的時(shí)候,采用等待隊列讓進(jìn)程休眠,直到有數據到達的時(shí)候才喚醒進(jìn)程完成數據的讀操作。
  在本驅動(dòng)中的read,若循環(huán)隊列緩沖區中沒(méi)有數據,則進(jìn)程進(jìn)入休眠態(tài),定時(shí)器函數每隔0.2s讀取鍵值一次,將按鍵狀態(tài)放入緩沖并且適時(shí)喚醒進(jìn)程讀取數據。
  等待隊列的使用流程如下:
  1.聲明一個(gè)等待隊列
  2.把當前進(jìn)程加入到等待隊列中
  3.把進(jìn)程的狀態(tài)設置為T(mén)ASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE;
  4.調用schedule,以讓出cpu
 5.檢測所需要的資源是否可用,若是,把當前進(jìn)程從等待隊列中刪除,否則轉3循環(huán)
接下來(lái)我們在對read中有關(guān)等待隊列阻塞實(shí)現做具體的解釋
static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l)
{
  DECLEARE_WAITQUEUE(wait,current);//聲明等待隊列,將當前進(jìn)程加入到等待隊列中
  KEY_EVENT t;
  ulong out_buf[2];
  if(head==tail)//當前循環(huán)隊列中沒(méi)有數據可以讀取
  {
    if(filp->f_flags & O_NONBLOCK)//假如用戶(hù)采用的是非堵塞方式讀取
       return _EAGAIN;
    add_wait_queue(&queue,&wait);//將當前進(jìn)程加入等待隊列
    current->state=TASK_INTERRUPTIBLE;//設置當前進(jìn)程的狀態(tài)
    while((head==tail)&&!signal_pending(current))//假若還沒(méi)有數據到循環(huán)隊列并且當前進(jìn)程沒(méi)有受到信號(該類(lèi)信號具體來(lái)說(shuō)是未決的休眠)
    {
       shedule();//進(jìn)程調度
       current->state=TASK_INTERRUPTIBLE;
    }
    current->state=TASK_RUNNING;//該進(jìn)程恢復執行
    remove_wait_queue(&queue,&wait);//移出等待隊列
    if(head==tail)
      return count;
    t=get_data();//調用get_data()函數,得到緩沖區中的數據,下面將給予詳細的 介紹
    out_buf[0]=t.status;
    out_buf[1]=t.click;
    copy_to_user(buf,&out_buf,sizeof(out_buf));//將得到的鍵值拷貝到用戶(hù)數據區
    return count;
   
  }
}
八.poll系統調用操作接口函數
  當程序需要進(jìn)行對多個(gè)文件讀寫(xiě)時(shí),如果某個(gè)文件沒(méi)有準備好,則系統就會(huì )處于讀寫(xiě)阻塞的狀態(tài),這影響了其他文件的讀寫(xiě),為了避免讀寫(xiě)阻塞,一般可以在應用程序中使用poll或者select函數。當poll函數返回時(shí),會(huì )給出一個(gè)文件是否可讀寫(xiě)的標志,應用程序根據不同的標志讀寫(xiě)相應的文件,實(shí)現非阻塞的讀寫(xiě),poll()函數通過(guò)poll系統調用,調用對應設備驅動(dòng)的poll()接口函數,poll返回不同的標志,告訴主進(jìn)程文件是否可以讀寫(xiě),這些返回標志存放在includeasmpoll.h中
 
 標志 含義
 POLLIN 如果設備無(wú)阻塞的讀,就返回該值
 POLLRDNORM 通常的數據已經(jīng)準備好,可以讀了,就返回
該值。通常的做法是會(huì )返回(POLLLIN|POLLRDNORA)
 POLLRDBAND 如果可以從設備讀出帶外數據,就返回該值,它只可在linux內核的某些網(wǎng)絡(luò )代碼中使用,通常不用在設備驅動(dòng)程序中
 POLLPRI 如果可以無(wú)阻塞的讀取高優(yōu)先級(帶外)數據,就返回該值,返回該值會(huì )導致select

報告文件發(fā)生異常,以為select八帶外數據當作異常處理POLLHUP 當讀設備的進(jìn)程到達文件尾時(shí),驅動(dòng)程序必須返回該值,依照select的功能描述,調用select的進(jìn)程被告知進(jìn)程時(shí)可讀的。
 POLLERR 如果設備發(fā)生錯誤,就返回該值。
 POLLOUT 如果設備可以無(wú)阻塞地些,就返回該值
 POLLWRNORM 設備已經(jīng)準備好,可以寫(xiě)了,就返回該值。通常地做法是(POLLOUT|POLLNORM)
 POLLWRBAND 于POLLRDBAND類(lèi)似
在本章地驅動(dòng)程序中,Keypad_poll()函數在緩沖區有新數據時(shí)(當head?。絫ail),返回一個(gè)

POLLIN|POLLRDNORM,告訴主進(jìn)程有新的

九.在設備驅動(dòng)中實(shí)現異步通知
  雖然大多數時(shí)候阻塞型和非阻塞型操作的組合及poll方法可以有效查詢(xún)設備是否可以讀寫(xiě),但是如果驅動(dòng)程序能避免主動(dòng)的查詢(xún),改主動(dòng)為被動(dòng)的信號通知觸發(fā),則可以提高程序的效率,這也就是異步通知的目的。異步通知向進(jìn)程發(fā)送SIGIO信號,通知訪(fǎng)問(wèn)設備的進(jìn)程,表示該設備已經(jīng)準備好IO讀寫(xiě)了。
 之后就是如何實(shí)現異步通知的問(wèn)題了,要啟動(dòng)異步通知,必須執行兩個(gè)步驟:首先,須要制定某個(gè)作為文件的“屬主”。文件屬主的進(jìn)程ID保存在filp->f_owner中,這可以通過(guò)fcntl()系統調用執行F_SETOWN命令設置。此外,用戶(hù)程序還必須曙色之設備的FASYNC標志,以真正啟動(dòng)異步通知機制。這里的FASYNC標志也使用fcntl()設置。
 在完成這兩個(gè)步驟之后,當新數據到達時(shí)就會(huì )產(chǎn)生一個(gè)SIGNO信號,此信號發(fā)送到存放在filp->owner中的進(jìn)程。
 從驅動(dòng)的角度看,則主要時(shí)通過(guò)調用兩個(gè)內核提供的函數來(lái)實(shí)現就是了。他們分別是:int

fasync_helper()和void kill_fasync();這兩個(gè)函數定義在:includelinuxfsfcntl.h
  要實(shí)現異步,驅動(dòng)中只要如下編寫(xiě)即可
static struct fasync_struct *fasync;//首先是定義一個(gè)結構體
static int Keypad_release(struct inode *inode,struct file *filp)
{
  Keypad_fasync(-1,filp,0);//這是一個(gè)異步通知
 。。。。。。。
}
static int Keypad_fasync(int fd,struct file *filp,int on)
{
  int retval;
 retval=fasync_helper(fd,filp,on,&fasync);
 if(retval<0)
   return retval;
 return 0;
}
到此為止,鍵盤(pán)驅動(dòng)已經(jīng)介紹完了,接下來(lái)就介紹下一個(gè)利用使用驅動(dòng)的應用實(shí)例了。
以下程序的主體是一個(gè)條件循環(huán),每次循環(huán)執行一次,就讀取一次鍵值。
1。打開(kāi)Keypad設備
 #define DEV_NAME "/dev/Keypad"
int fb=0;
fb=open(DEV_NAME,O_RNONLY);
if(!fb){
  printf("Error:cannot open Keypad device.n");
  exit(1);
}
printf("The Keypad device was opened successfully.n");
}
2.讀取鍵值
unsigned long keydata[2];
int input=1;
while(input!=0)
{
  if(read(fd,(char*)keydata,sizeof(keydata))==-1){
    printf("Error reading the keypad data");
    close(fb);
    exit(2);
   }
   if(keydata[0]){
   switch(keydata[1]){
     case 1:printf("KEYPUSED 1");//1鍵被按下
           input=0;////下此循環(huán)退出
           break;
     。。。。。。。。。。。。。。。。。。
   }
  }
}
3。關(guān)閉Keypad設備
close(fb);
printf("Good bye Keypad");
 
鍵盤(pán)驅動(dòng)到此介紹完畢??!

塵埃粒子計數器相關(guān)文章:塵埃粒子計數器原理


關(guān)鍵詞: 驅動(dòng)

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>