input子系统与tp驱动

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
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这是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;
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
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
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
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链表
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
三大结构体关系视图二:

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;
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

然后我们分析mtk_tpd.c

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

struct tpd_device *tpd = 0;
 
 
  • 1
  • 2
  • 3

在此处我们可看见代码中定义了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,//设备树匹配
    },
}; 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

再看具体的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;
    }
    ...
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

除了一些初始化和注册外,和我们的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) - 1);

        __module_get(THIS_MODULE);
    }

    return dev;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

tpd_probe函数中间的代码不看,我们先只关心和input_dev结构体相关的代码部分

诸如set_bitinput_set_abs_params等函数,填充了input_dev结构体,设置相关事件,用到的各种宏定义在input.h文件中,还遍历了tpd_driver_list数组,实现对不同型号TP的兼容

关键的input_register_device函数

它将驱动中填充的input_dev结构体注册到内核中,我们来看它注册的核心代码块

int input_register_device(struct input_dev *dev)
{
    ...

    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);

    /*  事件类型由input_dev的evbit成员来表示,
    在这里将其EV_SYN置位,表示设备支持所有的事件。
    注意,一个设备可以支持一种或者多种事件类型

    #define EV_SYN              0x00    //表示设备支持所有的事件
    #define EV_KEY              0x01    //键盘或者按键,表示一个键码
    #define EV_REL              0x02    //鼠标设备,表示一个相对的光标位置结果  
    #define EV_ABS              0x03    //手写板产生的值,其是一个绝对整数值  
    #define EV_MSC              0x04    //其他类型  
    #define EV_LED              0x11    //LED灯设备
    #define EV_SND              0x12    //蜂鸣器,输入声音  
    #define EV_REP              0x14    //允许重复按键类型  
    #define EV_PWR              0x16    //电源管理事件
    */

    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    __clear_bit(KEY_RESERVED, dev->keybit);

    ...

    /*
     * 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.
     */
    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;
    }
    /*  
     * rep主要是处理重复按键,如果没有定义dev->rep[REP_DELAY]和 dev->rep[REP_PERIOD], 
     * 则将其赋值为默认值。dev->rep[REP_DELAY]是指第一次按下多久算一次,这里是250ms, 
     * dev->rep[REP_PERIOD]指如果按键没有被抬起,每33ms算一次。 
     */

    //如果dev没有定义getkeycode和setkeycode,则赋默认值。
    //他们的作用一个是获得键的扫描码,一个是设置键的扫描码

    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;

    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;

    error = device_add(&dev->dev);/*将input_dev封装的dev注册到sysfs*/ 
    if (error)
        goto err_free_vals;

    ...

    /*将input_dev挂在input_dev_list上*/ 
    list_add_tail(&dev->node, &input_dev_list);//将新分配的input设备连接到input_dev_list链表上    

    list_for_each_entry(handler, &input_handler_list, node)//宏for循环
        input_attach_handler(dev, handler);
        //input_attach_handler的主要功能就是调用了两个函数,一个input_match_device进行配对,一个connect处理配对成功后续工作
        //遍历input_handler_list链表,配对 input_dev 和 input_handler   
        //input_attach_handler 这个函数是配对的关键
    /*
     * 这里先把input_dev挂载到input_dev_list 链表上,然后对每个挂载到input_handler_list 的handler
     * 调用input_attach_handler(dev, handler); 去匹配。 所有的input_dev挂载到input_dev_list 链表上
     * 所有的handler挂载到input_handler_list 上
     */

    ...
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

注册input device的过程就是为input device设置默认值,并将其挂在input_dev_list上与挂在input_handler_list中的handler相匹配。如果匹配成功就会调用handlerconnnect函数

所以我们接着看input_attach_handler是怎么做的

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int error;
    /*匹配id*/
    id = input_match_device(handler, dev);
    if (!id)
        return -ENODEV;
    /*如果匹配,则调用具体的handler的connect函数*/
    error = handler->connect(handler, dev, id);
    //evdev_connect函数做配对后的善后工作,分配一个evdev结构体,
    //并初始化相关成员,evdev结构体中有input_handle结构,初始化并注册之。 
    if (error && error != -ENODEV)
        pr_err("failed to attach handler %s to device %s, error: %d\n",
               handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

关于input_match_device函数,它是通过匹配id来确认匹配的,看handlerid是否支持,在事件处理层有这么一段代码,以evdev.c为例

static const struct input_device_id evdev_ids[] = {  
    { .driver_info = 1 },   // Matches all devices   
    { },                    // Terminating zero entry   
};
 
 
  • 1
  • 2
  • 3
  • 4

这是对evdev_handler的成员id_table的实例化

evdev_ids没有定义flags,也没有定义匹配属性值

这个evdev_ids的意思就是:evdev_handler可以匹配所有input_dev设备,

也就是所有的input_dev发出的事件,都可以由evdev_handler来处理

特别注意input_attach_handler中通过handler调用的connect函数,是在事件处理层定义并实现的,以evdev.c为例,则connect函数就是evdev_connect

evdev_connect()函数主要用来连接input_devinput_handler,这样事件的流通链才能建立,流通链建立后,事件才知道被谁处理,或者处理后将向谁返回结果

所以要想了解connect函数的具体作用,我们很有必要先来分析input_handler

2)input_handler

input_handler结构体是在事件处理层直接实例化的,以evdev.c为例

~/build_projects2/build/60_ali/kernel-3.18/drivers/input/evdev.c

