linux驅動開發:input子系統二

輸入子系統:linux內核中將所有輸入設備歸爲一個類:input子系統。裏面實現了幾乎所有輸入設備的公共操作接口,相當於內核給我們搭建了一個輸入子系統的框架,我們用它的框架開發驅動,會變得非常簡單。因爲一些公共的函數內核已經幫我們實現好了,我們只需要實現不同輸入設備間的差異部分即可。

上篇文章 記錄了在輸入子系統的框架下實現一個按鍵的上報功能。一個輸入設備,上報一種按鍵事件。

那麼一個輸入設備,也是可以上報多種按鍵事件的。

因爲上一章的框架已經搭建好,我們只需要註冊其他的按鍵中斷並上報按鍵事件即可。所以整個代碼就變得非常簡單。

當然,如果作爲模塊:在模塊卸載時,需要逆序消除對內核的影響。簡單的講: 在你的init函數中 register一個設備,那麼在你的exit函數中就應該 unregister這個設備.

那麼直接上代碼:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <linux/input.h>
#include <asm/io.h>
#include <linux/kernel.h>
#include <mach/gpio.h>
#include <linux/irq.h>
#include <asm/bitops.h>



#define _HIGH               1
#define _LOW                0

#define _ZERO               0
enum button_event 
{
    _EV_UP,
    _EV_DOWN,
};

struct buttons_desc
{
    int gpio;       //PHY button Addr
    int keycode;        // button keycode
    char *name;         // button name
};

static struct buttons_desc buttons_array[] =
{
    {S5PV210_GPH2(0),KEY_LEFT,"LEFT"},
    {S5PV210_GPH2(1),KEY_RIGHT,"RIGHT"},
    {S5PV210_GPH2(2),KEY_UP,"UP"},
    {S5PV210_GPH2(3),KEY_DOWN,"DOWN"},

    {S5PV210_GPH3(0),KEY_MENU,"MENU"},
    {S5PV210_GPH3(1),KEY_ENTER,"ENTER"},
    {S5PV210_GPH3(2),KEY_BACKSPACE,"BACKSPACE"},
    {S5PV210_GPH3(3),KEY_DELETE,"DELETE"},
};
/*****irq use****/
volatile struct buttons_desc *g_buttons_desc_info = NULL;
/*****input device use****/
static struct input_dev *buttons_input_dev =NULL;

/*****timer use****/
static struct timer_list buttons_timer;

/******func start******/



/******button irq******/

static irqreturn_t smart210_buttons_irq(int irq, void* dev_id)
{
    //get whitch key trigger the irq
    g_buttons_desc_info =(volatile struct buttons_desc*)dev_id;

    //Eliminate key jitter,delay 10 ms to get key value
    mod_timer(&buttons_timer,jiffies + HZ/100);

    return IRQ_HANDLED;
}
/******timer del func******/
static void buttons_timer_func(unsigned long data)
{
    int uc_keysta =0;

    struct buttons_desc *uc_buttons_desc =(struct buttons_desc *)g_buttons_desc_info;

    if(!uc_buttons_desc)
    {
        return;
    }

    //get the pin level :high /low
    //pree up 0,pree down 1
    if(gpio_get_value(uc_buttons_desc->gpio) == _HIGH)
    {
        uc_keysta =_EV_UP;
    }
    else
    {
        uc_keysta =_EV_DOWN;
    }

    input_event(buttons_input_dev,EV_KEY, uc_buttons_desc->keycode,uc_keysta);
    input_event(buttons_input_dev,EV_SYN, _ZERO,_ZERO);


}
static int __init smart210_buttons_init(void)
{
    int i;
    int ret=0;

    //1.alloc input dev device
    buttons_input_dev = input_allocate_device();
    if (!buttons_input_dev) 
    {
        ret = -ENOMEM;
        printk("1.error allocate input device!\n");

        goto err_allocate_input_dev;
    }
    //2.set up the input dev param,Report event
    //EV_KEY :key event
    //EV_SYN: sync event
    set_bit(EV_KEY,buttons_input_dev->evbit);
    set_bit(EV_SYN,buttons_input_dev->evbit);
    set_bit(EV_REP,buttons_input_dev->evbit);
    //set EV_KEY key code
    for(i =0; i < ARRAY_SIZE(buttons_array);i++)
    {
        set_bit(buttons_array[i].keycode,buttons_input_dev->keybit);
    }
    //set input dev name
    buttons_input_dev->name = "buttons";

    //3.register device
    ret = input_register_device(buttons_input_dev);

    if(ret) 
    {
        printk("2.error to register input device!\n");

        goto err_register_input_dev;
    }


/******special func user add for different input device*********/

    //4.irq request
    for(i =0; i < ARRAY_SIZE(buttons_array);i++)
    {

        ret =request_irq(gpio_to_irq(buttons_array[i].gpio),smart210_buttons_irq,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                         buttons_array[i].name,&buttons_array[i]);
        if(ret)
        {
            break;
        }
    }

    if(ret) 
    {
        i--;
        for(; i >0;i--)
        {
            if(!buttons_array[i].gpio)
            {
                continue;
            }
            disable_irq(gpio_to_irq(buttons_array[i].gpio));
            free_irq(gpio_to_irq(buttons_array[i].gpio),&buttons_array[i]);

        }

        printk("3.error to request irq !\n");
        goto err_request_irq;
    }

    //5.timer initial
    init_timer(&buttons_timer);
    buttons_timer.function =buttons_timer_func;
    add_timer(&buttons_timer);
    printk("4.success initial input device!\n");
    return 0;

err_request_irq:
    input_unregister_device(buttons_input_dev);
err_register_input_dev:
err_allocate_input_dev:
    input_free_device(buttons_input_dev);
    return ret;
}

