
鍵盤在所有的驅動之中最為簡單的一種,但它卻包含了驅動的基本框架,對以后繼續深入學習其他復雜的驅動大有裨益,以下便為你逐步剖析驅動的開發。采用的是查詢方式。一.內核模塊的注冊和撤銷 在加載模塊的時候,首先運行的是內核模塊的注冊函數。它的功能包括內核注冊設備以及變量的初始化。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);//卸載該模塊二.虛擬文件系統與硬件驅動的接口static struct file_operations Keypad_fops={ open:Keypad_open, read:Keypad_read, poll:Keypad_poll, fasync:Keypad_fasync, release:Keypad_release,};該接口定義完之后一些便是對這幾個具體函數的實現了!現在我們一起進入下一步吧,是不是覺得其實沒什么難度的呢?別那么早開心著呢?這幾個函數的實現時候,涉及到很多技術,包括內核定時器,等待隊列的具體實現(阻塞方式),異步方式的具體實現技巧,循環隊列。看到這么多技術你是否感到很興奮呢?以下本人將以通俗的方式為你講解,希望你能理解。三.設備的打開操作接口函數具體實現(Keypad_open)設備打開一般包括兩大操作,一是完成設備的初始化,二是設備引用計數器加1static 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();//開啟內核定時器,在固定周期時間內獲取鍵盤新的變化}以下實現鍵盤鍵值獲取函數read_xy()主要是從KEY_CS(對應的讀入地址,之前可以根據具體的硬件設備定義,比如#define kEY_CS(*
(volatile unsigned short *)(0xf820000))此處應該根據具體的不同而不同!將讀入的鍵值存入buf[]緩存中,環形緩沖的寫指針是head,讀指針是tail,前面已經定義過了////////////////////////////////鍵盤事件的數據結構定義/////////////////////////////////typedef struct{ ulong status;//按鍵的值 ulong click;//是否有按鍵按下,1表示有,0表示沒有}KEY_EVENT static KEY_EVENT cur_data,buf[BUFSIZE];//BUFSIZE為宏定義,用于定義環形緩沖的大小static void new_data(void){ if((KEY_CS & 0xff)!=0xff) //從KEY_CS地址讀入數據,若有一個為0則表示有一個按鍵被按下了(此處硬件電路為低電平有效) { 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){////////循環隊列緩沖區的應用在此開始了^_^ int last=head--; if(last<0)////////若已經到了對首之前,則跳到隊尾,以實現循環隊列 last=BUFSIZE-1; } //////按鍵信息存入循環隊列緩沖區中 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);}
接下來我們介紹其他幾個文件接口函數的實現四.先介紹關閉函數keypad_release(),為什么先介紹它呢?道理很簡單,應該它比較簡單,先讓大家做下熱身運動,在介紹完這個之后,繼續會介紹一個比較復雜的函數. 關閉操作主要實現的是:關閉設備異步通知,設備計數器減1,刪除定時器信號中斷static int Keypad_release(struct inode *inode,struct){ Keypad_fasync(-1,filp,0); module_put(THIS_MODULE); del_timer(&timer); return 0;}五.設備讀取操作接口函數實現Keypad_read() 主要作用是從緩沖區讀取鍵值,通過調用get_data()實現,通過copy_to_user()函數將鍵值復制到用戶的數據區中static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l){ DECLEARE_WAITQUEUE(wait,current);//聲明等待隊列,將當前進程加入到等待隊列中 KEY_EVENT t; ulong out_buf[2]; if(head==tail)//當前循環隊列中沒有數據可以讀取 { if(filp->f_flags & O_NONBLOCK)//假如用戶采用的是非堵塞方式讀取 return _EAGAIN; add_wait_queue(&queue,&wait);//將當前進程加入等待隊列 current->state=TASK_INTERRUPTIBLE;//設置當前進程的狀態 while((head==tail)&&!signal_pending(current))//假若還沒有數據到循環隊列并且當前進程沒有受到信號 { shedule();//進程調度 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));//將得到的鍵值拷貝到用戶數據區 return count; }}很自然我們就應該要介紹get_data()函數的實現了,該函數的功能就是從我們定義的循環隊列緩沖區中讀出我們要的鍵值,所以其實很簡單的如果理解循環隊列的原理,在此不多加解釋,大家應該具備一般的數據結構相關的知識吧static KEY_EVENT get_data(void){ int last=tail if(++tail==BUFSIZE) tail=0; return buf[last];}上面如果你看得懂得話,那么可以進入下面的學習了,主要介紹的是內核定時器的使用,利用等待隊列實現阻塞型I/O,poll系統調用,異步通知方式,介紹完之后,我將給出一個應用實例,對于有使用過文件操作系統調用的來說,對我們所寫的鍵盤驅動來說,他們基本上是一樣的。廢話少說,我們馬上開始我們精彩的驅動開發!六.內核定時器的使用 在該驅動中,我們假設對鍵盤的獲取是以0.2s為周期執行。源代碼如下static struct timer_list timer;///////我們定義的定時器,也許你會問timer_list是什么來的,其實一看名稱就應該就知道了,而為什么要用到list那么多定時器呢?其實在linux中還有很多相同的定義,比如說信號,我們定義的也是信號集,你可以定義該list是一個元素的,也可以是多個的。所以對于timer_list就可以這樣描述:在未來某一個特定時刻執行某一系列特定任務的功能。下面我們還會給出內核中timer_list的具體描述, static int Keypad_starttimer(void){ init_timer(&timer);//初始化定時器結構 timer.function=Keypad_timer;//超時服務程序 timer.expires=jiffies+20;//當前時刻加0.2s add_timer(&timer); return 0;}///超時服務程序static void Keypad_timer(unsigned long data){ read_xy();}/////////接下來說下timer-list這個數據結構,如果你不感興趣的話可以跳過,該結構在
include\linux\timer.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;}七.利用等待隊列實現阻塞型I\O 在用戶程序執行讀操作的時候有可能尚且沒有數據可以讀取,為此需要讓read操作等待,直到有數據可以讀取,這就是阻塞型i\o,阻塞型io可以通過使用進程休眠方法實現。在無數據可以讀取的時候,采用等待隊列讓進程休眠,直到有數據到達的時候才喚醒進程完成數據的讀操作。 在本驅動中的read,若循環隊列緩沖區中沒有數據,則進程進入休眠態,定時器函數每隔0.2s讀取鍵值一次,將按鍵狀態放入緩沖并且適時喚醒進程讀取數據。 等待隊列的使用流程如下: 1.聲明一個等待隊列 2.把當前進程加入到等待隊列中 3.把進程的狀態設置為TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE; 4.調用schedule,以讓出cpu 5.檢測所需要的資源是否可用,若是,把當前進程從等待隊列中刪除,否則轉3循環接下來我們在對read中有關等待隊列阻塞實現做具體的解釋static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l){ DECLEARE_WAITQUEUE(wait,current);//聲明等待隊列,將當前進程加入到等待隊列中 KEY_EVENT t; ulong out_buf[2]; if(head==tail)//當前循環隊列中沒有數據可以讀取 { if(filp->f_flags & O_NONBLOCK)//假如用戶采用的是非堵塞方式讀取 return _EAGAIN; add_wait_queue(&queue,&wait);//將當前進程加入等待隊列 current->state=TASK_INTERRUPTIBLE;//設置當前進程的狀態 while((head==tail)&&!signal_pending(current))//假若還沒有數據到循環隊列并且當前進程沒有受到信號(該類信號具體來說是未決的休眠) { shedule();//進程調度 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));//將得到的鍵值拷貝到用戶數據區 return count; }}八.poll系統調用操作接口函數 當程序需要進行對多個文件讀寫時,如果某個文件沒有準備好,則系統就會處于讀寫阻塞的狀態,這影響了其他文件的讀寫,為了避免讀寫阻塞,一般可以在應用程序中使用poll或者select函數。當poll函數返回時,會給出一個文件是否可讀寫的標志,應用程序根據不同的標志讀寫相應的文件,實現非阻塞的讀寫,poll()函數通過poll系統調用,調用對應設備驅動的poll()接口函數,poll返回不同的標志,告訴主進程文件是否可以讀寫,這些返回標志存放在include\asm\poll.h中 標志 含義 POLLIN 如果設備無阻塞的讀,就返回該值 POLLRDNORM 通常的數據已經準備好,可以讀了,就返回該值。通常的做法是會返回(POLLLIN|POLLRDNORA) POLLRDBAND 如果可以從設備讀出帶外數據,就返回該值,它只可在linux內核的某些網絡代碼中使用,通常不用在設備驅動程序中 POLLPRI 如果可以無阻塞的讀取高優先級(帶外)數據,就返回該值,返回該值會導致select
報告文件發生異常,以為select八帶外數據當作異常處理POLLHUP 當讀設備的進程到達文件尾時,驅動程序必須返回該值,依照select的功能描述,調用select的進程被告知進程時可讀的。 POLLERR 如果設備發生錯誤,就返回該值。 POLLOUT 如果設備可以無阻塞地些,就返回該值 POLLWRNORM 設備已經準備好,可以寫了,就返回該值。通常地做法是(POLLOUT|POLLNORM) POLLWRBAND 于POLLRDBAND類似在本章地驅動程序中,Keypad_poll()函數在緩沖區有新數據時(當head!=tail),返回一個
POLLIN|POLLRDNORM,告訴主進程有新的
九.在設備驅動中實現異步通知 雖然大多數時候阻塞型和非阻塞型操作的組合及poll方法可以有效查詢設備是否可以讀寫,但是如果驅動程序能避免主動的查詢,改主動為被動的信號通知觸發,則可以提高程序的效率,這也就是異步通知的目的。異步通知向進程發送SIGIO信號,通知訪問設備的進程,表示該設備已經準備好IO讀寫了。 之后就是如何實現異步通知的問題了,要啟動異步通知,必須執行兩個步驟:首先,須要制定某個作為文件的“屬主”。文件屬主的進程ID保存在filp->f_owner中,這可以通過fcntl()系統調用執行F_SETOWN命令設置。此外,用戶程序還必須曙色之設備的FASYNC標志,以真正啟動異步通知機制。這里的FASYNC標志也使用fcntl()設置。 在完成這兩個步驟之后,當新數據到達時就會產生一個SIGNO信號,此信號發送到存放在filp->owner中的進程。 從驅動的角度看,則主要時通過調用兩個內核提供的函數來實現就是了。他們分別是:int
fasync_helper()和void kill_fasync();這兩個函數定義在:include\linux\fs\fcntl.h 要實現異步,驅動中只要如下編寫即可static struct fasync_struct *fasync;//首先是定義一個結構體static int Keypad_release(struct inode *inode,struct file *filp){ Keypad_fasync(-1,filp,0);//這是一個異步通知 。。。。。。。}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;}到此為止,鍵盤驅動已經介紹完了,接下來就介紹下一個利用使用驅動的應用實例了。以下程序的主體是一個條件循環,每次循環執行一次,就讀取一次鍵值。1。打開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;////下此循環退出 break; 。。。。。。。。。。。。。。。。。。 } }}3。關閉Keypad設備close(fb);printf("Good bye Keypad"); 鍵盤驅動到此介紹完畢!!
關鍵詞: 驅動
網站首頁 |網站簡介 | 關于我們 | 廣告業務 | 投稿信箱
Copyright © 2000-2020 www.yjkq2010.com All Rights Reserved.
中國網絡消費網 版權所有 未經書面授權 不得復制或建立鏡像
聯系郵箱:920 891 263@qq.com
欧美色综合网_狠狠色狠色综合曰曰_麻豆精品一区二区av白丝在线_久久精品综合一区 天天av天天翘天天综合网色鬼国产| 7799精品视频| 狠狠狠色丁香婷婷综合激情 | 日韩精品一级二级| 懂色av中文一区二区三区| 日韩精品乱码免费| 91首页免费视频| 亚洲国产精品99久久久久久久久| 极品尤物av久久免费看| 欧洲av一区二区嗯嗯嗯啊| 国产精品国产三级国产aⅴ入口| 免费欧美日韩国产三级电影| 欧美视频在线播放| 26uuu色噜噜精品一区二区| 日韩电影一二三区| 在线视频你懂得一区二区三区| 欧美国产成人精品| 国产风韵犹存在线视精品| 欧美一区二区三区视频免费 | 成人黄色综合网站| 日韩视频在线你懂得| 亚洲一区二区三区国产| 欧美日韩在线播放一区| 欧美一区二区私人影院日本| 午夜欧美在线一二页| 欧美日韩一区二区在线观看| 亚洲高清一区二区三区| 91精品午夜视频| 另类中文字幕网| 欧美成人国产一区二区| 久久精品久久99精品久久| 精品999在线播放| 成人看片黄a免费看在线| 亚洲欧美中日韩| 欧美丝袜丝交足nylons图片| 亚洲视频免费在线| 在线播放一区二区三区| 五月天一区二区三区| 日韩欧美资源站| 狠狠色丁香婷婷综合久久片| 国产精品无圣光一区二区| 91小视频在线免费看| 五月综合激情日本mⅴ| 在线看国产日韩| 最新日韩av在线| 欧美伊人久久久久久久久影院| 日韩经典中文字幕一区| 精品88久久久久88久久久| www.视频一区| 午夜精品福利视频网站| 91精品国产手机| 国产成人aaaa| 天堂资源在线中文精品| 欧美成人bangbros| aaa亚洲精品一二三区| 亚洲高清免费观看| 欧美www视频| 色成年激情久久综合| 久久国产综合精品| 亚洲国产精品久久人人爱蜜臀| ww亚洲ww在线观看国产| 91论坛在线播放| 精品亚洲免费视频| 亚洲狠狠丁香婷婷综合久久久| 日韩欧美卡一卡二| 日本韩国精品一区二区在线观看| 免费成人av在线| 亚洲精品成人在线| www一区二区| 欧美一区二区在线免费播放 | 国产精品免费视频一区| 欧美视频你懂的| 国产精品一二二区| av资源网一区| 爽好多水快深点欧美视频| 亚洲色图.com| 国产欧美一区二区三区网站| 精品福利在线导航| 日韩三级中文字幕| 91极品视觉盛宴| 色婷婷久久99综合精品jk白丝| 欧美色欧美亚洲另类二区| 中文字幕巨乱亚洲| 日韩色视频在线观看| 正在播放一区二区| 欧美日韩免费一区二区三区 | 色婷婷综合久色| 欧美日韩一级二级| 欧美人xxxx| 久久精品国产一区二区三| 免费人成网站在线观看欧美高清| 亚洲精品久久久蜜桃| 国产精品久久福利| 久久久久国产精品免费免费搜索| 不卡av在线免费观看| 不卡一卡二卡三乱码免费网站| 成人免费va视频| 色综合网站在线| 日韩激情中文字幕| 午夜精品一区二区三区免费视频| 中文字幕一区在线| 最新热久久免费视频| 亚洲一区二区综合| 免费成人在线影院| 国产成人午夜精品影院观看视频| 国产成都精品91一区二区三| 91丨porny丨蝌蚪视频| 欧美日韩一区三区| 日韩亚洲欧美在线观看| 亚洲日本一区二区| 日韩国产欧美在线视频| 国产精品一线二线三线精华| 99久久国产免费看| 欧美一区二区三区四区在线观看| 精品国产青草久久久久福利| 久久夜色精品国产噜噜av| 国产精品国产三级国产a| 亚洲一区二区av在线| 亚洲在线一区二区三区| 久久免费的精品国产v∧| 亚洲一区二区三区免费视频| 久久国产精品一区二区| av电影天堂一区二区在线| 欧美一级片免费看| 中文字幕一区二区三区四区| 亚洲一区二区av电影| 国产激情视频一区二区在线观看 | 国产成人8x视频一区二区| 欧洲另类一二三四区| 2017欧美狠狠色| 蜜桃av一区二区三区电影| 成人av小说网| 丁香啪啪综合成人亚洲小说| 欧美在线三级电影| 国产色一区二区| 日韩中文字幕不卡| 国产乱码精品一区二区三区五月婷| 色婷婷av一区二区三区软件| 国产调教视频一区| 日韩av二区在线播放| 精品一区二区三区不卡| 欧美日韩在线电影| 久久老女人爱爱| 亚洲成人福利片| 色综合久久综合网欧美综合网| 久久久美女艺术照精彩视频福利播放| 国产精品美女久久久久av爽李琼| 日本欧美韩国一区三区| 国内一区二区在线| 7777精品伊人久久久大香线蕉经典版下载 | 成人在线综合网| 欧美激情中文字幕| 久久成人麻豆午夜电影| 日韩在线观看一区二区| 欧美美女一区二区在线观看| 狠狠色狠狠色综合系列| 日韩一区二区免费电影| 一区二区三区精品视频在线| 成人黄色免费短视频| 久久精品视频在线免费观看| 蜜桃av一区二区三区电影| 欧美一区永久视频免费观看| 日韩中文字幕亚洲一区二区va在线 | 1区2区3区国产精品| 成人久久视频在线观看| 国产日韩成人精品| 中文乱码免费一区二区| 国产乱码精品一区二区三| 这里是久久伊人| 午夜视频久久久久久| 欧美性生活影院| 亚洲一区在线视频观看| 亚洲另类一区二区| 欧美在线视频全部完| 天天影视网天天综合色在线播放| 欧美日韩三级一区二区| 视频一区视频二区中文字幕| 91麻豆文化传媒在线观看| 国产欧美日韩精品一区| 99视频在线精品| 青青草精品视频| 国产精品久久久久精k8| 日本乱码高清不卡字幕| 久久99国产乱子伦精品免费| 欧美日韩国产大片| 精品中文字幕一区二区 | 日韩高清中文字幕一区| 欧美变态口味重另类| 91美女在线视频| 人禽交欧美网站| 亚洲色图在线播放| 538prom精品视频线放| 成人av电影免费在线播放| 亚洲成人综合网站| 国产精品嫩草影院com| 欧美性欧美巨大黑白大战| 国产精品一线二线三线精华| 亚洲影院免费观看| 国产精品久久久久久久浪潮网站 | 99国产精品久久久久久久久久|