static struct input_handler evdev_handler = {
    .event      = evdev_event,
    .events     = evdev_events,
    .connect    = evdev_connect,
    .disconnect = evdev_disconnect,
    .legacy_minors  = true,
    .minor      = EVDEV_MINOR_BASE,
    .name       = "evdev",
    .id_table   = evdev_ids,
};

static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
    input_unregister_handler(&evdev_handler);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

可以看出,evdev_handler的注册是放在平台初始化函数中进行的。也即,evdev.c一旦加载,就会执行evdev_init函数,则input_register_handler函数立即执行

int input_register_handler(struct input_handler *handler)
{
    struct input_dev *dev;
    int error;

    error = mutex_lock_interruptible(&input_mutex);
    if (error)
        return error;

    INIT_LIST_HEAD(&handler->h_list);

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

    list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    mutex_unlock(&input_mutex);
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

由此可见,input_register_handlerinput_register_device本质上做的事是一样的,input_attach_handler也会调用handler->connect

我们先简单了解下input_handle结构体,因为它的注册也是在connect函数中技能型的

3)input_handle

input_handle结构体和input_dev类似,是被包含在另一个人结构体中调用

struct evdev {
    int open;
    struct input_handle handle;
    wait_queue_head_t wait;
    struct evdev_client __rcu *grab;
    struct list_head client_list;
    spinlock_t client_lock; /* protects client_list */
    struct mutex mutex;
    struct device dev;
    struct cdev cdev;
    bool exist;
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

好,现在问题又回到了connect函数,我们看看evdev.c中的实现

/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect.
 */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;
    int minor;
    int dev_no;
    int error;

    /*
     * EVDEV_MINORS定义为32.表示evdev_handler所表示的32个设备文件.
     * evdev_table是一个struct evdev类型的全局数组.struct evdev是模块使用的封装结构
     * 在evdev_table找到为空的那一项,当找到为空的一项,便结束for循环。
     * 这时,minor就是数组中第一项为空的序号
     */
    minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
    if (minor < 0) {
        error = minor;
        pr_err("failed to reserve new minor: %d\n", error);
        return error;
    }

    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    if (!evdev) {
        error = -ENOMEM;
        goto err_free_minor;
    }

    /*
     * 对分配的evdev结构进行初始化,
     * 主要对链表、互斥锁和等待队列做必要的初始化。
     * 在evdev中,封装了一个handle结构,这个结构与handler是不同的。
     * 可以把handle看成是handler和input device的信息集合体,
     * 这个结构用来联系匹配成功的handler和input device。
     */
    INIT_LIST_HEAD(&evdev->client_list);
    spin_lock_init(&evdev->client_lock);
    mutex_init(&evdev->mutex);
    init_waitqueue_head(&evdev->wait);
    evdev->exist = true;

    dev_no = minor;
    /* Normalize device number if it falls into legacy range */
    if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
        dev_no -= EVDEV_MINOR_BASE;
    dev_set_name(&evdev->dev, "event%d", dev_no);

    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;
    /*
     * 在设备驱动模型中注册一个evdev->dev的设备,并初始化一个evdev->dev的设备
     * 这里,使evdev->dev所属的类指向input_class
     * 这样在/sysfs中创建的设备目录就会在/sys/class/input/下显示
     */
    evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);

    /*
     *input_register_handle完成的主要功能是: 
     *list_add_tail_rcu(&handle->d_node, &dev->h_list); 
     *list_add_tail(&handle->h_node, &handler->h_list); 
     */
    error = input_register_handle(&evdev->handle);
    if (error)
        goto err_free_evdev;

    cdev_init(&evdev->cdev, &evdev_fops);
    evdev->cdev.kobj.parent = &evdev->dev.kobj;
    error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
    if (error)
        goto err_unregister_handle;

    error = device_add(&evdev->dev);//将evdev->device注册到sysfs文件系统中
    if (error)
        goto err_cleanup_evdev;

    return 0;

 err_cleanup_evdev:
    evdev_cleanup(evdev);
 err_unregister_handle:
    input_unregister_handle(&evdev->handle);
 err_free_evdev:
    put_device(&evdev->dev);
 err_free_minor:
    input_free_minor(minor);
    return error;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

如上所示,input_devinput_handler注册时都要调用的handler->connect,所要完成的最主要的任务,就是填充并注册input_handle(顺便初始化了一些链表头、锁、等待队列,注册了字符设备框架)

我们再看input_register_handle函数

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.
     */
    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);

    /*
     * 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.
     */
    list_add_tail_rcu(&handle->h_node, &handler->h_list);

    if (handler->start)
        handler->start(handle);

    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

handle通过d_node挂到input deviceh_list,通过h_node挂到 handlerh_list上,这样input_devinput_handlerinput_handle就联系起来了,可与前面的框图或下面图对照

image


3、事件上报流程

我们先来看一下input.c的代码

static int __init input_init(void)//input子系统的初始化函数
{
    int err;

    err = class_register(&input_class);/*创建一个类input_class*/
    if (err) {
        pr_err("unable to register input_dev class\n");
        return err;
    }

    err = input_proc_init();/*在/proc下创建入口项*/
    if (err)
        goto fail1;

    /* 
     * 注册设备号INPUT_MAJOR的设备,
     * input子系统的设备的主设备号都是13,即INPUT_MAJOR为13
     */ 
    err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                     INPUT_MAX_CHAR_DEVICES, "input");
    if (err) {
        pr_err("unable to register char major %d", INPUT_MAJOR);
        goto fail2;
    }

    return 0;

 fail2: input_proc_exit();
 fail1: class_unregister(&input_class);
    return err;
}

