Linux輸入子系統(tǒng)詳解

版權(quán)聲明:本文為衛(wèi)偉學(xué)習(xí)總結(jié)文章堂飞,轉(zhuǎn)載請注明出處!
導(dǎo)讀:Linux輸入子系統(tǒng)由驅(qū)動層稼跳、輸入子系統(tǒng)核心層、事件處理層三部分組成。一個輸入事件碧聪,如鼠標(biāo)移動、鍵盤按下等通過Driver->Inputcore->Event handler->userspace的順序到達(dá)用戶控件的應(yīng)用程序液茎。


驅(qū)動層
將底層的硬件輸入轉(zhuǎn)化為統(tǒng)一事件形式逞姿,向輸入核心層(Input Core)匯報辞嗡。
輸入子系統(tǒng)核心層

  • 為驅(qū)動層提供輸入設(shè)備注冊與操作接口,如:input_register_device;
  • 通知事件處理層對事件進(jìn)行處理; 在/Proc下產(chǎn)生相應(yīng)的設(shè)備信息滞造。

事件處理層
主要是和用戶空間交互(Linux在用戶空間將所有的設(shè)備都當(dāng)作文件處理续室,由于在一般的驅(qū)動程序中都有提供fops接口,以及在/dev下生成相應(yīng)的設(shè)備文件nod,這些操作在輸入子系統(tǒng)中由事件處理層完成)谒养。
設(shè)備描述
input_dev結(jié)構(gòu)是實現(xiàn)設(shè)備驅(qū)動核心工作:向系統(tǒng)報告按鍵挺狰、觸摸屏等輸入事件(event,通過input_event結(jié)構(gòu)描述),不再需要關(guān)心文件操作接口买窟。驅(qū)動報告事件經(jīng)過inputCore和Eventhandler到達(dá)用戶空間丰泊。
注冊輸入設(shè)備函數(shù):
int input_register_device(struct input_dev *dev);
注銷輸入設(shè)備函數(shù):
void input_unregister_device(struct input_dev *dev)始绍;
驅(qū)動實現(xiàn)--初始化(事件支持)set_bit()告訴input輸入子系統(tǒng)支持那些事件瞳购,那些按鍵。例如:set_bit(EV_KEY,button_dev.evbit) (其中button_dev是struct input_dev類型)亏推。
struct input_dev中有兩個成員為

  • evbit事件類型(包括EV_RST,EV_REL,EV_MSC,EV_KEY,EV_ABS,EV_REP等)学赛。
  • eybit按鍵類型(當(dāng)事件類型為EV_KEY時包括BTN_LEFT,BTN_0,BTN_1,BTN_MIDDLE等)。

驅(qū)動實現(xiàn)——報告事件用于報告EV_KEY,EV_REL,EV_ABS事件的函數(shù)分別為:

void input_report_key(struct input_dev *dev,unsigned int code,int value)
void input_report_rel(struct input_dev *dev,unsigned int code,int value)
void input_report_abs(struct input_dev *dev,unsigned int code,int value)

驅(qū)動實現(xiàn)——報告結(jié)束input_sync()同步用于告訴input core子系統(tǒng)報告結(jié)束吞杭,觸摸屏設(shè)備驅(qū)動中盏浇,一次點擊的整個報告過程如下:

input_reprot_abs(input_dev,ABS_X,x); //x坐標(biāo)
input_reprot_abs(input_dev,ABS_Y,y); // y坐標(biāo)
input_reprot_abs(input_dev,ABS_PRESSURE,1);
input_sync(input_dev);//同步結(jié)束

input.c文件分析

drivers/input/input.c:
input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //入口函數(shù)

static const struct file_operations input_fops = {
   .owner = THIS_MODULE,
   .open = input_open_file,
};

怎么讀按鍵?

input_open_file
struct input_handler *handler = input_table[iminor(inode) >> 5];
new_fops = fops_get(handler->fops)  //  =>&evdev_fops
file->f_op = new_fops;
err = new_fops->open(inode, file);

 app: read > ... > file->f_op->read  