static void __exit smart210_buttons_exit(void)
{
    int i;
    del_timer(&buttons_timer);

    for(i=0;i< ARRAY_SIZE(buttons_array);i++)
    {
        disable_irq(gpio_to_irq(buttons_array[i].gpio));
        free_irq(gpio_to_irq(buttons_array[i].gpio),&buttons_array[i]);
    }

    input_unregister_device(buttons_input_dev);

    input_free_device(buttons_input_dev);

    printk("5.success unload input device!\n");
}

module_init(smart210_buttons_init);
module_exit(smart210_buttons_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("User");

實驗現象:
input設備註冊成功後,cat/proc/interrupts下面會有你的中斷號:
這裏寫圖片描述
第一列:中斷號
第二列:響應的次數,沒有響應中斷就是0
第三列:中斷類型
第四列:中斷的name

同樣,dev/input/目錄下面會多一個event*設備文件,主設備號13,次設備號自動分配.
我們可以操作這個設備文件:
這裏寫圖片描述
以16進制的格式顯示event1中的數據。裏面的數據含義如下:
這裏寫圖片描述

同時,在 proc/bus/input/下面會有devices和 handlers兩個文件

devices:
這裏寫圖片描述
這裏面兩個輸入設備,一個是TS的,一個是按鍵的.
handlers:
這裏寫圖片描述
這裏面有4個handlers,其中evdev是我們按鍵的,mousedev是鼠標的.其他的不清楚

整個input設備註冊的流程:


input device register procedure:

1.alloc input dev device

/*****input device use****/
static struct input_dev *buttons_input_dev =NULL;

buttons_input_dev =input_allocate_device();

2. fill in/ initial the  struct buttons_input_dev

    //set up the input dev param,Report event
    //EV_KEY :key event
    //EV_SYN: sync event
    set_bit(EV_KEY,buttons_input_dev->evbit);
    set_bit(EV_SYN,buttons_input_dev->evbit);
    set_bit(EV_REP,buttons_input_dev->evbit);
    //set EV_KEY key code
    set_bit(buttons_array[0].keycode,buttons_input_dev->keybit);
    //set input dev name
    buttons_input_dev->name = "buttons";

3.register input device
    ret = input_register_device(buttons_input_dev);


/*****input device register end****/

4. Report event:

    input_event(buttons_input_dev,EV_KEY, uc_buttons_desc->keycode,uc_keysta);
    input_event(buttons_input_dev,EV_SYN, _ZERO,_ZERO);//EV_SYN must have ,its finish end flag


5. cancle /logout the device:

    input_unregister_device(buttons_input_dev);

6. free the input dev:

    input_free_device(buttons_input_dev);

all the above is the frame of input system.

標準的輸入子系統的註冊大概就是這樣的。

測試程序:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<linux/input.h>


struct input__event
{
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

int fd;

int main()
{
    struct input__event ev_key;
    fd = open("/dev/input/event1",O_RDWR);

    printf("fd =%d,\n",fd);
    while(1)
    {
        read(fd,&ev_key,sizeof(ev_key));
        if(ev_key.type == EV_KEY)
        {
            printf("time:%ld s,%ld us,type: %d,code:%d,vale:%d.\n",ev_key.time.tv_sec,ev_key.time.tv_usec,ev_key.type,ev_key.code,ev_key.value);
        }
    }


    return 0;
}

用戶空間調用open,read函數讀按鍵值出來:

這樣,按鍵的驅動和測試程序都已完成.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章