static void __exit input_exit(void)
{
    input_proc_exit();
    unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                 INPUT_MAX_CHAR_DEVICES);
    class_unregister(&input_class);
}

subsys_initcall(input_init);
module_exit(input_exit);  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

上述代码是核心层的一个最基础的注册过程

input_init函数中,先注册了一个名为input_class的类,所有input device都属于这个类

sysfs中表现就是:
所有input device所代表的目录都位于/dev/class/input下面

然后调用input_proc_init()/proc下面建立相关的交互文件

再调用register_chrdev()注册了主设备号为INPUT_MAJOR(13),次设备号为0~255的字符设备

我们可以稍微了解下input_proc_init函数的实现:

static int __init input_proc_init(void)
{
    struct proc_dir_entry *entry;

    proc_bus_input_dir = proc_mkdir("bus/input", NULL);
    if (!proc_bus_input_dir)
        return -ENOMEM;

    entry = proc_create("devices", 0, proc_bus_input_dir,
                &input_devices_fileops);
    if (!entry)
        goto fail1;

    entry = proc_create("handlers", 0, proc_bus_input_dir,
                &input_handlers_fileops);
    if (!entry)
        goto fail2;

    return 0;

 fail2: remove_proc_entry("devices", proc_bus_input_dir);
 fail1: remove_proc_entry("bus/input", NULL);
    return -ENOMEM;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

kernel-3.18源码中,input.c里面的file_operations实例化了两个

static int input_proc_devices_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &input_devices_seq_ops);
}

static const struct file_operations input_devices_fileops = {
    .owner      = THIS_MODULE,
    .open       = input_proc_devices_open,
    .poll       = input_proc_devices_poll,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = seq_release,
};

...

static int input_proc_handlers_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &input_handlers_seq_ops);
}

