當前位置:首頁 > 芯聞號 > 充電吧
[導讀]部分內(nèi)容參考Linux學習之路,表示感謝. 輸入子系統(tǒng)一般將該類驅(qū)動劃分為3部分,事件處理層為純軟件的東西,設備層涉及底層硬件,它們通過核心層建立聯(lián)系,對外提供open write等接口。

部分內(nèi)容參考Linux學習之路,表示感謝.

輸入子系統(tǒng)一般將該類驅(qū)動劃分為3部分,事件處理層為純軟件的東西,設備層涉及底層硬件,它們通過核心層建立聯(lián)系,對外提供open write等接口。 一、核心層 input.c向外界提供接口

① 在 input_init 中注冊了字符設備驅(qū)動

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
static const struct file_operations input_fops = {
        .owner = THIS_MODULE,
        .open = input_open_file,
    };

② 在注冊的fops中,僅僅只有1個open函數(shù),下面我們來看這個open函數(shù)input_open_file

static int input_open_file(struct inode *inode, struct file *file)
{
 // 定義一個input_handler指針,根據(jù)次設備號,從 input_table 數(shù)組中取出對應的 handler
    struct input_handler *handler = input_table[iminor(inode) >> 5];
    const struct file_operations *old_fops, *new_fops = NULL;
// 將 handler 的fops賦值給file->f_op,并用調(diào)用新的open函數(shù)
        old_fops = file->f_op;
        file->f_op = fops_get(handler->fops);
        err = file->f_op->open(inode, file);
那么,必定有個地方創(chuàng)建了handler并對它進行一定的設置,并提供fops函數(shù),將它放入input_table。

就這樣,Input.c 實現(xiàn)了一個通用對外接口。

二、事件處理層,注冊input_handler ① 放入鏈表、數(shù)組(input_register_handler)

input.c/input_register_handler 函數(shù)中 創(chuàng)建了handler并對它進行一定的設置,提供fops函數(shù),將它放入input_table

int input_register_handler(struct input_handler *handler)
    {
        // 將 handler 放入 input_table
        input_table[handler->minor >> 5] = handler;

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

        // 取出 input_dev_list 鏈表中的每一個 dev 與 該 handler 進行 比對
        list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);
    }

以 Evdev.c 為例來更好的進行說明

static struct input_handler evdev_handler = {
        .event = evdev_event,
        .connect = evdev_connect,
        .disconnect = evdev_disconnect,
        .fops  = &evdev_fops,
        .minor = EVDEV_MINOR_BASE,
        .name  = "evdev",
        .id_table= evdev_ids,
    };
    static int __init evdev_init(void)
    {
        return input_register_handler(&evdev_handler);
    }
我們與核心層提供的接口對應一下,假設APP需要讀按鍵:

app: read > ... > file->f_op->read == handler->fops->read == evdev_handler->evdev_fops->evdev_read

 /* 讀函數(shù)中 如果沒有事件上報休眠,等待上報事件 喚醒休眠,將事件傳送到用戶空間 */
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
//如果無數(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);
 //將內(nèi)核空間數(shù)據(jù)拷貝到用戶空間,略
    return retval;
}
② 匹配 (input_attach_handler)
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
    {
        // 看 dev.id 是否存在于 handler->id_table 中
        id = input_match_device(handler->id_table, dev);
        if (!id)
        return -ENODEV;
        // 在的話,調(diào)用 handler->connect
        error = handler->connect(handler, dev, id);
    }
③ 建立連接

