linux input设备驱动分析

linux input设备驱动分析

工作机制

输入设备工作机制: 输入动作–》产生中断–》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 <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>

    //输入按键设备
    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;
};
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值