static const struct file_operations input_handlers_fileops = {
    .owner      = THIS_MODULE,
    .open       = input_proc_handlers_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = seq_release,
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

上述代码是待解决分析的,目前没想通为什么要分两个

现在我们来分析事件处理层代码,以evdev.c为例

之前我们分析了evdev_handler的注册,现在来看事件处理层和上层的交互

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,
    .llseek     = no_llseek,
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里实例化的evdev_fops,它的成员函数就是对上层提供的接口

先看evdev_open函数,一旦上层打开设备文件就会调用

static int evdev_open(struct inode *inode, struct file *file)
{
    struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
    unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
    unsigned int size = sizeof(struct evdev_client) +
                    bufsize * sizeof(struct input_event);
    struct evdev_client *client;
    int error;

    client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
    if (!client)
        client = vzalloc(size);
    if (!client)
        return -ENOMEM;

    client->bufsize = bufsize;
    spin_lock_init(&client->buffer_lock);
    snprintf(client->name, sizeof(client->name), "%s-%d",
            dev_name(&evdev->dev), task_tgid_vnr(current));
    client->evdev = evdev;
    evdev_attach_client(evdev, client);

    error = evdev_open_device(evdev);
    if (error)
        goto err_free_client;

    file->private_data = client;
    nonseekable_open(inode, file);

    return 0;

 err_free_client:
    evdev_detach_client(evdev, client);
    kvfree(client);
    return error;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

有上述代码可知,evdev_open做了以下几件事:

  • 分配并初始化一个client结构体,并将它和evdev关联起来,关联的内容是,将client->evdev指向它所表示的evdev,调用evdev_attach_client()client挂到evdev->client_list

    这里我们简单看一下client结构体的定义,就在evdev.c

    struct evdev_client {
        unsigned int head;
        unsigned int tail;
        unsigned int packet_head; /* [future] position of the first element of next packet */
        spinlock_t buffer_lock; /* protects access to buffer, head and tail */
        struct wake_lock wake_lock;
        bool use_wake_lock;
        char name[28];
        struct fasync_struct *fasync;
        struct evdev *evdev;
        struct list_head node;
        int clkid;
        bool revoked;
        unsigned int bufsize;
        struct input_event buffer[];
    };
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这里先强调一点:我们驱动层上报的输入事件的键值,就是存放在evdev->buffer中的

  • 调用evdev_open_device()函数,打开输入设备使设备准备好接收或者发送数据

    static int evdev_open_device(struct evdev *evdev)
    {
        int retval;
    
        retval = mutex_lock_interruptible(&evdev->mutex);
        if (retval)
            return retval;
    
        if (!evdev->exist)
            retval = -ENODEV;
        else if (!evdev->open++) {
            retval = input_open_device(&evdev->handle);                       
            if (retval)
                evdev->open--;
        }
    
        mutex_unlock(&evdev->mutex);
        return retval;
    }
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    很明显,所谓的打开设备,最终是通过调用核心层input.c中的input_open_device函数实现的

再看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;
    size_t read = 0;
    int error;

    if (count != 0 && count < input_event_size())
        return -EINVAL;

    for (;;) {
        if (!evdev->exist || client->revoked)
            return -ENODEV;

        if (client->packet_head == client->tail &&
            (file->f_flags & O_NONBLOCK))
            return -EAGAIN;

        /*
         * count == 0 is special - no IO is done but we check
         * for error conditions (see above).
         */
        if (count == 0)
            break;

        while (read + input_event_size() <= count &&
               evdev_fetch_next_event(client, &event)) {

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

            read += input_event_size();
        }

        if (read)
            break;

        if (!(file->f_flags & O_NONBLOCK)) {
            error = wait_event_interruptible(evdev->wait,
                    client->packet_head != client->tail ||
                    !evdev->exist || client->revoked);
            if (error)
                return error;
        }
    }

    return read;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

我们注意到,在for循环的末尾,实现了一个等待队列来等待事件,没有事件就阻塞

这里阻塞等待的事件是什么呢? 以TP为例的话,就是触摸按键事件

我们的事件处理层对上层的读操作,用一个等待队列实现阻塞,这样就能保证,我们只有在触摸按键事件发生,中断到来,我们才去上报按键事件,并唤醒阻塞,让事件处理层的evdev_read将键值最终通过copy_to_user送到用户空间

上述代码的while循环中调用了input_event_to_user函数:

int input_event_to_user(char __user *buffer,
            const struct input_event *event)
{
    if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
        struct input_event_compat compat_event;

        compat_event.time.tv_sec = event->time.tv_sec;
        compat_event.time.tv_usec = event->time.tv_usec;
        compat_event.type = event->type;
        compat_event.code = event->code;
        compat_event.value = event->value;

        if (copy_to_user(buffer, &compat_event,
                 sizeof(struct input_event_compat)))
            return -EFAULT;

    } else {
        if (copy_to_user(buffer, event, sizeof(struct input_event)))
            return -EFAULT;
    }

    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

如我们所料,最终调用copy_to_user上报键值

让我们再回到evdev_read函数中,我们发现等待队列是在循环末尾实现的,我们可能会有疑问:之前就已经有一次事件上报了啊?它如何避免不必要的input_event_to_user

我们会发现,evdev_read函数中多次调用input_event_size函数进行条件判断

它判断缓存区大小是否足够,在读取数据的情况下,可能当前缓存区内没有数据可读,在这里先睡眠等待缓存区中有数据,如果在睡眠的时候,条件满足,是不会进行睡眠状态而直接返回的,然后根据read()提供的缓存区大小,将client中的数据(即上报的键值)写入到用户空间的缓存区中

到目前为止,我们搞清楚了用户程序对事件处理层的接口调用,以及上报到用户空间之前,在evdev_read中做的准备,那么问题来了,输入事件是怎么从设备驱动层一步步传递到事件驱动层的?

我们之前在讨论evdev_fops时,有两个成员函数eventevents,它们将在接下来的讨论中扮演举足轻重的角色

我们必须从设备驱动层看起,因为数据是从这一层开始上报的

我们知道,MTKTP驱动框架中,有个通用驱动mtk_tpd.c,这一层主要对输入事件进行一些初始化设置,所以,按键事件的中断捕捉和数据读取,是在具体型号TP驱动中完成的

jiang-pc:~/kernel-3.18/drivers/input/touchscreen/mediatek$ ls
focaltech      GT1151  GT9XX_hotknot      icn85xx_37pre       mtk_tpd.c           tpd_debug.c    tpd_init.c
ft5x0x         GT1X    GT9XX_hotknot_m    Kconfig             synaptics_i2c_rmi4  tpd_debug.h    tpd_misc.c
gslx68x        GT910   GT9XX_hotknot_scp  Makefile            tpd_button.c        tpd_default.c  tpd_setting.c
gslx68x_37pre  GT911   GT9XXTB_hotknot    met_ftrace_touch.h  tpd_calibrate.c     tpd_default.h
gslx68x_lp1    GT928   icn85xx            msg22xx             tpd_calibrate.h     tpd.h
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们以GT9XXTB_hotknot型号的TP驱动为例:

jiang-pc:~/kernel-3.18/drivers/input/touchscreen/mediatek/GT9XXTB_hotknot$ ls
goodix_tool.c  gt9xx_driver.c  gt9xx_update.c  include  Kconfig  Makefile
 
 
  • 1
  • 2

我们目前只关心gt9xx_driver.c,即主要的驱动代码

为了简化分析,我们暂时不去管此驱动代码中的框架和功能,后面会具体分析

我们现在只要知道,在驱动代码中,为了上报键值,调用了:

input_sync(tpd->dev);                                                     
input_report_key(tpd->dev, KEY_POWER, 0);
 
 
  • 1
  • 2

两者的定义:

~/kernel-3.18/include/linux/input.h

static inline void input_report_key(struct input_dev *dev, unsigned code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}  

static inline void input_sync(struct input_dev *dev)
{                             
    input_event(dev, EV_SYN, SYN_REPORT, 0);
} 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可见,二者都调用了input_event函数

~/kernel-3.18/drivers/input/input.c

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_handle_event(dev, type, code, value);
        spin_unlock_irqrestore(&dev->event_lock, flags);
    }
}
EXPORT_SYMBOL(input_event);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

除了自旋锁,只调用了一个input_handle_event函数

