Input子系统与TP驱动

Input子系统与TP驱动

from vine_farer

1、层级结构

jiang-pc:~/build_projects2/build/60_ali/kernel-3.18/drivers/input$ ls
apm-power.c  ff-core.c     goodix_finger   input-polldev.c  keyboard    Makefile         serio
cdfinger     ff-memless.c  input.c         jmt101           keycombo.c  matrix-keymap.c  sparse-keymap.c
elan         fingerprint   input-compat.c  joydev.c         keyreset.c  misc             sw9551_fp
evbug.c      focaltech_fp  input-compat.h  joystick         madev       mouse            tablet
evdev.c      gameport      input-mt.c      Kconfig          madev_080t  mousedev.c       touchscreen

这是Input子系统在内核驱动中的顶层目录(MTK平台

Input子系统分为三个层级:

事件处理层:evdev.c 、joydev.c、mousedev.c

核心层:input.c

设备驱动层:touchscreen、mouse、fingerprint 等

事件处理层:通过核心层的API获取输入事件上报的数据,定义API与应用层交互

核心层:为事件处理层和设备驱动层提供接口API

设备驱动层:采集输入设备的数据信息,通过核心层提供的API上报数据

层级框架视图:

image


2、基础数据结构

struct input_dev:会在具体设备驱动层中被填充

struct input_handle:会在事件处理层和设备驱动层注册设备时通过input_dev或input_handler间接调用

struct input_handler:会在事件处理层如evdev.c中被实例化

结构体定义在kernel-3.18/include/linux/input.h

三大结构体关系视图一:

image1

1)input_dev
struct input_dev {
    const char *name;/*导出到用户空间的相关信息,在sys文件可以看到*/
    const char *phys;
    const char *uniq;
    struct input_id id;/*与input_handler匹配用的id*/

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
    /*输入设备的事件支持位图*/
    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)];/*LED事件*/
    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 hint_events_per_packet;

    unsigned int keycodemax;
    unsigned int keycodesize;
    void *keycode;

    int (*setkeycode)(struct input_dev *dev,
              const struct input_keymap_entry *ke,
              unsigned int *old_keycode);
    int (*getkeycode)(struct input_dev *dev,
              struct input_keymap_entry *ke);

    struct ff_device *ff;

    unsigned int repeat_key;//最近一次的按键值
    struct timer_list timer;

    int rep[REP_CNT];

    struct input_mt *mt;

    struct input_absinfo *absinfo;

    unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反应设备当前的按键状态
    unsigned long led[BITS_TO_LONGS(LED_CNT)];//反应设备当前的led状态
    unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反应设备当前的声音输入状态
    unsigned long sw[BITS_TO_LONGS(SW_CNT)];//反应设备当前的开关状态

    int (*open)(struct input_dev *dev);//第一次打开设备时调用,初始化设备用
    void (*close)(struct input_dev *dev);//最后一个应用程序释放设备时用,关闭设备
    int (*flush)(struct input_dev *dev, struct file *file);/*用于处理传递给设备的事件,如LED事件和声音事件*/
    int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

    struct input_handle __rcu *grab;//当前占有该设备的input_handle

    spinlock_t event_lock;
    struct mutex mutex;

    unsigned int users;
    bool going_away;

    struct device dev;

    struct list_head    h_list;//该链表头用于链接此input_dev所关联的input_handle
    struct list_head    node;//用于将此input_dev链接到input_dev_list

    unsigned int num_vals;
    unsigned int max_vals;
    struct input_value *vals;

    bool devres_managed;
};
2)input_handler
struct input_handler {

    void *private;

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);/*event用于处理事件*/
    void (*events)(struct input_handle *handle,
               const struct input_value *vals, unsigned int count);
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);/*connect用于建立handler和device的联系*/
    void (*disconnect)(struct input_handle *handle);/*disconnect用于解除handler和device的联系*/
    void (*start)(struct input_handle *handle);

    bool legacy_minors;
    int minor;//次设备号
    const char *name;

    const struct input_device_id *id_table;//用于和input_dev匹配

    struct list_head    h_list;//用于链接和此input_handler相关的input_handle
    struct list_head    node;//用于将该input_handler链入input_handler_list
};
3)input_handle
struct input_handle {

    void *private;

    int open;//记录设备的打开次数(有多少个应用程序访问设备)
    const char *name;

    struct input_dev *dev;//指向所属的device
    struct input_handler *handler;//指向所属的handler

