input驱动分析

本文深入探讨了Linux内核中的input驱动模型,通过一个简单的按钮中断处理例子,介绍了如何初始化、注册input设备以及中断处理。关键函数包括input_allocate_device、input_register_device、input_report_key和input_sync。input驱动涉及按键、触摸屏等多种输入设备,文章详细解析了设备初始化、注册过程以及事件处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                        

      input驱动在linux内核中经常会用到,比如按键类: power button,一般用gpio来做按键模拟,还有touch key等。触摸屏,键盘鼠标,gsensor,gameport等。本文结合一例子对input驱动模型做探讨。

 下面是linux文档中的例子。

#include <linux/input.h>

#include <linux/module.h>

#include <linux/init.h>

#include <asm/irq.h>

#include <asm/io.h>

static struct input_dev *button_dev;//将该button设备定义为input_dev类型

static irqreturn_t  button_interrupt(int irq, void *dummy)//中断处理函数

{

  input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);//向上层汇报键值//BTN_0,inb(BUTTON_PORT) & 1表示是通码还是断码

         input_sync(button_dev);//等待结构体数据都填好后,即数据同步后发送给上层。

         return IRQ_HANDLED;

}

static int __init button_init(void)//button设备的初始化

{

         int error;

    //中断申请,BUTTON_IRQ是中断源,button_interrupt是中断处理函数,0表示中//断类型,是上升沿,下降沿之类,在linux 2.6.35表示0是IRQF_TRIGGER_NONE            //类型;”button”是设备名字;NULL表示设备的相关数据为空

         if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {

                printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);

                return -EBUSY;

        }

    //分配设备内存

         button_dev = input_allocate_device();

         if (!button_dev) {

                   printk(KERN_ERR "button.c: Not enough memory\n");

                   error = -ENOMEM;

                   goto err_free_irq;

         }

    //属性设置

         button_dev->evbit[0] = BIT_MASK(EV_KEY);//表示该input设备是按键事件

         button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);//设置按键的键值

 

         error = input_register_device(button_dev);//注册input设备

         if (error) {

                   printk(KERN_ERR "button.c: Failed to register device\n");

                   goto err_free_dev;

         }

         return 0;

 err_free_dev:

         input_free_device(button_dev);// 注册失败,释放已注册的部分

 err_free_irq:

         free_irq(BUTTON_IRQ, button_interrupt);//释放中断

         return error;

}

 

static void __exit button_exit(void)

{

        input_unregister_device(button_dev);// 注销注册

         free_irq(BUTTON_IRQ, button_interrupt);

}

module_init(button_init);  // button设备初始化部分,生成模块。Linux驱动特有的结构 

module_exit(button_exit);//设备注销模块

 

上面是比较简单的注册 input 设备中断处理例子。

注意几个API函数:

1):

set_bit(EV_KEY, button_dev.evbit); set_bit(BTN_0, button_dev.keybit);

分别用来设置设备所产生的事件以及上报的按键值。Struct iput_dev 中有两个成员,一个是evbit.一个是keybit.分别用表示设备所支持的动作和按键类型。

2): input_register_device(&button_dev);

用来注册一个input device.

3): input_report_key()

用来上层上报一个按键动作

4): input_sync()

等待结构体数据都填好后,本次事件操作已完成,数据同步后发送给上层。

 

一.首先来分析input_allocate_device

struct input_dev *input_allocate_device(void)

{

         struct input_dev *dev;

    // 使用kzalloc函数分配内存

         dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);

         if (dev) {

                   dev->dev.type = &input_dev_type;

                   dev->dev.class = &input_class;

                   device_initialize(&dev->dev);

                   mutex_init(&dev->mutex);

                   spin_lock_init(&dev->event_lock);

                   INIT_LIST_HEAD(&dev->h_list);

                   INIT_LIST_HEAD(&dev->node);

 

                   __module_get(THIS_MODULE);

         }

         return dev;

}

         内存分配成功后,向其成员变量赋值,主要包括input_dev_type(包括设备属性group,设备uevent事件),input_class(设备名以及设备节点),以及设备初始化(kobject是在里初始化的,为创建设备文件系统做准备),还有自旋锁的初始化等。

 

二、注册函数input_register_device

   int input_register_device(struct input_dev *dev)