input_table數(shù)組由誰構(gòu)造篇亭?
input_register_handler
注冊input_handler:

input_register_handler
// 放入數(shù)組
input_table[handler->minor >> 5] = handler;

// 放入鏈表
list_add_tail(&handler->node, &input_handler_list);

// 對于每個input_dev缠捌,調(diào)用input_attach_handler
list_for_each_entry(dev, &input_dev_list, node)
    input_attach_handler(dev, handler); // 根據(jù)input_handler的id_table判斷能否支持這個input_dev

注冊輸入設(shè)備:

input_register_device
// 放入鏈表
list_add_tail(&dev->node, &input_dev_list);

// 對于每一個input_handler,都調(diào)用input_attach_handler
list_for_each_entry(handler, &input_handler_list, node)
    input_attach_handler(dev, handler); // 根據(jù)input_handler的id_table判斷能否支持這個input_dev

input_attach_handler
id = input_match_device(handler->id_table, dev);
error = handler->connect(handler, dev, id);
注冊input_dev或input_handler時译蒂,會兩兩比較左邊的input_dev和右邊的input_handler,
根據(jù)input_handler的id_table判斷這個input_handler能否支持這個input_dev曼月,
如果能支持,則調(diào)用input_handler的connect函數(shù)建立"連接"柔昼。

怎么建立連接哑芹?

  • 分配一個input_handle結(jié)構(gòu)體;
  • input_handle.dev = input_dev; // 指向左邊的input_dev
    input_handle.handler = input_handler; // 指向右邊的input_handler
  • 注冊: input_handler->h_list = &input_handle;
    inpu_dev->h_list = &input_handle;
evdev_connect
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一個input_handle

// 設(shè)置
evdev->handle.dev = dev;  // 指向左邊的input_dev
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;  // 指向右邊的input_handler
evdev->handle.private = evdev;

// 注冊
error = input_register_handle(&evdev->handle);

怎么讀按鍵捕透?

evdev_read
        // 無數(shù)據(jù)并且是非阻塞方式打開聪姿,則立刻返回
        if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
            return -EAGAIN;
        
        // 否則休眠 
        retval = wait_event_interruptible(evdev->wait,
            client->head != client->tail || !evdev->exist);

誰來喚醒?
evdev_event
wake_up_interruptible(&evdev->wait);
**
evdev_event被誰調(diào)用乙嘀?**
猜:應(yīng)該是硬件相關(guān)的代碼末购,input_dev那層調(diào)用的
在設(shè)備的中斷服務(wù)程序里,確定事件是什么虎谢,然后調(diào)用相應(yīng)的input_handler的event處理函數(shù)

gpio_keys_isr
// 上報事件
input_event(input, type, button->code, !!state);
input_sync(input);

input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
struct input_handle *handle;

list_for_each_entry(handle, &dev->h_list, d_node)
    if (handle->open)
        handle->handler->event(handle, type, code, value);

怎么寫符合輸入子系統(tǒng)框架的驅(qū)動程序盟榴?

  • 向內(nèi)核申請input_dev結(jié)構(gòu)體;
  • 設(shè)置input_dev的成員婴噩;
  • 注冊input_dev 驅(qū)動設(shè)備擎场;
  • 初始化定時器和中斷羽德;
  • 寫中斷服務(wù)函數(shù);
  • 寫定時器超時函數(shù)迅办;
  • 在出口函數(shù)中 釋放中斷函數(shù),刪除定時器,卸載釋放驅(qū)動宅静。

具體代碼如下(都加了注釋):

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/gpio_keys.h>
#include <asm/gpio.h>


struct input_dev *buttons_dev;            //  定義一個input_dev結(jié)構(gòu)體  
static struct ping_desc *buttons_id;          //保存dev_id,在定時器中用
static struct timer_list buttons_timer;    //定時器結(jié)構(gòu)體  

struct  ping_desc{

