Input子系统与TP驱动
from vine_farer
1、层级结构
jiang-pc:~/build_projects2/build/60_ali/kernel-3.18/drivers/input$ ls
apm-power.c ff-core.c goodix_finger input-polldev.c keyboard Makefile serio
cdfinger ff-memless.c input.c jmt101 keycombo.c matrix-keymap.c sparse-keymap.c
elan fingerprint input-compat.c joydev.c keyreset.c misc sw9551_fp
evbug.c focaltech_fp input-compat.h joystick madev mouse tablet
evdev.c gameport input-mt.c Kconfig madev_080t mousedev.c touchscreen
这是Input子系统在内核驱动中的顶层目录(MTK平台)
Input子系统分为三个层级:
事件处理层:evdev.c 、joydev.c、mousedev.c
核心层:input.c
设备驱动层:touchscreen、mouse、fingerprint 等
事件处理层:通过核心层的API获取输入事件上报的数据,定义API与应用层交互
核心层:为事件处理层和设备驱动层提供接口API
设备驱动层:采集输入设备的数据信息,通过核心层提供的API上报数据
层级框架视图:
2、基础数据结构
struct input_dev:会在具体设备驱动层中被填充
struct input_handle:会在事件处理层和设备驱动层注册设备时通过input_dev或input_handler间接调用
struct input_handler:会在事件处理层如evdev.c中被实例化
结构体定义在kernel-3.18/include/linux/input.h
三大结构体关系视图一:
1)input_dev
struct input_dev {
const char *name;/*导出到用户空间的相关信息,在sys文件可以看到*/
const char *phys;
const char *uniq;
struct input_id id;/*与input_handler匹配用的id*/
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
/*输入设备的事件支持位图*/
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];/*支持所有事件*/
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];/*按键事件*/
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];/*相对位移事件*/
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];/*绝对位移事件*/
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];/*其它事件*/
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];/*LED事件*/
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/*声音事件*/
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];/*受力事件*/
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];/*开关机事件*/
unsigned int hint_events_per_packet;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);
struct ff_device *ff;
unsigned int repeat_key;//最近一次的按键值
struct timer_list timer;
int rep[REP_CNT];
struct input_mt *mt;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反应设备当前的按键状态
unsigned long led[BITS_TO_LONGS(LED_CNT)];//反应设备当前的led状态
unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反应设备当前的声音输入状态
unsigned long sw[BITS_TO_LONGS(SW_CNT)];//反应设备当前的开关状态
int (*open)(struct input_dev *dev);//第一次打开设备时调用,初始化设备用
void (*close)(struct input_dev *dev);//最后一个应用程序释放设备时用,关闭设备
int (*flush)(struct input_dev *dev, struct file *file);/*用于处理传递给设备的事件,如LED事件和声音事件*/
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle __rcu *grab;//当前占有该设备的input_handle
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list;//该链表头用于链接此input_dev所关联的input_handle
struct list_head node;//用于将此input_dev链接到input_dev_list
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
};
2)input_handler
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);/*event用于处理事件*/
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);/*connect用于建立handler和device的联系*/
void (*disconnect)(struct input_handle *handle);/*disconnect用于解除handler和device的联系*/
void (*start)(struct input_handle *handle);
bool legacy_minors;
int minor;//次设备号
const char *name;
const struct input_device_id *id_table;//用于和input_dev匹配
struct list_head h_list;//用于链接和此input_handler相关的input_handle
struct list_head node;//用于将该input_handler链入input_handler_list
};
3)input_handle
struct input_handle {
void *private;
int open;//记录设备的打开次数(有多少个应用程序访问设备)
const char *name;
struct input_dev *dev;//指向所属的device
struct input_handler *handler;//指向所属的handler
struct list_head d_node;//用于将此input_handle链入所属input_dev的h_list链表
struct list_head h_node;//用于将此input_handle链入所属input_handler的h_list链表
};
三大结构体关系视图二:
浅析三大结构体关系
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
个
以TP驱动为例:
MTK
平台的TP
驱动是分为两个部分组合在一起的,全平台的共享驱动mtk_tpd.c
(抽象),以及各个型号TP
的独立驱动(真实)
mtk_tpd.c
负责将TP注册到platform
总线,以及利用input
子系统核心层提供的API向事件处理层上报键值各个型号的独立驱动负责
I2C
总线挂接,读取键值提交给mtk_tpd.c
我们在此处先单独分析三大结构体在驱动中的作用
1)input_dev
input_dev
结构体被定义在tpd_device
结构体中,tpd_device
如下定义
~/kernel-3.18/drivers/input/touchscreen/mediatek/tpd.h
struct tpd_device {
struct device *tpd_dev;
struct regulator *reg;
struct regulator *io_reg;
struct input_dev *dev;
struct input_dev *kpd;
struct timer_list timer;
struct tasklet_struct tasklet;
int btn_state;
};
然后我们分析mtk_tpd.c
~/kernel-3.18/drivers/input/touchscreen/mediatek/mtk_tpd.c
struct tpd_device *tpd = 0;
在此处我们可看见代码中定义了tpd_device
的结构体指针,后面对input_dev
结构体的调用和填充,都是通过tpd->dev
来实现的
我们之前提到过,mtk_tpd.c
做的重要的一件事就是注册platform
平台总线,对设备的申请、注册,一些事件的属性设置,以及对各型号TP的兼容遍历,都是在其probe
函数中完成的
static struct platform_driver tpd_driver = {
.remove = tpd_remove,
.shutdown = NULL,
.probe = tpd_probe,
.driver = {
.name = TPD_DEVICE,
.pm = &tpd_pm_ops,
.owner = THIS_MODULE,
.of_match_table = touch_of_match,//设备树匹配
},
};
再看具体的tpd_probe
函数
/* touch panel probe */
static int tpd_probe(struct platform_device *pdev)
{
int touch_type = 1; /* 0:R-touch, 1: Cap-touch */
int i = 0;
#ifndef CONFIG_CUSTOM_LCM_X
#ifdef CONFIG_LCM_WIDTH
unsigned long tpd_res_x = 0, tpd_res_y = 0;
int ret = 0;
#endif
#endif
TPD_DMESG("enter %s, %d\n", __func__, __LINE__);
if (misc_register(&tpd_misc_device))
pr_err("mtk_tpd: tpd_misc_device register failed\n");
tpd_get_gpio_info(pdev);
tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
if (tpd == NULL)
return -ENOMEM;
memset(tpd, 0, sizeof(struct tpd_device));
/* allocate input device */
tpd->dev = input_allocate_device();
if (tpd->dev == NULL) {
kfree(tpd);
return -ENOMEM;
}
...
除了一些初始化和注册外,和我们的input_dev
有关的函数是tpd->dev = input_allocate_device();
,它主要为结构体申请内存空间,填充部分结构体信息和初始化一些锁、定时器、链表头,如下所示:
struct input_dev *input_allocate_device(void)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
init_timer(&dev->timer);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
dev_set_name(&dev->dev, "input%lu",
(unsigned long) atomic_inc_return(&input_no) -