{

         static atomic_t input_no = ATOMIC_INIT(0);

         struct input_handler *handler;

         const char *path;

         int error;

 

         /* Every input device generates EV_SYN/SYN_REPORT events. */

    //所有设备都支持EV_SYN这种事件类型

         __set_bit(EV_SYN, dev->evbit);

 

         /* KEY_RESERVED is not supposed to be transmitted to userspace. */

         __clear_bit(KEY_RESERVED, dev->keybit);

 

         /* Make sure that bitmasks not mentioned in dev->evbit are clean. */

         input_cleanse_bitmasks(dev);

 

         /*

          * If delay and period are pre-set by the driver, then autorepeating

          * is handled by the driver itself and we don't do it in input.c.

          */

    //初始化计时器

         init_timer(&dev->timer);

   //如果没有设置重复按键REP_DELAY和REP_PERIOD的值,下面会赋默认值

         if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {

                   dev->timer.data = (long) dev;

                   dev->timer.function = input_repeat_key;

                   dev->rep[REP_DELAY] = 250;

                   dev->rep[REP_PERIOD] = 33;

         }

    //没有设置取键的扫描码和设置键的扫描码,赋默认值

         if (!dev->getkeycode)

                   dev->getkeycode = input_default_getkeycode;

 

         if (!dev->setkeycode)

                   dev->setkeycode = input_default_setkeycode;

 

         dev_set_name(&dev->dev, "input%ld",

                        (unsigned long) atomic_inc_return(&input_no) - 1);

    //添加到设备列表,总的设备增加一个

         error = device_add(&dev->dev);

         if (error)

                   return error;

    //得到设备文件路径

         path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);

         printk(KERN_INFO "input: %s as %s\n",

                   dev->name ? dev->name : "Unspecified device", path ? path : "N/A");

         kfree(path);

     //设置互斥锁,防止被外界的中断干扰

         error = mutex_lock_interruptible(&input_mutex);

         if (error) {

                   device_del(&dev->dev);

                   return error;

         }

   //把设备节点添加到input设备的链表的尾部

         list_add_tail(&dev->node, &input_dev_list);

   /*下面是device和handler的匹配。将input device 挂到input_dev_list 链表上.然后,对每一个挂在input_handler_list 的handler 调用input_attach_handler().在这里的情况有好比设备模型中的device 和driver 的匹配。所有的input device 都挂在input_dev_list 链上。所有的handle 都挂在input_handler_list 上。*/

         list_for_each_entry(handler, &input_handler_list, node)

                   input_attach_handler(dev, handler);

 

         input_wakeup_procfs_readers();

    //操作完毕,解除互斥锁

         mutex_unlock(&input_mutex);

 

         return 0;

}

 

接下来来看input_attach_handler:

 

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)

{

         const struct input_device_id *id;

         int error;

    //具体的匹配在input_match_device中完成,得到一个id

         id = input_match_device(handler, dev);

         if (!id)

                   return -ENODEV;

    //匹配成功,调用handler->connect

         error = handler->connect(handler, dev, id);

         if (error && error != -ENODEV)

                   printk(KERN_ERR

                            "input: failed to attach handler %s to device %s, "

                            "error: %d\n",

                            handler->name, kobject_name(&dev->dev.kobj), error);

 

         return error;

}

分析input_match_device:

    #define MATCH_BIT(bit, max) \

                   for (i = 0; i < BITS_TO_LONGS(max); i++) \

                            if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \

                                     break; \

                   if (i != BITS_TO_LONGS(max)) \

                            continue;

 

static const struct input_device_id *input_match_device(struct input_handler *handler,

                                                                 struct input_dev *dev)

{

         const struct input_device_id *id;

         int i;

 

         for (id = handler->id_table; id->flags || id->driver_info; id++) {

 

                   if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)

                            if (id->bustype != dev->id.bustype)

                                     continue;

 

                   if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)

                            if (id->vendor != dev->id.vendor)

                                     continue;

 

                   if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)

                            if (id->product != dev->id.product)

                                     continue;

 

                   if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)

                            if (id->version != dev->id.version)

                                     continue;

 

                   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))

                            return id;

         }

 

         return NULL;

}

        大概意思是,id是由handler->id_table赋值的,先通过id->flags依次匹配INPUT_DEVICE_ID_MATCH_BUS(总线类型),INPUT_DEVICE_ID_MATCH_VENDOR(设备厂商),INPUT_DEVICE_ID_MATCH_PRODUCT(设备),INPUT_DEVICE_ID_MATCH_VERSION(设备版本)。如果id->flags没有定义这些,则往下执行,通过MATCH_BIT宏依次匹配evbit,keybit这些位。

 

在这里,我们关注下handler和handle

三、注册handler   input_register_handler

    对input device而言,Handler是interface

    int input_register_handler(struct input_handler *handler)