   unsigned  char  *name;          //中斷設(shè)備名稱
   int            pin_irq;          //按鍵的外部中斷標(biāo)志位
   unsigned  int    pin;                //引腳
   unsigned int  irq_ctl;           //觸發(fā)中斷狀態(tài):   IRQ_TYPE_EDGE_BOTH
   unsigned  int    button;         //dev_id,對應(yīng)鍵盤的 L ,  S,  空格,  enter      
};

    // KEY1 -> L
    // KEY2 -> S
    // KEY3 -> 空格
    // KEY4 -> enter
static  struct ping_desc   buttons_desc[5]=
{
   {"s1", IRQ_EINT0,   S3C2410_GPF0,  IRQ_TYPE_EDGE_BOTH,KEY_L},
   {"s2", IRQ_EINT2,   S3C2410_GPF2,  IRQ_TYPE_EDGE_BOTH,KEY_S},
   {"s3", IRQ_EINT11, S3C2410_GPG3 , IRQ_TYPE_EDGE_BOTH,KEY_SPACE},
   {"s4", IRQ_EINT19, S3C2410_GPG11,IRQ_TYPE_EDGE_BOTH,KEY_ENTER},
};



/*5. 寫中斷服務(wù)函數(shù)*/
static irqreturn_t  buttons_irq (int irq, void *dev_id)       //中斷服務(wù)函數(shù)
{
   buttons_id=(struct ping_desc *)dev_id;             //保存當(dāng)前的dev_id
   mod_timer(&buttons_timer, jiffies+HZ/100 );   //更新定時器值 10ms 
   return 0;
}



 /*6.寫定時器超時函數(shù)*/
void buttons_timer_function(unsigned long i)
{
   int val;
   val=s3c2410_gpio_getpin(buttons_id->pin);             //獲取是什么電平 
  if(val)         //高電平,松開
   {
     /*上報事件*/
     input_event(buttons_dev,EV_KEY,buttons_id->button, 0);  //上報EV_KEY類型,button按鍵,0(沒按下)
     input_sync(buttons_dev);         // 上傳同步事件,告訴系統(tǒng)有事件出現(xiàn)                       
  }

 else      //低電平,按下
  {
     /*上報事件*/
     input_event(buttons_dev, EV_KEY, buttons_id->button, 1);  //上報EV_KEY類型,button按鍵,1(按下)
     input_sync(buttons_dev);       // 上傳同步事件,告訴系統(tǒng)有事件出現(xiàn)
 }
}


static int buttons_init(void)   //入口函數(shù)
{
   int i;      
   buttons_dev=input_allocate_device();  //1.向內(nèi)核 申請input_dev結(jié)構(gòu)體
   /*2.設(shè)置input_dev ,  */
   set_bit(EV_KEY,buttons_dev->evbit);       //支持鍵盤事件
   set_bit(EV_REP,buttons_dev->evbit);       //支持鍵盤重復(fù)按事件

   set_bit(KEY_L,buttons_dev->keybit);                  //支持按鍵 L
   set_bit(KEY_S,buttons_dev->keybit);                //支持按鍵 S
   set_bit(KEY_SPACE,buttons_dev->keybit);      //支持按鍵 空格
   set_bit(KEY_ENTER,buttons_dev->keybit);     //支持按鍵 enter

   /*3.注冊input_dev */
   input_register_device(buttons_dev);

  
   /*4. 初始化硬件:初始化定時器和中斷*/      
   // KEY1 -> L
   // KEY2 -> S
   // KEY3 -> 空格
   // KEY4 -> enter
   init_timer(&buttons_timer);
   buttons_timer.function=buttons_timer_function;
   add_timer(&buttons_timer);

   for(i=0;i<4;i++)
   request_irq(buttons_desc[i].pin_irq, buttons_irq, buttons_desc[i].irq_ctl, buttons_desc[i].name, &buttons_desc[i]);

   return 0;
}