static void input_handle_event(struct input_dev *dev,
                   unsigned int type, unsigned int code, int value)
{
    ...

    if (disposition & INPUT_FLUSH) {
        if (dev->num_vals >= 2)
            input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    } else if (dev->num_vals >= dev->max_vals - 2) {
        dev->vals[dev->num_vals++] = input_value_sync;
        input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

为了上报数据,它调用了input_pass_values函数

static void input_pass_values(struct input_dev *dev,
                  struct input_value *vals, unsigned int count)
{
    struct input_handle *handle;
    struct input_value *v;

    if (!count)
        return;

    rcu_read_lock();

    handle = rcu_dereference(dev->grab);
    if (handle) {
        count = input_to_handler(handle, vals, count);
    } else {
        list_for_each_entry_rcu(handle, &dev->h_list, d_node)
            if (handle->open)
                count = input_to_handler(handle, vals, count);
    }

    rcu_read_unlock();

    ...
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

再调用input_to_handler函数

static unsigned int input_to_handler(struct input_handle *handle,
            struct input_value *vals, unsigned int count)
{
    struct input_handler *handler = handle->handler;

    ...

    if (handler->events)
        handler->events(handle, vals, count);
    else if (handler->event)
        for (v = vals; v != end; v++)
            handler->event(handle, v->type, v->code, v->value);

    return count;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里出现的handler->eventhandler->events就是在事件处理层定义的evdev_handler的成员函数evdev_eventevdev_events

现在我们终于可以来看evdev_event这个函数是怎么是实现的了

static void evdev_event(struct input_handle *handle,
            unsigned int type, unsigned int code, int value)
{
    struct input_value vals[] = { { type, code, value } };

    evdev_events(handle, vals, 1);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

原来所谓的evdev_event最终也是调用evdev_events实现的

static void evdev_events(struct input_handle *handle,
             const struct input_value *vals, unsigned int count)
{
    struct evdev *evdev = handle->private;
    struct evdev_client *client;
    ktime_t time_mono, time_real;

    time_mono = ktime_get();
    time_real = ktime_mono_to_real(time_mono);

    rcu_read_lock();

    client = rcu_dereference(evdev->grab);

    if (client)
        evdev_pass_values(client, vals, count, time_mono, time_real);
    else
        list_for_each_entry_rcu(client, &evdev->client_list, node)
            evdev_pass_values(client, vals, count,
                      time_mono, time_real);

    rcu_read_unlock();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

发现无论怎样都会调用evdev_pass_values函数,我们继续追

static void evdev_pass_values(struct evdev_client *client,
            const struct input_value *vals, unsigned int count,
            ktime_t mono, ktime_t real)
{
    struct evdev *evdev = client->evdev;
    const struct input_value *v;
    struct input_event event;
    bool wakeup = false;

    if (client->revoked)
        return;

    event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
                      mono : real);

    /* Interrupts are disabled, just acquire the lock. */
    spin_lock(&client->buffer_lock);

    for (v = vals; v != vals + count; v++) {
        event.type = v->type;
        event.code = v->code;
        event.value = v->value;
        __pass_event(client, &event);
        if (v->type == EV_SYN && v->code == SYN_REPORT)
            wakeup = true;
    }

    spin_unlock(&client->buffer_lock);

    if (wakeup)
        wake_up_interruptible(&evdev->wait);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

好了,前面的代码暂不分析,我们看到函数末尾调用了wake_up_interruptible函数,唤醒睡眠,这意味着什么?

和前面我们在分析事件处理层的evdev_read函数一联系,就明朗了

我们在evdev_read函数中通过等待队列实现了阻塞,等待输入事件的随机发生

而在触摸按键事件随机发生后,设备驱动层代码通过input_report_abs等函数上报数据,逐级调用,通过在事件处理层evdev.c里定义的evdev_handler->events处理事件,最终调用到evdev_pass_values并唤醒睡眠

一旦在evdev_read中的等待队列被唤醒,则进入下一轮循环上报新的键值给用户空间

自此,Input子系统的事件上报流程大致分析完毕


4、设备驱动层

我们着重分析一下具体型号的独立驱动代码都做了哪些比较重要的事

1)具体驱动的加载
/* called when loaded into kernel */
static int __init tpd_driver_init(void)
{      
    GTP_INFO("MediaTek gt91xx touch panel driver init\n");
    tpd_get_dts_info();
    if (tpd_driver_add(&tpd_device_driver) < 0) 
        GTP_INFO("add generic driver failed\n");

    return 0;
}          

/* should never be called */
static void __exit tpd_driver_exit(void)
{          
    GTP_INFO("MediaTek gt91xx touch panel driver exit\n");
    tpd_driver_remove(&tpd_device_driver);
}
module_init(tpd_driver_init);
module_exit(tpd_driver_exit);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

TP驱动加载时,做的最重要一件事就是tpd_driver_add(&tpd_device_driver)

这里有两点要分析:

  • tpd_device_driver结构体

    ~/kernel-3.18/drivers/input/touchscreen/mediatek/GT9XXTB_hotknot/gt9xx_driver.c
    
    static struct tpd_driver_t tpd_device_driver = {
        .tpd_device_name = "gt9xx",
        .tpd_local_init = tpd_local_init,
        .suspend = tpd_suspend,
        .resume = tpd_resume,
    };
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这个结构体最重要的成员是tpd_local_init函数,当结构体被注册到内核后,这个函数会在mtk_tpd.ctpd_probe函数中被调用,后面再议

  • tpd_driver_add函数

    ~/kernel-3.18/drivers/input/touchscreen/mediatek/mtk_tpd.c
    
    int tpd_driver_add(struct tpd_driver_t *tpd_drv)
    {
        int i;
    
        if (g_tpd_drv != NULL) {
            TPD_DMESG("touch driver exist\n");
            return -1;
        }
        /* check parameter */
        if (tpd_drv == NULL)
            return -1;
        tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;
        /* R-touch */
        if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {
            tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
            tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
            tpd_driver_list[0].suspend = tpd_drv->suspend;
            tpd_driver_list[0].resume = tpd_drv->resume;
            tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
            return 0;
        }
        for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
            /* add tpd driver into list */
            if (tpd_driver_list[i].tpd_device_name == NULL) {
                tpd_driver_list[i].tpd_device_name = tpd_drv->tpd_device_name;
                tpd_driver_list[i].tpd_local_init = tpd_drv->tpd_local_init;
                tpd_driver_list[i].suspend = tpd_drv->suspend;
                tpd_driver_list[i].resume = tpd_drv->resume;
                tpd_driver_list[i].tpd_have_button = tpd_drv->tpd_have_button;
                tpd_driver_list[i].attrs = tpd_drv->attrs;
    
    #if 0
    
                if (tpd_drv->tpd_local_init() == 0) {
                    TPD_DMESG("load %s successfully\n",
                          tpd_driver_list[i].tpd_device_name);
                    g_tpd_drv = &tpd_driver_list[i];
                }
    
    #endif
    
                break;
            }
            if (strcmp(tpd_driver_list[i].tpd_device_name, tpd_drv->tpd_device_name) == 0)
                return 1;   /* driver exist */
        }
    
        return 0;
    }
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    这个函数是在mtk_tpd.c中定义的接口函数,在此处被具体型号的TP驱动调用

    此函数完成的任务,最主要是将当前的平台驱动,具体就是struct tpd_driver_t tpd_device_driver结构体填充到tpd_driver_list静态数组中

    这么做的目的是为了实现兼容

    怎么个兼容呢?

    我们要回到mtk_tpd.c文件中的platform平台代码tpd_probe函数中,之前我们遗漏了一些比较关键的代码块,如:

        ...
        /* struct input_dev dev initialization and registration */
        //input设备事件类型的设置
        tpd->dev->name = TPD_DEVICE;//设置输入设备名字
        set_bit(EV_ABS, tpd->dev->evbit);//绝对坐标事件
        set_bit(EV_KEY, tpd->dev->evbit);//按键事件
        set_bit(ABS_X, tpd->dev->absbit);//报告工具的x坐标(单点触摸带MT多点触摸)
        set_bit(ABS_Y, tpd->dev->absbit);
        set_bit(ABS_PRESSURE, tpd->dev->absbit);//报告触摸工具顶尖处的压力或触摸接触的信号强度
    
    #if !defined(CONFIG_MTK_S3320) && !defined(CONFIG_MTK_S3320_47)\
    
        && !defined(CONFIG_MTK_S3320_50) && !defined(CONFIG_MTK_MIT200) \
        && !defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) && !defined(CONFIG_MTK_S7020)
        set_bit(BTN_TOUCH, tpd->dev->keybit);//指示工具是否接触触摸设备
    
    #endif /* CONFIG_MTK_S3320 */
    
        set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);
    
        /* save dev for regulator_get() before tpd_local_init() */
        tpd->tpd_dev = &pdev->dev;
        for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
            /* add tpd driver into list */
            if (tpd_driver_list[i].tpd_device_name != NULL) {
                tpd_driver_list[i].tpd_local_init();
                /* msleep(1); */
                if (tpd_load_status == 1) {
                    TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%s\n",
                          tpd_driver_list[i].tpd_device_name);
    /* Vanzo:luanshijun on: Thu, 03 Mar 2016 11:04:28 +0800
     * add device info
     */
                                    extern void v_set_dev_name(int id, char *name);
                                    v_set_dev_name(2, tpd_driver_list[i].tpd_device_name);
    // End of Vanzo:luanshijun
                    g_tpd_drv = &tpd_driver_list[i];
                    break;
                }
            }
        }
        if (g_tpd_drv == NULL) {
            if (tpd_driver_list[0].tpd_device_name != NULL) {
                g_tpd_drv = &tpd_driver_list[0];
                /* touch_type:0: r-touch, 1: C-touch */
                touch_type = 0;
                g_tpd_drv->tpd_local_init();
                TPD_DMESG("[mtk-tpd]Generic touch panel driver\n");
            } else {
                TPD_DMESG("[mtk-tpd]cap touch and Generic touch both are not loaded!!\n");
                return 0;
            }
        }
        ...
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    关键代码:遍历mtktpd_driver_list里面的所有的驱动,判断名字是否为NULL

    每一个module touch IC驱动都会添加到这个静态数组里面

    对于if (tpd_load_status == 1)这个条件,会判断我们所遍历的每一个module IC驱动的初始化函数,i2c通信成功的话就会将tpd_load_status1(具体驱动的probe函数中),所以我们就是通过这个值判断哪一个驱动的

    这里对tpd_driver_list数组进行成员遍历,用来兼容对应的TP驱动,并且执行成员函数tpd_local_init()touch IC进行初始化

    ~/kernel-3.18/drivers/input/touchscreen/mediatek/GT9XXTB_hotknot/gt9xx_driver.c
    
    static int tpd_local_init(void)
    {
        ...
    
        if (i2c_add_driver(&tpd_i2c_driver) != 0) {
            GTP_INFO("unable to add i2c driver.\n");
            return -1;
        }
    
        if (tpd_load_status == 0) {
            GTP_INFO("add error touch panel driver.\n");
            i2c_del_driver(&tpd_i2c_driver);
            return -1;
        }
    
        ...
    }
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    代码很长,做了很多处理,在此略去

    但很明显,TP设备的I2C驱动框架注册是在这里完成的

    /* use a define to avoid include chaining to get THIS_MODULE */
    
    #define i2c_add_driver(driver) \                                  
    
        i2c_register_driver(THIS_MODULE, driver)
       
       
    • 1
    • 2
    • 3
    • 4
    • 5

    我们简单列一下I2C设备的注册和匹配硬件的函数调用:

    i2c_add_driver -> 
        i2c_register_driver(THIS_MODULE, driver) ->  
            driver_register(&driver->driver) ->
                bus_add_driver(drv);//添加驱动到总线上->
                    if (drv->bus->p->drivers_autoprobe) {error = driver_attach(drv);->)
                        bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) ->
                            driver_match_device(drv, dev) -> //判断drv->bus->match()是否存在 并执行
                                drv->bus->match ? drv->bus->match(dev, drv) : 1 ;
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最终调用的match函数就是i2c_device_match

    static int i2c_device_match(struct device *dev, struct device_driver *drv)
    {
        struct i2c_client   *client = i2c_verify_client(dev);
        struct i2c_driver   *driver;
    
        if (!client)
            return 0;
    
        /* Attempt an OF style match */
        if (of_driver_match_device(dev, drv))
            return 1;
    
        /* Then ACPI style match */
        if (acpi_driver_match_device(dev, drv))
            return 1;
    
        driver = to_i2c_driver(drv);
        /* match on an id table if there is one */
        if (driver->id_table)
            return i2c_match_id(driver->id_table, client) != NULL;
    
        return 0;
    }
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    关键一点:i2c驱动的probe函数在哪调用?

    之前在跟踪I2C设备的注册和匹配硬件时,注意到会回调函数__driver_attach

    static int __driver_attach(struct device *dev, void *data)
    {
        struct device_driver *drv = data;
    
        /*
         * Lock device and try to bind to it. We drop the error
         * here and always return 0, because we need to keep trying
         * to bind to devices and some drivers will return an error
         * simply if it didn't support the device.
         *
         * driver_probe_device() will spit a warning if there
         * is an error.
         */
    
        if (!driver_match_device(drv, dev))
            return 0;
    
        if (dev->parent)    /* Needed for USB */
            device_lock(dev->parent);
        device_lock(dev);
        if (!dev->driver)
            driver_probe_device(drv, dev);
        device_unlock(dev);
        if (dev->parent)
            device_unlock(dev->parent);
    
        return 0;
    }
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    我们发现一个重要的函数driver_probe_device(drv, dev);

    int driver_probe_device(struct device_driver *drv, struct device *dev)
    {
        int ret = 0;
    
        if (!device_is_registered(dev))
            return -ENODEV;
    
        pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
             drv->bus->name, __func__, dev_name(dev), drv->name);
    
        pm_runtime_barrier(dev);
        ret = really_probe(dev, drv);
        pm_request_idle(dev);
    
        return ret;
    }
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    很明显,它调用了一个叫作really_probe的函数

    static int really_probe(struct device *dev, struct device_driver *drv)
    {
        ...
        if (dev->bus->probe) {
            ret = dev->bus->probe(dev);
            if (ret)
                goto probe_failed;
        } else if (drv->probe) {
            ret = drv->probe(dev);
            if (ret)
                goto probe_failed;
        }
        ...
    }
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    得出结论,i2c驱动的probe函数,就是这样调用的

    既然注册了I2C设备,那接下来就要讨论I2C驱动部分了