{

         struct input_dev *dev;

         int retval;

 

         retval = mutex_lock_interruptible(&input_mutex);

         if (retval)

                   return retval;

    //初始化handler链表头

         INIT_LIST_HEAD(&handler->h_list);

 

         // handler->minor是设备的设备号,右移五位存放到input_table[]里

if (handler->fops != NULL) {

                   if (input_table[handler->minor >> 5]) {

                            retval = -EBUSY;

                            goto out;

                   }

                   input_table[handler->minor >> 5] = handler;

         }

    //在input_handler_lis尾部加入该handler节点

         list_add_tail(&handler->node, &input_handler_list);

    //对每一个挂在input_ dev _list 的device 调用input_attach_handler()

         list_for_each_entry(dev, &input_dev_list, node)

                   input_attach_handler(dev, handler);

 

         input_wakeup_procfs_readers();

 

 out:

         mutex_unlock(&input_mutex);

         return retval;

}
       这和input_register_device函数有点相似。

 

四、关于handle

    在代码里,handle类型是input_handle,它的数据结构如下:

    struct input_handle {

         void *private;

         int open;

         const char *name;//设备名

         struct input_dev *dev;//input_dev

         struct input_handler *handler;//handler

         struct list_head        d_node;// input设备节点链表结构

         struct list_head        h_node;// handler节点的链表结构

};

  通过上述结构体,它包含了input设备和handler。Handle的注册如下:

 //注意这段英文注释

/* This function is supposed to be called from handler's

 * connect() method.

 */

int input_register_handle(struct input_handle *handle)

{

         struct input_handler *handler = handle->handler;

         struct input_dev *dev = handle->dev;

         int error;

         /*

          * We take dev->mutex here to prevent race with

          * input_release_device().

          */

         error = mutex_lock_interruptible(&dev->mutex);

         if (error)

                   return error;

         /*

          * Filters go to the head of the list, normal handlers

          * to the tail.

          */

    //将handle挂到input设备的h_list上

         if (handler->filter)

                   list_add_rcu(&handle->d_node, &dev->h_list);

         else

                   list_add_tail_rcu(&handle->d_node, &dev->h_list);

         mutex_unlock(&dev->mutex);

    /*下面这段英文注释,结合上面的英文注释,会有点奇怪,handle的注册和/*handler的connect方法有什么联系?前面handler和input设备匹配成功会调用handler的connect方法,连接的意思。Handle也是连接handler和input设备。Handler的connect 方         法是回调函数,具体函数内容没有明白,再加上handle的注册和handler的connect方法有什么联系,哪位朋友知道,请告之。*/

         /*

          * Since we are supposed to be called from ->connect()

          * which is mutually exclusive with ->disconnect()

          * we can't be racing with input_unregister_handle()

          * and so separate lock is not needed here.

          */

    //也将handle挂到handler的h_list上

         list_add_tail_rcu(&handle->h_node, &handler->h_list);

            //如果handler有定义start方法,就调用它,注意传进来的参数是handle,也//就是启动handle,这样完成注册

         if (handler->start)

                   handler->start(handle);

         return 0;

}

      通过分析handle的注册函数,handle分别挂在input device和handler的链表上, 实现三者的关联。handler通过ID与input device匹配,关联由handle完成。

 

五、报告按键情况函数input_report_key

     static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)

{

         input_event(dev, EV_KEY, code, !!value);

}

   它调用input_event函数

void input_event(struct input_dev *dev,

                    unsigned int type, unsigned int code, int value)

{

         unsigned long flags;

   //首先判断是否支持该类事件

         if (is_event_supported(type, dev->evbit, EV_MAX)) {

                   spin_lock_irqsave(&dev->event_lock, flags);

       //利用input device输入来产生随机数

                   add_input_randomness(type, code, value);

        //对事件类型进行处理

                   input_handle_event(dev, type, code, value);

                   spin_unlock_irqrestore(&dev->event_lock, flags);

         }

}

 

Input_handle_event函数:

static void input_handle_event(struct input_dev *dev,

                                   unsigned int type, unsigned int code, int value)