static int buttons_exit(void)  //出口函數(shù)
{
   /*7.釋放中斷函數(shù),刪除定時器,卸載釋放驅(qū)動*/
   int i;
   for(i=0;i<4;i++)
          free_irq(buttons_desc[i].pin_irq,&buttons_desc[i]);    //釋放中斷函數(shù)

   del_timer(&buttons_timer);   //刪除定時器

   input_unregister_device(buttons_dev);     //卸載類下的驅(qū)動設(shè)備
   input_free_device(buttons_dev);                //釋放驅(qū)動結(jié)構(gòu)體
   return 0; 
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL v2");

測試運行
掛載鍵盤驅(qū)動后, 如下圖,可以通過 ls -l /dev/event* 命令查看已掛載的設(shè)備節(jié)點:

輸入子系統(tǒng)的主設(shè)備號為13,其中event驅(qū)動本身的此設(shè)備號是從64開始的,如上圖,內(nèi)核啟動時,會加載自帶觸摸屏驅(qū)動,所以我們的鍵盤驅(qū)動的次設(shè)備號=64+1

測試運行有兩種,一種是直接打開/dev/tyy1,第二種是使用exec命令

  • cat /dev/tty1 //tty1:LCD終端,就會通過tty_io.c來訪問鍵盤驅(qū)動,然后打印在tty1終端上
  • exec 0</dev/tty1 //將/dev/tty1掛載到-sh進(jìn)程描述符0下,此時的鍵盤驅(qū)動就會直接打印在tty1終端上
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末站欺,一起剝皮案震驚了整個濱河市姨夹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌镊绪,老刑警劉巖匀伏,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蝴韭,居然都是意外死亡够颠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門榄鉴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來履磨,“玉大人,你說我怎么就攤上這事庆尘√曜纾” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵驶忌,是天一觀的道長矛辕。 經(jīng)常有香客問我,道長付魔,這世上最難降的妖魔是什么聊品? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮几苍,結(jié)果婚禮上翻屈,老公的妹妹穿的比我還像新娘。我一直安慰自己妻坝,他們只是感情好伸眶,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刽宪,像睡著了一般厘贼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上圣拄,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天涂臣,我揣著相機(jī)與錄音,去河邊找鬼售担。 笑死赁遗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的族铆。 我是一名探鬼主播飞袋,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼葱椭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起轴踱,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啄枕,沒想到半個月后膀值,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡栅葡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年茉兰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欣簇。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡规脸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出熊咽,到底是詐尸還是另有隱情莫鸭,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布横殴,位于F島的核電站被因,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衫仑。R本人自食惡果不足惜梨与,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惑畴。 院中可真熱鬧蛋欣,春花似錦、人聲如沸如贷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杠袱。三九已至尚猿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間楣富,已是汗流浹背凿掂。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庄萎。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓踪少,卻偏偏與公主長得像,于是被迫代替她去往敵國和親糠涛。 傳聞我的和親對象是個殘疾皇子援奢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 輸入子系統(tǒng)概述 Linux內(nèi)核為了能夠處理各種不同類型的輸入設(shè)備,比如 觸摸屏 忍捡,鼠標(biāo) , 鍵盤 , 操縱桿...
    JalynFang閱讀 10,357評論 4 9
  • 一集漾、input子系統(tǒng)介紹 1.1 系統(tǒng)介紹 本文是基于linux-2.6.32內(nèi)核進(jìn)行分析的,如果使用的是其他版本...
    konishi5202閱讀 1,090評論 3 6
  • 驅(qū)動程序 1. 申請設(shè)備砸脊,設(shè)置input設(shè)備支持的事件類型(比如EV_ABS,EV_KEY) input_allo...
    gbmaotai閱讀 844評論 0 0
  • 輸入子系統(tǒng): 輸入設(shè)備是嵌入式系統(tǒng)必不可少的組成部分具篇,輸入設(shè)備的類型和物理形態(tài)多種多樣,如鼠標(biāo)凌埂,鍵盤驱显,觸摸屏等等,...
    ibo閱讀 549評論 0 1
  • 今天感恩節(jié)哎侨舆,感謝一直在我身邊的親朋好友秒紧。感恩相遇!感恩不離不棄挨下。 中午開了第一次的黨會熔恢,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,569評論 0 11