    struct list_head    d_node;//用于将此input_handle链入所属input_dev的h_list链表
    struct list_head    h_node;//用于将此input_handle链入所属input_handler的h_list链表
};
三大结构体关系视图二:

image2

浅析三大结构体关系
  • input_handle是连接input_deviceinput_handler的桥梁

  • input_device可以通过input_handle找到input_handler,同样的input_handler可以通过input_handle找到input_device

  • 一个device可能对应多个handler,而一个handler也不能只处理一个device,比如说一个鼠标,它可以对应evdev_handler,也可以对应mouse_handler,因此当其注册时与系统中的handler进行匹配,就有可能产生两个实例,一个是evdev,另一个是mousedev,而任何一个实例中都只有一个handle,至于以何种方式来传递事件,就由用户程序打开哪个实例来决定

  • 后面一个情况很容易理解,一个事件驱动不能只为一个甚至一种设备服务,系统中可能有多种设备都能使用这类handler,比如event handler就可以匹配所有的设备

  • input子系统中,有8种事件驱动,每种事件驱动最多可以对应32个设备,因此dev实例总数最多可以达到256

以TP驱动为例:

MTK平台的TP驱动是分为两个部分组合在一起的,全平台的共享驱动mtk_tpd.c(抽象),以及各个型号TP的独立驱动(真实)

mtk_tpd.c负责将TP注册到platform总线,以及利用input子系统核心层提供的API向事件处理层上报键值

各个型号的独立驱动负责I2C总线挂接,读取键值提交给mtk_tpd.c

我们在此处先单独分析三大结构体在驱动中的作用

1)input_dev

input_dev结构体被定义在tpd_device结构体中,tpd_device如下定义

~/kernel-3.18/drivers/input/touchscreen/mediatek/tpd.h

struct tpd_device {
    struct device *tpd_dev;
    struct regulator *reg;
    struct regulator *io_reg;
    struct input_dev *dev;
    struct input_dev *kpd;
    struct timer_list timer;
    struct tasklet_struct tasklet;
    int btn_state;
};

然后我们分析mtk_tpd.c

~/kernel-3.18/drivers/input/touchscreen/mediatek/mtk_tpd.c

struct tpd_device *tpd = 0;

在此处我们可看见代码中定义了tpd_device的结构体指针,后面对input_dev结构体的调用和填充,都是通过tpd->dev来实现的

我们之前提到过,mtk_tpd.c做的重要的一件事就是注册platform平台总线,对设备的申请、注册,一些事件的属性设置,以及对各型号TP的兼容遍历,都是在其probe函数中完成的

static struct platform_driver tpd_driver = {
    .remove = tpd_remove,
    .shutdown = NULL,
    .probe = tpd_probe,
    .driver = {
            .name = TPD_DEVICE,
            .pm = &tpd_pm_ops,
            .owner = THIS_MODULE,
            .of_match_table = touch_of_match,//设备树匹配
    },
}; 

再看具体的tpd_probe函数

/* touch panel probe */
static int tpd_probe(struct platform_device *pdev)
{
    int touch_type = 1; /* 0:R-touch, 1: Cap-touch */
    int i = 0;
#ifndef CONFIG_CUSTOM_LCM_X
#ifdef CONFIG_LCM_WIDTH
    unsigned long tpd_res_x = 0, tpd_res_y = 0;
    int ret = 0;
#endif
#endif

    TPD_DMESG("enter %s, %d\n", __func__, __LINE__);

    if (misc_register(&tpd_misc_device))
        pr_err("mtk_tpd: tpd_misc_device register failed\n");
    tpd_get_gpio_info(pdev);
    tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
    if (tpd == NULL)
    return -ENOMEM;
    memset(tpd, 0, sizeof(struct tpd_device));

    /* allocate input device */
    tpd->dev = input_allocate_device();
    if (tpd->dev == NULL) {
        kfree(tpd);
        return -ENOMEM;
    }
    ...

除了一些初始化和注册外,和我们的input_dev有关的函数是tpd->dev = input_allocate_device();,它主要为结构体申请内存空间,填充部分结构体信息和初始化一些锁、定时器、链表头,如下所示:

struct input_dev *input_allocate_device(void)
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_dev *dev;

    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_timer(&dev->timer);
        INIT_LIST_HEAD(&dev->h_list);
        INIT_LIST_HEAD(&dev->node);

        dev_set_name(&dev->dev, "input%lu",
                 (unsigned long) atomic_inc_return(&input_no) - 
  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值