我們以 Evdev.c 為例,看一下connect函數(shù)

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
    const struct input_device_id *id)
    {
        // 不要關(guān)心 evdev ,只看 evdev->handle 即可,這里構(gòu)建了一個 handle ,注意不是handler
        // handle 就是個 中間件,可以理解成膠帶,它把 hander 與 dev 連在一起
        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

        // 第一次建立聯(lián)系,在 handle 中記錄 dev 與 handle 的信息,這樣通過handle就可以找到dev與handler
        // 即是 實現(xiàn) handle -> dev   handle -> hander 的聯(lián)系
        evdev->handle.dev = dev;
        evdev->handle.handler = handler;

        // 申請設備號,創(chuàng)建設備節(jié)點
        devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
        dev_set_name(&evdev->dev, "event%d", minor);
        // 在input 類下面創(chuàng)建設備,文件夾的名字是 evdev->name ->inputn ,設備名是 dev->cdev.dev.name -> eventn
        cdev = class_device_create(&input_class, &dev->cdev, devt,
        dev->cdev.dev, evdev->name);
        // 注冊 handle
        error = input_register_handle(&evdev->handle);
    }
④ 注冊handle,第二次建立聯(lián)系
int input_register_handle(struct input_handle *handle)
    {
        struct input_handler *handler = handle->handler;
        // 將handle 記錄在 dev->h_list 中
        list_add_tail(&handle->d_node, &handle->dev->h_list);
        // 將handle 記錄在 handler->h_list 中
        list_add_tail(&handle->h_node, &handler->h_list);
        // 至此,dev 與 hander 也可以找到handle了,dev <-> handle <-> handler 之間暢通無阻
    }
小結(jié): 事件處理層,構(gòu)建 handler , 通過 input_register_handler 進行注冊,注冊時 1、將 handler 放入 input_handler_list 鏈表 2、將 handler 放入 input_table 3、取出 input_dev_list鏈表中的每一個dev 調(diào)用 input_attach_handler 進行id匹配 4、如果匹配成功,則調(diào)用 handler->connect 第一次建立連接 5、創(chuàng)建 handle 來進行第二次建立連接,在 handle 中記錄 dev 與 handler 的信息,這樣通過handle就可以找到dev與handler 6、在dev hander 中記錄 handle的信息,實現(xiàn) dev <-> handle <-> handler 三、設備層,注冊input_dev
int input_register_device(struct input_dev *dev)
    {
        // 將 dev 放入 input_dev_list
        list_add_tail(&dev->node, &input_dev_list);
        // 匹配 handler ,參考 ①、②
        list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);
    }
建立設備層與設備處理層聯(lián)系作用

以上面Evdev.c讀按鍵為例:假設無按鍵按下進如休眠模式

wait_event_interruptible(evdev->wait,
        client->head != client->tail || !evdev->exist);

那么誰來喚醒休眠呢?

static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
    wake_up_interruptible(&evdev->wait);
}
這樣我們似乎明白了,在設備層,我們寫驅(qū)動的時候,比如按鍵按了一下,我們要上報event 到Handler層進行處理,然后提交給用戶程序。

例如:Gpio_keys.c 中斷處理函數(shù)中

static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
    {
        // 上報事件
        input_event(input, type, button->code, !!state);
        input_sync(input);
        return IRQ_HANDLED;
    }

回看input.c/input_event函數(shù)

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);
最終調(diào)用 handler->event(handle, type, code, value); 剛好與我們的例子對應:evdev_handler->evdev_event >> wake_up_interruptible(&evdev->wait); 四、寫一個基于Input子系統(tǒng)的設備驅(qū)動 事件處理層不用我們管了,- -是暫時能力有限管不了。寫寫設備層的程序就好了。 軟件設計流程: /* 1. 分配一個Input_dev結(jié)構(gòu)體 */ /* 2. 設置 支持哪一類事件,該類事件里的那些事件*/ /* 3.注冊 */ /* 4.硬件相關(guān)操作 */ 流程圖:

設置事件的類型:

/*事件類型:*/

struct input_dev {  

        void *private;                           //輸入設備私有指針,一般指向用于描述設備驅(qū)動層的設備結(jié)構(gòu)  

        const char *name;                        //提供給用戶的輸入設備的名稱  
        const char *phys;                        //提供給編程者的設備節(jié)點的名稱  
        const char *uniq;                        //指定唯一的ID號,就像MAC地址一樣  
        struct input_id id;                      //輸入設備標識ID,用于和事件處理層進行匹配  

