from vine_farer
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上报数据
struct input_dev:会在具体设备驱动层中被填充
struct input_handle:会在事件处理层和设备驱动层注册设备时通过input_dev或input_handler间接调用
struct input_handler:会在事件处理层如evdev.c中被实例化
结构体定义在kernel-3.18/include/linux/input.h
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;
};
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
};
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链表
};
input_handle
是连接input_device
和input_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
个
MTK
平台的TP
驱动是分为两个部分组合在一起的,全平台的共享驱动mtk_tpd.c
(抽象),以及各个型号TP
的独立驱动(真实)
mtk_tpd.c
负责将TP注册到platform
总线,以及利用input
子系统核心层提供的API向事件处理层上报键值各个型号的独立驱动负责
I2C
总线挂接,读取键值提交给mtk_tpd.c
我们在此处先单独分析三大结构体在驱动中的作用
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) - 1);
__module_get(THIS_MODULE);
}
return dev;
}
tpd_probe
函数中间的代码不看,我们先只关心和input_dev
结构体相关的代码部分
诸如set_bit
、input_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 上
*/
...
}
注册input device
的过程就是为input device
设置默认值,并将其挂在input_dev_list
上与挂在input_handler_list
中的handler
相匹配。如果匹配成功就会调用handler
的connnect
函数
所以我们接着看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;
}
关于input_match_device
函数,它是通过匹配id
来确认匹配的,看handler
的id
是否支持,在事件处理层有这么一段代码,以evdev.c
为例
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, // Matches all devices
{ }, // Terminating zero entry
};
这是对
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_dev
和input_handler
,这样事件的流通链才能建立,流通链建立后,事件才知道被谁处理,或者处理后将向谁返回结果
所以要想了解connect
函数的具体作用,我们很有必要先来分析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);
}
可以看出,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;
}
由此可见,input_register_handler
和input_register_device
本质上做的事是一样的,input_attach_handler
也会调用handler->connect
我们先简单了解下input_handle
结构体,因为它的注册也是在connect
函数中技能型的
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;
};
好,现在问题又回到了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;
}
如上所示,input_dev
和input_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;
}
将handle
通过d_node
挂到input device
的h_list
,通过h_node
挂到 handler
的h_list
上,这样input_dev
、input_handler
、input_handle
就联系起来了,可与前面的框图或下面图对照
我们先来看一下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);
上述代码是核心层的一个最基础的注册过程
在
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;
}
在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,
};
上述代码是待解决分析的,目前没想通为什么要分两个
现在我们来分析事件处理层代码,以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,
};
这里实例化的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;
}
有上述代码可知,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[];
};
这里先强调一点:我们驱动层上报的输入事件的键值,就是存放在
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;
}
很明显,所谓的打开设备,最终是通过调用核心层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;
}
我们注意到,在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;
}
如我们所料,最终调用copy_to_user
上报键值
让我们再回到evdev_read
函数中,我们发现等待队列是在循环末尾实现的,我们可能会有疑问:之前就已经有一次事件上报了啊?它如何避免不必要的input_event_to_user
?
我们会发现,evdev_read
函数中多次调用input_event_size
函数进行条件判断
它判断缓存区大小是否足够,在读取数据的情况下,可能当前缓存区内没有数据可读,在这里先睡眠等待缓存区中有数据,如果在睡眠的时候,条件满足,是不会进行睡眠状态而直接返回的,然后根据
read()
提供的缓存区大小,将client
中的数据(即上报的键值)写入到用户空间的缓存区中
到目前为止,我们搞清楚了用户程序对事件处理层的接口调用,以及上报到用户空间之前,在evdev_read
中做的准备,那么问题来了,输入事件是怎么从设备驱动层一步步传递到事件驱动层的?
我们之前在讨论evdev_fops
时,有两个成员函数event
和events
,它们将在接下来的讨论中扮演举足轻重的角色
我们必须从设备驱动层看起,因为数据是从这一层开始上报的
我们知道,
MTK
的TP
驱动框架中,有个通用驱动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
我们以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
我们目前只关心gt9xx_driver.c
,即主要的驱动代码
为了简化分析,我们暂时不去管此驱动代码中的框架和功能,后面会具体分析
我们现在只要知道,在驱动代码中,为了上报键值,调用了:
input_sync(tpd->dev);
input_report_key(tpd->dev, KEY_POWER, 0);
两者的定义:
~/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);
}
可见,二者都调用了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);
除了自旋锁,只调用了一个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;
}
}
为了上报数据,它调用了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();
...
}
再调用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;
}
这里出现的handler->event
和handler->events
就是在事件处理层定义的evdev_handler
的成员函数evdev_event
、evdev_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);
}
原来所谓的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();
}
发现无论怎样都会调用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);
}
好了,前面的代码暂不分析,我们看到函数末尾调用了wake_up_interruptible
函数,唤醒睡眠,这意味着什么?
和前面我们在分析事件处理层的evdev_read
函数一联系,就明朗了
我们在
evdev_read
函数中通过等待队列实现了阻塞,等待输入事件的随机发生而在触摸按键事件随机发生后,设备驱动层代码通过
input_report_abs
等函数上报数据,逐级调用,通过在事件处理层evdev.c
里定义的evdev_handler->events
处理事件,最终调用到evdev_pass_values
并唤醒睡眠一旦在
evdev_read
中的等待队列被唤醒,则进入下一轮循环上报新的键值给用户空间
自此,Input
子系统的事件上报流程大致分析完毕
我们着重分析一下具体型号的独立驱动代码都做了哪些比较重要的事
/* 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);
当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,
};
这个结构体最重要的成员是tpd_local_init
函数,当结构体被注册到内核后,这个函数会在mtk_tpd.c
的tpd_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;
}
这个函数是在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;
}
}
...
关键代码:遍历
mtk
的tpd_driver_list
里面的所有的驱动,判断名字是否为NULL
每一个
module touch IC
驱动都会添加到这个静态数组里面对于
if (tpd_load_status == 1)
这个条件,会判断我们所遍历的每一个module IC
驱动的初始化函数,i2c
通信成功的话就会将tpd_load_status
置1
(具体驱动的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;
}
...
}
代码很长,做了很多处理,在此略去
但很明显,TP
设备的I2C
驱动框架注册是在这里完成的
/* use a define to avoid include chaining to get THIS_MODULE */
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
我们简单列一下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 ;
最终调用的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;
}
关键一点: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;
}
我们发现一个重要的函数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;
}
很明显,它调用了一个叫作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;
}
...
}
得出结论,i2c
驱动的probe
函数,就是这样调用的
既然注册了I2C
设备,那接下来就要讨论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,
};
和正常的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,
>_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);
...
}
任何
TP
驱动都会有上电、读固件版本、初始化屏几个步骤重点有两个函数:事件处理线程、中断处理函数
中断处理函数:
static irqreturn_t tpd_eint_interrupt_handler(void)
{
TPD_DEBUG_PRINT_INT;
tpd_flag = 1;
wake_up_interruptible(&waiter);
return IRQ_HANDLED;
}
一旦触摸事件发生,则触发中断,此中断处理函数唤醒等待队列
那么这个等待队列是谁的呢?
事件处理线程:
static int touch_event_handler(void *unused)
{
...
sched_setscheduler(current, SCHED_RR, ¶m);
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);
...
}
开启这个线程的目的是读取坐标,上报坐位给上层使用;它会在循环内轮询触摸事件,但触摸事件是随机的,所以用等待队列实现阻塞
只有当触摸事件的中断到来,才唤醒队列,之后通过
input_report_abs
和input_sync
函数上报键值,上报事件数据的问题我们先前已经讨论过了在上报事件数据时,内核与
TP
之间会进行I2C
通信获取数据,用gtp_i2c_read
和gtp_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;
}
调用了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;
}
可见通过I2C
获取硬件数据,一样是要通过i2c_msg
结构体和i2c_transfer
函数