歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> linux input設備驅動分析

linux input設備驅動分析

日期:2017/3/1 11:47:19   编辑:關於Linux

工作機制

輸入設備工作機制: 輸入動作–》產生中斷–》CPU通過總線或者IO讀取數據到緩沖區

構架層次

   app
//--------------------
   input_event_driver
//--------------------
   input_core
//--------------------
   input_device_driver
//--------------------
   hardware

數據結構

輸入設備對象
struct input_dev {
    const char *name;
    const char *phys;
    const char *uniq;
    struct input_id id;
    //表示能產生哪類事件
    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
    //能產生哪些案件事件
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
    //能產生哪些相對位移事件
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
    //能產生哪些絕對位移事件
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
    //其他事件
    unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
    unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

    unsigned int keycodemax;
    unsigned int keycodesize;
    void *keycode;
    int (*setkeycode)(struct input_dev *dev,
              unsigned int scancode, unsigned int keycode);
    int (*getkeycode)(struct input_dev *dev,
              unsigned int scancode, unsigned int *keycode);

    struct ff_device *ff;

    unsigned int repeat_key;
    struct timer_list timer;
    int sync;

    int abs[ABS_CNT];
    int rep[REP_MAX + 1];

    unsigned long key[BITS_TO_LONGS(KEY_CNT)];
    unsigned long led[BITS_TO_LONGS(LED_CNT)];
    unsigned long snd[BITS_TO_LONGS(SND_CNT)];
    unsigned long sw[BITS_TO_LONGS(SW_CNT)];

    int absmax[ABS_CNT];
    int absmin[ABS_CNT];
    int absfuzz[ABS_CNT];
    int absflat[ABS_CNT];
    int absres[ABS_CNT];

    int (*open)(struct input_dev *dev);
    void (*close)(struct input_dev *dev);
    int (*flush)(struct input_dev *dev, struct file *file);
    int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

    struct input_handle *grab;
    .......
    struct device dev;

    struct list_head    h_list;
    struct list_head    node;
};

描述一類事件驅動處理
struct input_handler {
    void *private;
    //事件處理函數指針。設備驅動報告的事件最終由這個函數來處理
    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    //連接handler和input_dev的函數指針
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);
    const struct file_operations *fops;
    int minor;
    const char *name;
    const struct input_device_id *id_table;
    struct list_head    h_list;
    struct list_head    node;
};
//links input device with an input handler
struct input_handle {
    void *private;
    int open;
    const char *name;
    struct input_dev *dev;
    struct input_handler *handler;
    struct list_head    d_node;
    struct list_head    h_node;
};
//上報數據的類型
struct input_event {
 struct timeval time; //記錄事件發生的時間戳
 __u16 type;   //事件類型
 __u16 code;   //事件代碼
 __s32 value;  //事件值,如坐標的偏移值,或者是抬起,1表示按下 0釋放
}

代碼分析

輸入設備號://#define INPUT_MAJOR     13
static int __init input_init(void)
    err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

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

static int input_open_file(struct inode *inode, struct file *file)
    struct input_handler *handler;
    handler = input_table[iminor(inode) >> 5]; //通過子設備號 獲取handler
    if (handler)
        new_fops = fops_get(handler->fops);//獲取對應handler的fops
    .....
    old_fops = file->f_op;
    file->f_op = new_fops;


那麼input_table是在哪兒注冊的?
int input_register_handler(struct input_handler *handler)
    if (handler->fops != NULL) {
        if (input_table[handler->minor >> 5]) {
            retval = -EBUSY;
            goto out;
        }
        input_table[handler->minor >> 5] = handler;
    }
    //後續分析
    list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);

誰調用了input_register_handler?
 Joydev.c (drivers\input):  return input_register_handler(&joydev_handler);
 Keyboard.c (drivers\char): error = input_register_handler(&kbd_handler);
 Mousedev.c (drivers\input):    error = input_register_handler(&mousedev_handler);
 Evdev.c (drivers\input):   return input_register_handler(&evdev_handler); 

以evdev.c舉例:
static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}

static struct input_handler evdev_handler = {
    .event      = evdev_event,
    .connect    = evdev_connect,
    .disconnect = evdev_disconnect,
    .fops       = &evdev_fops,
    .minor      = EVDEV_MINOR_BASE, //64
    .name       = "evdev",
    .id_table   = evdev_ids,
};

