<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

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

          鍵盤驅(qū)動(dòng)開(kāi)發(fā)

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

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

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

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

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

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

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

          fasync_helper()和void kill_fasync();這兩個(gè)函數(shù)定義在:includelinuxfsfcntl.h
            要實(shí)現(xiàn)異步,驅(qū)動(dòng)中只要如下編寫即可
          static struct fasync_struct *fasync;//首先是定義一個(gè)結(jié)構(gòu)體
          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;
          }
          到此為止,鍵盤驅(qū)動(dòng)已經(jīng)介紹完了,接下來(lái)就介紹下一個(gè)利用使用驅(qū)動(dòng)的應(yīng)用實(shí)例了。
          以下程序的主體是一個(gè)條件循環(huán),每次循環(huán)執(zhí)行一次,就讀取一次鍵值。
          1。打開(kāi)Keypad設(shè)備
           #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設(shè)備
          close(fb);
          printf("Good bye Keypad");
           
          鍵盤驅(qū)動(dòng)到此介紹完畢!!

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


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

          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();