2)TP驱动的I2C驱动部分
~/kernel-3.18/drivers/input/touchscreen/mediatek/GT9XXTB_hotknot/gt9xx_driver.c

static const struct of_device_id tpd_of_match[] = {
    {.compatible = "mediatek,cap_touch"},
    {},
};

static struct i2c_driver tpd_i2c_driver = {
    .probe = tpd_i2c_probe,
    .remove = tpd_i2c_remove,
    .detect = tpd_i2c_detect,
    .driver.name = "gt9xx",
    .driver = {
           .name = "gt9xx",
           .of_match_table = tpd_of_match,
           },
    .id_table = tpd_i2c_id,
    .address_list = (const unsigned short *)forces,
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

和正常的I2C驱动框架一样,这里实例化I2C驱动的结构体极其成员函数

我们着重分析tpd_i2c_probe函数的功能

static s32 tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    ...

    ret = tpd_power_on(client);//上电
    if (ret < 0)
        GTP_ERROR("I2C communication ERROR!");

#ifdef VELOCITY_CUSTOM
    tpd_v_magnify_x = TPD_VELOCITY_CUSTOM_X;
    tpd_v_magnify_y = TPD_VELOCITY_CUSTOM_Y;

#endif

    ret = gtp_read_version(client, &version_info);//读固件版本
    if (ret < 0)
        GTP_ERROR("Read version failed.");