static const struct file_operations evdev_fops = {
    .owner      = THIS_MODULE,
    .read       = evdev_read,
    .write      = evdev_write,
    .poll       = evdev_poll,
    .open       = evdev_open,
    .release    = evdev_release,
    .unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = evdev_ioctl_compat,
#endif
    .fasync     = evdev_fasync,
    .flush      = evdev_flush
};

最終會打開evdev_open
static int evdev_open(struct inode *inode, struct file *file)
    struct evdev_client *client;
    evdev = evdev_table[i];
    client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); //分配緩沖區
    file->private_data = client;
總結: app open->...-> evdev_open

怎麼去讀緩沖區的數據?
read-->sys_read-->evdev_read
static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
    struct evdev_client *client = file->private_data;
    struct evdev *evdev = client->evdev;
    struct input_event event;
    //如果隊列為空
    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);
    //copy_to_usr
    while (retval + input_event_size() <= count &&
           evdev_fetch_next_event(client, &event)) {

        if (input_event_to_user(buffer + retval, &event))
            return -EFAULT;

        retval += input_event_size();
    }
什麼時候喚醒?
在解釋喚醒之前,先分析分析設備驅動與事件驅動的匹配
首先看一個簡單的設備驅動程序
ex:
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 

    //輸入按鍵設備
    static struct input_dev *input_button;
    //延時去抖動的timer_list
    static struct timer_list timer_button;
    //中斷引腳描述
    static struct pin_desc{
         int irq; //中斷號
         char * irqname;//中斷名稱
         unsigned int pin;//引腳
         unsigned int key_val;//事件值
    };

    static struct pin_desc * pin_desc_pd;
    static struct pin_desc pins_desc[4]={
         {IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L},
         {IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S},
         {IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER},
         {IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT}
    };
        //中斷處理函數
    irqreturn_t irq_input_button_handler (int irq, void *dev_id)
    {
         pin_desc_pd = ( struct pin_desc *)dev_id;
         mod_timer(&timer_button, jiffies+HZ/100);//10ms後啟動定時器
         return IRQ_RETVAL(IRQ_HANDLED);
    }

    //定時器到期處理函數
    void timer_button_function(unsigned long dat){
         struct pin_desc *pin_val = pin_desc_pd;
         if(pin_val == NULL)
          return;
         unsigned int val = s3c2410_gpio_getpin(pin_val->pin);
         if(val){
          input_event(input_button,EV_KEY,pin_val->key_val,0);
          input_sync(input_button);
         }
         else{
          input_event(input_button,EV_KEY,pin_val->key_val,1);
          input_sync(input_button);
         }
    }

    static int input_button_init(void){
         int i;

         //1.分配input結構體
         input_button = input_allocate_device();
         if (!input_button)
              return -ENOMEM;

         //2.設置事件類
         set_bit(EV_KEY,input_button->evbit);

         //3.設置事件類中的事件
         set_bit(KEY_L,input_button->keybit);
         set_bit(KEY_S,input_button->keybit);
         set_bit(KEY_ENTER,input_button->keybit);
         set_bit(KEY_LEFTSHIFT,input_button->keybit);

         //4.注冊input_dev
         input_register_device(input_button);

         //硬件相關的代碼中斷與定時器
         init_timer(&timer_button);
         timer_button.function = timer_button_function;
         add_timer(&timer_button);
         //注冊中斷
         for( i = 0; i < 4; i++){
             request_irq(pins_desc[i].irq, irq_input_button_handler,IRQT_BOTHEDGE,
                 pins_desc[i].irqname, &pins_desc[i]);
         }
             return 0;
    }

    static void input_button_exit(void){
         int i;
         for( i = 0; i < 4; i++){
          free_irq(pins_desc[i].irq, &pins_desc[i]);
         }
         del_timer(&timer_button);
         input_unregister_device(input_button);
         input_free_device(input_button);
    }
    module_init(input_button_init);
    module_exit(input_button_exit);
    MODULE_LICENSE("GPL");

