最近要实现双击唤醒屏幕
第一个方案:勉强实现,但是功耗大,还没进行消抖处理
实现步骤如下:
1.找到tp实现的代码kernel/drivers/input/touchscreen/gsl/gslX68X.c
2.找到tp休眠挂起的代码,开启中断,去掉拉低电源引脚
在static struct i2c_driver gsl_ts_driver结构体中有 .pm = &gsl2680_ts_pm_ops,此结构体进行电源管理,唤醒和休眠,如下:
static const struct dev_pm_ops gsl2680_ts_pm_ops = {
.suspend = gsl_ts_suspend, //休眠函数,拉低shutdown的引脚,供给低电压
.resume = gsl_ts_resume, //唤醒函数
};
下面是休眠函数
static int gsl_ts_suspend(struct device *dev)
{
...
struct gsl_ts *ts = dev_get_drvdata(dev);
...
gsl_halt_flag = 1;
enable_irq(ts->irq); //当进入休眠的时候,为了获取双击坐标,要先打开中断
//disable_irq_nosync(ts->irq); 去掉关中断
//gslX680_shutdown_low(); 去掉拉低电源,其函数如下
return 0;
}
/************************************************
Description : Put the shutdown(Reset) on Lower Voltage;
Input : None
Return Value : return 0
************************************************/
static int gslX680_shutdown_low(void)
{
gpio_direction_output(ts_globe->reset_gpio, 0); //
return 0;
}
在设备树msm8916-mtp.dtsi中找到
goodix,reset-gpio = <&msm_gpio 12 0x0>; 12号是shutdown引脚,同时可以去原理图中得到验证
3.注册键值
首先在static int gsl_ts_init_ts函数中,注册键值,在probe函数中会调用的函数。
注册语句:input_set_capability(ts->input, EV_KEY, KEY_POWER);
4.上报KEY_POWER键值
上报键值的位置:在中断处理函数的下半段中,进行判断,上报KEY_POWER键值或者进行正常上报
INIT_WORK(&ts->work, gsl_ts_xy_worker); //下半段函数为gsl_ts_xy_worker
#include //用前后两次jiffies来判断是否双击
static unsigned long old = 0; //记录上一次jiffies
static void gsl_ts_xy_worker(struct work_struct *work)
{
int rc;
u8 read_buf[4] = { 0 };
struct gsl_ts *ts = container_of(work, struct gsl_ts, work);
...
if(gsl_halt_flag == 1) //进入了休眠模式
{
if(old == 0) //old==0 为第一次触摸
{
old = jiffies;
return;
}
//前后两次触摸在20个时钟中断到80个时钟中断之间,说明双击,上报键值
//1s发生1HZ个时钟中断,此系统1HZ==100
else if((old != 0)&& ((jiffies-old) < 80) && ((jiffies-old) > 20))
{
gsl_halt_flag = 0; //休眠标志置为0
old = 0; //old也置为0
//注意:上报KEY_POWER键值要上报两次,第一次键值为1,第二次键为0
/*因为是报告键值是虚拟,所以必须报1,0两次,如果只报1,会被认为KEY_POWER一直按下没有放开,导致input只上报KEY_POWER不成功。*/
input_report_key(ts->input,KEY_POWER, 1);
input_sync(ts->input);
input_report_key(ts->input,KEY_POWER, 0);
input_sync(ts->input);
}
//超过了80个时钟中断,第二次按下无效,old清0
else if((old != 0)&&((jiffies-old) >= 80))
{
old = 0;
}
enable_irq(ts->irq);
return;
}
...
如果不是进入休眠模式,则进行正常的上报
}
5.测试时,发现如下bug:插上usb时可以进行双击唤醒,正常情况(无usb)不能进行双击唤醒,下面解bug
5.1猜想1,认为是tp在无usb插入的时候,一段时间过后tp关闭
让tp一直打开,方法:去电源的设备树中进行设置
在msm8916-regulator.dtsi中的两个regulator中加入regulator-always-on;
之所以是两个,是因为在驱动代码中发现了的gsl_power_on函数中,regulator_enable(data->vdd);和 regulator_enable(data->vcc_i2c);
rpm-regulator-ldoa6 {
status = "okay";
pm8916_l6: regulator-l6 {
...
regulator-always-on;
};
};
rpm-regulator-ldoa17 {
status = "okay";
pm8916_l17: regulator-l17 {
...
regulator-always-on;
};
};
结果:tp正常唤醒速度加快,因为tp一直打开,但是仍然不能解决bug
5.2打印无usb的灭屏log发现:
Freezing of tasks aborted after 0.006 seconds (50 tasks refusing to freeze, wq_busy=0): //说明进入了深度睡眠,冻结了许多内核线程
//打开log,发现的确是进入了kernle\power下的Suspend.c函数,电源挂起了
[ 182.810610] BMS: report_vm_bms_soc: ============last_soc=92 calculated_soc=92 soc=92 time_since_last_change=20
[ 185.261140] pm_suspend^^^^^^^^^^^^^^
[ 185.283804] enter_state------PM: Preparing system for mem sleep
[ 185.306882] suspend_prepare^^^^^^^^^^^^^^
[ 185.311425] freeze_processes^^^^^^^^^^^^^^
[ 185.317467] try_to_freeze_tasks^^^^^^^^^^^^^^
[ 185.323754] Freezing of tasks aborted after 0.006 seconds (50 tasks refusing to freeze, wq_busy=0):
[ 185.341550] pm_suspend^^^^^^^^^^^^^^
[ 185.352996] enter_state------PM: Preparing system for mem sleep
[ 185.360836] suspend_prepare^^^^^^^^^^^^^^
[ 185.365439] freeze_processes^^^^^^^^^^^^^^
[ 185.371014] try_to_freeze_tasks^^^^^^^^^^^^^^
[ 185.379308] suspend_freeze_processes^^^^^^^^^^^^^^
[ 185.384690] freeze_kernel_threads^^^^^^^^^^^^^^
[ 185.390697] try_to_freeze_tasks^^^^^^^^^^^^^^
//进入Suspend.c,屏蔽电源挂起相关代码
int pm_suspend(suspend_state_t state)
{
...
pr_err("pm_suspend-------------\n");
error = enter_state(state);
...
}
//进入enter_state(state);
static int enter_state(suspend_state_t state)
{
int error;
if(0){//屏蔽代码
...
}
error = 0;
return error;
6.bug暂时解决,但是会造成耗电多,因为进入深度睡眠的时候没有冻结许多线程,同时没进行消抖处理
7.下篇继续研究,优化
now,我想说
学习辛苦了,现在不妨换换思路,瞧点文学东西,
如果你喜欢,聊历史,思哲学,品诗集,赏国学。
那就关注公众号:二校五叔
这个是博主的文学公众号啦^ _ ^