    ret = gtp_init_panel(client);//初始化TP,进行I2C通讯确认
    if (ret < 0)
        GTP_ERROR("GTP init panel failed.");

    /* Create proc file system 创建对应的proc文件系统目录 */
    gt91xx_config_proc =
        proc_create(GT91XX_CONFIG_PROC_FILE, 0660, NULL,
            &gt_upgrade_proc_fops);
    if (gt91xx_config_proc == NULL)
        GTP_ERROR("create_proc_entry %s failed\n",
              GT91XX_CONFIG_PROC_FILE);

#if defined(CONFIG_GTP_CREATE_WR_NODE)
    init_wr_node(client);
#endif

    thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);//开启触摸事件处理线程
    if (IS_ERR(thread)) {
        err = PTR_ERR(thread);
        GTP_INFO(TPD_DEVICE "failed create thread: %d\n", err);
    }

    ...

    node = of_find_matching_node(NULL, touch_of_match);
    if (node) {
        touch_irq = irq_of_parse_and_map(node, 0);
        ret = request_irq(touch_irq,
                  (irq_handler_t)tpd_eint_interrupt_handler,//申请中断处理函数
                  !int_type ? IRQF_TRIGGER_RISING :
                  IRQF_TRIGGER_FALLING,
                  "TOUCH_PANEL-eint", NULL);
        if (ret > 0) {
            ret = -1;
            GTP_ERROR("tpd request_irq IRQ LINE NOT AVAILABLE!.");
        }
    }
    /* mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); */
    enable_irq(touch_irq);

    ...

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