        unsigned long evbit[NBITS(EV_MAX)];      //位圖,記錄設備支持的事件類型  
        /* 
         *  #define EV_SYN          0x00    //同步事件 
         *  #define EV_KEY          0x01    //按鍵事件 
         *  #define EV_REL          0x02    //相對坐標 
         *  #define EV_ABS          0x03    //絕對坐標 
         *  #define EV_MSC          0x04    //其它 
         *  #define EV_SW           0x05    //開關(guān)事件 
         *  #define EV_LED          0x11    //LED事件 
         *  #define EV_SND          0x12 
         *  #define EV_REP          0x14    //重復上報 
         *  #define EV_FF           0x15 
         *  #define EV_PWR          0x16 
         *  #define EV_FF_STATUS    0x17 
         *  #define EV_MAX          0x1f 
         */  
        unsigned long keybit[NBITS(KEY_MAX)];    //位圖,記錄設備支持的按鍵類型  
        unsigned long relbit[NBITS(REL_MAX)];    //位圖,記錄設備支持的相對坐標  
        unsigned long absbit[NBITS(ABS_MAX)];    //位圖,記錄設備支持的絕對坐標  
        unsigned long mscbit[NBITS(MSC_MAX)];    //位圖,記錄設備支持的其他功能  
        unsigned long ledbit[NBITS(LED_MAX)];    //位圖,記錄設備支持的指示燈  
        unsigned long sndbit[NBITS(SND_MAX)];    //位圖,記錄設備支持的聲音或警報  
        unsigned long ffbit[NBITS(FF_MAX)];      //位圖,記錄設備支持的作用力功能  
        unsigned long swbit[NBITS(SW_MAX)];      //位圖,記錄設備支持的開關(guān)功能  

        unsigned int keycodemax;                //設備支持的最大按鍵值個數(shù)  
        unsigned int keycodesize;               //每個按鍵的字節(jié)大小  
        void *keycode;                          //指向按鍵池,即指向按鍵值數(shù)組首地址  
        int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);        //修改按鍵值  
        int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);       //獲取按鍵值  

        struct ff_device *ff;                          

        unsigned int repeat_key;                //支持重復按鍵  
        struct timer_list timer;                //設置當有連擊時的延時定時器  

        int state;                  

        int sync;       //同步事件完成標識,為1說明事件同步完成  

        int abs[ABS_MAX + 1];                //記錄坐標的值  
        int rep[REP_MAX + 1];                //記錄重復按鍵的參數(shù)值  

        unsigned long key[NBITS(KEY_MAX)];   //位圖,按鍵的狀態(tài)  
        unsigned long led[NBITS(LED_MAX)];   //位圖,led的狀態(tài)  
        unsigned long snd[NBITS(SND_MAX)];   //位圖,聲音的狀態(tài)  
        unsigned long sw[NBITS(SW_MAX)];     //位圖,開關(guān)的狀態(tài)  

        int absmax[ABS_MAX + 1];             //位圖,記錄坐標的最大值  
        int absmin[ABS_MAX + 1];             //位圖,記錄坐標的最小值  
        int absfuzz[ABS_MAX + 1];            //位圖,記錄坐標的分辨率  
        int absflat[ABS_MAX + 1];            //位圖,記錄坐標的基準值  

        int (*open)(struct input_dev *dev);                         //輸入設備打開函數(shù)  
        void (*close)(struct input_dev *dev);                       //輸入設備關(guān)閉函數(shù)  
        int (*flush)(struct input_dev *dev, struct file *file);     //輸入設備斷開后刷新函數(shù)  
        int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);        //事件處理  

        struct input_handle *grab;              

        struct mutex mutex;                     //用于open、close函數(shù)的連續(xù)訪問互斥  
        unsigned int users;                  

        struct class_device cdev;               //輸入設備的類信息  
        union {                                 //設備結(jié)構(gòu)體  
                struct device *parent;  
        } dev;  

        struct list_head        h_list;         //handle鏈表  
        struct list_head        node;           //input_dev鏈表  
};  

驅(qū)動函數(shù):buttons_drv.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   
#include   


