日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

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

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

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

① 在 input_init 中注冊了字符設(shè)備驅(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ù)次設(shè)備號,從 input_table 數(shù)組中取出對應(yīng)的 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并對它進行一定的設(shè)置,并提供fops函數(shù),將它放入input_table。

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

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

input.c/input_register_handler 函數(shù)中 創(chuàng)建了handler并對它進行一定的設(shè)置,提供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);
    }
我們與核心層提供的接口對應(yīng)一下,假設(shè)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;

        // 申請設(shè)備號,創(chuàng)建設(shè)備節(jié)點
        devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
        dev_set_name(&evdev->dev, "event%d", minor);
        // 在input 類下面創(chuàng)建設(shè)備,文件夾的名字是 evdev->name ->inputn ,設(shè)備名是 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 三、設(shè)備層,注冊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);
    }
建立設(shè)備層與設(shè)備處理層聯(lián)系作用

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

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

設(shè)置事件的類型:

/*事件類型:*/

struct input_dev {  

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

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

        unsigned long evbit[NBITS(EV_MAX)];      //位圖,記錄設(shè)備支持的事件類型  
        /* 
         *  #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    //重復(fù)上報 
         *  #define EV_FF           0x15 
         *  #define EV_PWR          0x16 
         *  #define EV_FF_STATUS    0x17 
         *  #define EV_MAX          0x1f 
         */  
        unsigned long keybit[NBITS(KEY_MAX)];    //位圖,記錄設(shè)備支持的按鍵類型  
        unsigned long relbit[NBITS(REL_MAX)];    //位圖,記錄設(shè)備支持的相對坐標  
        unsigned long absbit[NBITS(ABS_MAX)];    //位圖,記錄設(shè)備支持的絕對坐標  
        unsigned long mscbit[NBITS(MSC_MAX)];    //位圖,記錄設(shè)備支持的其他功能  
        unsigned long ledbit[NBITS(LED_MAX)];    //位圖,記錄設(shè)備支持的指示燈  
        unsigned long sndbit[NBITS(SND_MAX)];    //位圖,記錄設(shè)備支持的聲音或警報  
        unsigned long ffbit[NBITS(FF_MAX)];      //位圖,記錄設(shè)備支持的作用力功能  
        unsigned long swbit[NBITS(SW_MAX)];      //位圖,記錄設(shè)備支持的開關(guān)功能  

        unsigned int keycodemax;                //設(shè)備支持的最大按鍵值個數(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;                //支持重復(fù)按鍵  
        struct timer_list timer;                //設(shè)置當有連擊時的延時定時器  

        int state;                  

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

        int abs[ABS_MAX + 1];                //記錄坐標的值  
        int rep[REP_MAX + 1];                //記錄重復(fù)按鍵的參數(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è)備打開函數(shù)  
        void (*close)(struct input_dev *dev);                       //輸入設(shè)備關(guān)閉函數(shù)  
        int (*flush)(struct input_dev *dev, struct file *file);     //輸入設(shè)備斷開后刷新函數(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;               //輸入設(shè)備的類信息  
        union {                                 //設(shè)備結(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)
{
    /* 設(shè)置定時器,發(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. 設(shè)置 */
    /* 2.1 能產(chǎn)生哪類事件 */
    set_bit(EV_KEY,buttons_dev->evbit);
    set_bit(EV_REP, buttons_dev->evbit);//用于重復(fù)事件,按下按鍵不松開可一直輸入

    /* 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)回顯,因為我們默認的輸出設(shè)備為串口,現(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);//從輸入設(shè)備的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)體可以支持所有的輸入事件。


剛好一一對應(yīng)測試成功。

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

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