任何TP驱动都会有上电、读固件版本、初始化屏几个步骤

重点有两个函数:事件处理线程中断处理函数

中断处理函数:

static irqreturn_t tpd_eint_interrupt_handler(void)
{
    TPD_DEBUG_PRINT_INT;
    tpd_flag = 1;
    wake_up_interruptible(&waiter);
    return IRQ_HANDLED;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

一旦触摸事件发生,则触发中断,此中断处理函数唤醒等待队列

那么这个等待队列是谁的呢?

事件处理线程:

static int touch_event_handler(void *unused)
{
    ...

    sched_setscheduler(current, SCHED_RR, &param);
    do {
        set_current_state(TASK_INTERRUPTIBLE);//设置Task 的状态为可中断的等待状态
        if (tpd_eint_mode) {
            wait_event_interruptible(waiter, tpd_flag != 0);//满足tpd_flag!=0 就唤醒队列
            tpd_flag = 0;//改变条件
        } else {
            msleep(tpd_polling_time);
        }

        set_current_state(TASK_RUNNING);//设置Task 的状态为执行态
        ...

#if defined(CONFIG_GTP_SLIDE_WAKEUP)
        if (DOZE_ENABLED == doze_status) {
            ret = gtp_i2c_read(i2c_client_point, doze_buf, 3);
            GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
            if (ret > 0) {
                if (0xAA == doze_buf[2]) {
                    GTP_INFO("Forward slide up screen!");
                    doze_status = DOZE_WAKEUP;
                    input_report_key(tpd->dev,
                             KEY_POWER, 1);
                    input_sync(tpd->dev);
                    input_report_key(tpd->dev,
                             KEY_POWER, 0);
                    input_sync(tpd->dev);
                    /* clear 0x814B */
                    doze_buf[2] = 0x00;
                    gtp_i2c_write(i2c_client_point,
                              doze_buf, 3);
                } else if (0xBB == doze_buf[2]) {
                    GTP_INFO("Back slide up screen!");
                    doze_status = DOZE_WAKEUP;
                    input_report_key(tpd->dev,
                             KEY_POWER, 1);
                    input_sync(tpd->dev);

    ...

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

开启这个线程的目的是读取坐标,上报坐位给上层使用;它会在循环内轮询触摸事件,但触摸事件是随机的,所以用等待队列实现阻塞

只有当触摸事件的中断到来,才唤醒队列,之后通过input_report_absinput_sync函数上报键值,上报事件数据的问题我们先前已经讨论过了

在上报事件数据时,内核与TP之间会进行I2C通信获取数据,用gtp_i2c_readgtp_i2c_write函数,同样在当前驱动代码中定义

我们随便看一个

s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{
    s32 ret = -1;
    u16 addr = (buf[0] << 8) + buf[1];

    ret = i2c_read_bytes_non_dma(client, addr, &buf[2], len - 2);
    if (!ret)
        return 2;
#if defined(CONFIG_GTP_SLIDE_WAKEUP)
    if (DOZE_ENABLED == doze_status)
        return ret;
#endif
#if defined(CONFIG_GTP_COMPATIBLE_MODE)
    if (CHIP_TYPE_GT9F == gtp_chip_type) {
        gtp_recovery_reset(client);
    } else
#endif
    {
        gtp_reset_guitar(client, 20);
    }
    return ret;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

调用了i2c_read_bytes_non_dma

int i2c_read_bytes_non_dma(struct i2c_client *client, u16 addr, u8 *rxbuf, int len)
{
    u8 buffer[GTP_ADDR_LENGTH];
    u8 retry;
    u16 left = len;
    u16 offset = 0;

    struct i2c_msg msg[2] = {
        {
         .addr = ((client->addr & I2C_MASK_FLAG) | (I2C_ENEXT_FLAG)),
         /* .addr = ((client->addr &I2C_MASK_FLAG) | (I2C_PUSHPULL_FLAG)), */
         .flags = 0,
         .buf = buffer,
         .len = GTP_ADDR_LENGTH,
         .timing = I2C_MASTER_CLOCK},
        {
         .addr = ((client->addr & I2C_MASK_FLAG) | (I2C_ENEXT_FLAG)),
         /* .addr = ((client->addr &I2C_MASK_FLAG) | (I2C_PUSHPULL_FLAG)), */
         .flags = I2C_M_RD,
         .timing = I2C_MASTER_CLOCK},
    };

    if (rxbuf == NULL)
        return -1;

    GTP_DEBUG("i2c_read_bytes to device %02X address %04X len %d", client->addr, addr, len);

    while (left > 0) {
        buffer[0] = ((addr + offset) >> 8) & 0xFF;
        buffer[1] = (addr + offset) & 0xFF;

        msg[1].buf = &rxbuf[offset];

        if (left > MAX_TRANSACTION_LENGTH) {
            msg[1].len = MAX_TRANSACTION_LENGTH;
            left -= MAX_TRANSACTION_LENGTH;
            offset += MAX_TRANSACTION_LENGTH;
        } else {
            msg[1].len = left;
            left = 0;
        }

        retry = 0;

        while (i2c_transfer(client->adapter, &msg[0], 2) != 2) {
            retry++;

            if (retry == 5) {
                GTP_ERROR("I2C read 0x%X length=%d failed\n", addr + offset, len);
                return -1;
            }
        }
    }

    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

可见通过I2C获取硬件数据,一样是要通过i2c_msg结构体和i2c_transfer函数



  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值