struct pin_desc {
    int irq;
    char *name;
    unsigned int pin;
    unsigned int key_val;
};

struct pin_desc pins_desc[3] = {
    {IRQ_EINT0, "s2",S3C2410_GPF0,KEY_L},
    {IRQ_EINT2, "s3",S3C2410_GPF2,KEY_S},
    {IRQ_EINT11,"s4",S3C2410_GPG3,KEY_ENTER},
};


static struct input_dev *buttons_dev; 
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    /* 設置定時器,發(fā)生中斷后10ms后再去讀電平值 */
    irq_pd = (struct pin_desc *)dev_id;
    mod_timer(&buttons_timer,jiffies+HZ/100);
    return IRQ_RETVAL(IRQ_HANDLED);
}

static void buttons_timer_function(unsigned long data)
{
    struct pin_desc *pindesc = irq_pd;
    unsigned int pinval;

    if(!pindesc)
        return;
    pinval =  s3c2410_gpio_getpin(pindesc->pin);
    if(pinval)
    {
        /* 松開 : 最后一個參數(shù): 0-松開,1-按下 */
        input_event(buttons_dev,EV_KEY,pindesc->key_val,0);
    }
    else
    {
        /* 按下 : 最后一個參數(shù): 0-松開,1-按下 */
        input_event(buttons_dev,EV_KEY,pindesc->key_val,1);
    }

}

static int buttons_init(void)
{
    int i;
    /* 1. 分配一個input_dev結(jié)構(gòu)體 */
    buttons_dev = input_allocate_device();

    /* 2. 設置 */
    /* 2.1 能產(chǎn)生哪類事件 */
    set_bit(EV_KEY,buttons_dev->evbit);
    set_bit(EV_REP, buttons_dev->evbit);//用于重復事件,按下按鍵不松開可一直輸入

    /* 2.2 能產(chǎn)生這類事件里的哪些操作:L,S,ENTER */
    set_bit(KEY_L,buttons_dev->keybit);
    set_bit(KEY_S,buttons_dev->keybit);
    set_bit(KEY_ENTER,buttons_dev->keybit);

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

    /* 4. 硬件相關(guān)的操作 */
    init_timer(&buttons_timer);
    buttons_timer.function = buttons_timer_function;
    add_timer(&buttons_timer);

    for(i = 0 ;i < 3;i++)
    {
        request_irq(pins_desc[i].irq, buttons_irq,IRQT_BOTHEDGE,pins_desc[i].name,&pins_desc[i]);
    }

    return 0;
}

static void buttons_exit(void)
{
    int i;
    for(i = 0;i < 3;i++)
    {
        free_irq(pins_desc[i].irq,&pins_desc[i]);
    }
    del_timer(&buttons_timer);
    input_unregister_device(buttons_dev);
    input_free_device(buttons_dev);
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
測試方法: ① 通過控制臺查看cat /dev/tty1

這里有個問題就是必須按下回車才會出現(xiàn)回顯,因為我們默認的輸出設備為串口,現(xiàn)在將他改為tty1,使用exec 0



② 通過hexdump /dev/event1 查看用戶得到的數(shù)據(jù)和驅(qū)動中的數(shù)據(jù)比對

硬件有數(shù)據(jù)產(chǎn)生時,調(diào)用input_event 上報時間(上報事件核心)
--》handle->handler->event(handle, type, code, value);//從輸入設備的h_list里面找出handle,從handle得到handler,調(diào)用它的event函數(shù)
--》evdev_event//記錄按鍵值-->發(fā)信號--》喚醒程序,因為之前read時沒數(shù)據(jù)會休眠;

那么我們這里:
open(/dev/event1)–》read > ... > –》evdev_handler->evdev_fops->evdev_read–》evdev_event_to_user –》讀到input_event -->

struct input_event {
struct timeval time;  時間
__u16 type;   類別(按鍵類、相對位移、絕對位移)
__u16 code;那個位置位置
__s32 value;
};//這個結(jié)構(gòu)體可以支持所有的輸入事件。


剛好一一對應測試成功。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