{

         int disposition = INPUT_IGNORE_EVENT;

         switch (type) {

         case EV_SYN:

                   switch (code) {

                   case SYN_CONFIG:

                            disposition = INPUT_PASS_TO_ALL;

                            break;

                   case SYN_REPORT:

                            if (!dev->sync) {

                                     dev->sync = 1;

                                     disposition = INPUT_PASS_TO_HANDLERS;

                            }

                            break;

                   case SYN_MT_REPORT:

                            dev->sync = 0;

                            disposition = INPUT_PASS_TO_HANDLERS;

                            break;

                   }

                   break;

         //按键事件

case EV_KEY:

     //判断按键是否支持

                   if (is_event_supported(code, dev->keybit, KEY_MAX) &&

                       !!test_bit(code, dev->key) != value) {

 

                            if (value != 2) {

                                     __change_bit(code, dev->key);

                                     if (value)

                                               input_start_autorepeat(dev, code);//重复按键

                                     else

                                               input_stop_autorepeat(dev);

                            }

                            disposition = INPUT_PASS_TO_HANDLERS;

                   }

                   break;

         case EV_SW:

                   if (is_event_supported(code, dev->swbit, SW_MAX) &&

                       !!test_bit(code, dev->sw) != value) {

 

                            __change_bit(code, dev->sw);

                            disposition = INPUT_PASS_TO_HANDLERS;

                   }

                   break;

    //轨迹球类型

         case EV_ABS:

                   if (is_event_supported(code, dev->absbit, ABS_MAX)) {

 

                            if (test_bit(code, input_abs_bypass)) {

                                     disposition = INPUT_PASS_TO_HANDLERS;

                                     break;

                            }

                            value = input_defuzz_abs_event(value,

                                               dev->abs[code], dev->absfuzz[code]);

 

                            if (dev->abs[code] != value) {

                                     dev->abs[code] = value;

                                     disposition = INPUT_PASS_TO_HANDLERS;

                            }

                   }

                   break;

         case EV_REL:

                   if (is_event_supported(code, dev->relbit, REL_MAX) && value)

                            disposition = INPUT_PASS_TO_HANDLERS;

                   break;

         case EV_MSC:

                   if (is_event_supported(code, dev->mscbit, MSC_MAX))

                            disposition = INPUT_PASS_TO_ALL;

                   break;

         case EV_LED:

                   if (is_event_supported(code, dev->ledbit, LED_MAX) &&

                       !!test_bit(code, dev->led) != value) {

 

                            __change_bit(code, dev->led);

                            disposition = INPUT_PASS_TO_ALL;

                   }

                   break;

         case EV_SND:

                   if (is_event_supported(code, dev->sndbit, SND_MAX)) {

 

                            if (!!test_bit(code, dev->snd) != !!value)

                                     __change_bit(code, dev->snd);

                            disposition = INPUT_PASS_TO_ALL;

                   }

                   break;

         case EV_REP:

                   if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {

                            dev->rep[code] = value;

                            disposition = INPUT_PASS_TO_ALL;

                   }

                   break;

         case EV_FF:

                   if (value >= 0)

                            disposition = INPUT_PASS_TO_ALL;

                   break;

         case EV_PWR:

                   disposition = INPUT_PASS_TO_ALL;

                   break;

         }

         if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)

                   dev->sync = 0;

         if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)

                   dev->event(dev, type, code, value);

    //需不需要handler参与进来

         if (disposition & INPUT_PASS_TO_HANDLERS)

                   input_pass_event(dev, type, code, value);//处理事件

}

 

input_pass_event函数:

 

   static void input_pass_event(struct input_dev *dev,

                                 unsigned int type, unsigned int code, int value)

{

         struct input_handler *handler;

         struct input_handle *handle;

 

         rcu_read_lock();

         handle = rcu_dereference(dev->grab);

         if (handle)

                   handle->handler->event(handle, type, code, value);

         else {

                   bool filtered = false;

 

                   list_for_each_entry_rcu(handle, &dev->h_list, d_node) {

                            if (!handle->open)

                                     continue;

 

                            handler = handle->handler;

                            if (!handler->filter) {

                                     if (filtered)

                                               break;

                                     handler->event(handle, type, code, value);

 

                            } else if (handler->filter(handle, type, code, value))

                                     filtered = true;

                   }

         }

         rcu_read_unlock();

}

        函数功能:grab是input_handle类型指针,是input_dev*dev的成员变量,下面函数rcu_dereference意思是去取dev里的handle,如果指定了handle,则调用handle->handler->event函数处理事件;若input device没有指定handle,则遍历input dev的h_list上的handle,handle若打开,则将该打开的handle的成员handler重新赋给handler,随后调用这个handler的event方法。有事件产生时,input device的handle会被打开。处理事件交给与input device相对应的handle->handler->event函数处理。

       结合linux文档例子,分析input驱动的API函数,对这些函数做了进一步探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值