輸入設備驅動就是完善一個input_dev對象並上報觸發事件
接口:
//allocate memory for new input device  
struct input_dev *input_allocate_device(void);
//注冊一個input_dev
int input_register_device(struct input_dev *dev)
分析一下這個接口:
int input_register_device(struct input_dev *dev)
    //遍歷input_handler_list的鏈表,與input_dev匹配
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);
            //對比結構體handler中input_device_id與結構體dev中bit數組,尋找的匹配的id
            id = input_match_device(handler, dev);
                MATCH_BIT(evbit,  EV_MAX);
                MATCH_BIT(keybit, KEY_MAX);
                MATCH_BIT(relbit, REL_MAX);
                MATCH_BIT(absbit, ABS_MAX);
                MATCH_BIT(mscbit, MSC_MAX);
                MATCH_BIT(ledbit, LED_MAX);
                MATCH_BIT(sndbit, SND_MAX);
                MATCH_BIT(ffbit,  FF_MAX);
                MATCH_BIT(swbit,  SW_MAX);

                if (!handler->match || handler->match(handler, dev))
            error = handler->connect(handler, dev, id); //handler->match,handler->connect在handler層

舉例evdev.c
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)  
    //device與handler匹配產生數據通道xxxevdev struct
    struct evdev *evdev;
    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    init_waitqueue_head(&evdev->wait);
    dev_set_name(&evdev->dev, "event%d", minor);
    evdev->minor = minor; 
    //初始化 handle
    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;
    // * This function puts a new input handle onto device's
    // * and handler's lists so that events can flow through
    // * it once it is opened using input_open_device().  
     error = input_register_handle(&evdev->handle);
     error = evdev_install_chrdev(evdev); 
         evdev_table[evdev->minor] = evdev; //將evdev放入到evdev_table當中 
     //添加設備
     error = device_add(&evdev->dev);

    總結  1,將構建的handler加入到input_handler_list中
          2,並匹配input_dev_list中的device,device的類型為struct input_dev
          3, 匹配的依據是各自對象中的id結構體,匹配成功後會執行handler中的connect函數
          4,對於input設備對象的驅動代碼中,需要設置evbit/keybit/absbit等數組中的位,用於進行比對

硬件數據如何上報?
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
    input_handle_event(dev, type, code, value);
        input_start_autorepeat(dev, code);
        input_pass_event(dev, type, code, value);
            handle->handler->event(handle, type, code, value);
所以此時又切換到evdev.c中的evdev_handler
static void evdev_event(struct input_handle *handle
          unsigned int type, unsigned int code, int value)
    //獲取通過handle中找到evdev
    struct evdev *evdev = handle->private;
    //封裝input_event
    event.type = type;
    event.code = code;
    event.value = value;
    //通過evdev->client_list找到一個evdev_client對象
    list_for_each_entry_rcu(client, &evdev->client_list, node)
    // 將input設備層傳遞過來的數據放入到client對象中
    evdev_pass_event(client, &event);
    //如果type為EV_SYN的話,那麼就將喚醒等待隊列
    if (type == EV_SYN && code == SYN_REPORT) //以此上報事件時一定要執行 EV_SYNC
        wake_up_interruptible(&evdev->wait);//喚醒evdev_wait,喚醒evdev_read

常用接口API

a.分配與釋放一個輸入設備:
     分配:
         struct input_dev *input_allocate_device(void);
     釋放:
         void input_free_device(struct input_dev *dev) //未注冊成功
         void input_unregister_device(struct input_dev *dev) //注冊成功
     注冊:
         int input_register_device(struct input_dev *dev);注冊子系統,也是我們的分析入口
     上報輸入事件接口:
        void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
               type:類事件
               code: 子事件
               value: 值
它的擴展:
 static inline void input_report_key(struct input_dev *dev, unsigned int code, int value);//上報按鍵事件
 static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);//報告相對坐標
 static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);//報告絕對坐標
 static inline void input_sync(struct input_dev *dev);//報告同步事件

將某個數據的位置1:(設置類事件,子事件)
     1.extern __inline__ int set_bit(int nr,long * addr)
     2.BIT_MASK(nr)
       BIT_WORD(nr)

設置輸入子事件的范圍(觸摸屏等):
     static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
例如電阻觸摸屏
     input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);
     input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
     input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);

使用hexdump調試

hexdump /dev/event1  (open(/dev/event1), read(), )
//           秒      微秒    類   code   value
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000

//對應結構體
struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};
Copyright © Linux教程網 All Rights Reserved