Touchpanel模拟P-sensor功能简述
1.功能概述
该功能用于低端机上为降低成本而设计,利用触摸屏的感应功能,模拟p-sensor在通话时候能根据距离远近来灭屏亮屏的一种功能,从功能性角度上来说可以替代p-sensor在通话过程中防止误触的操作。但从实际意义上说,该种功能是无法代替真实的物理sensor的,无论是灵敏度还是准确性。并且会需要touchpanel牺牲灵敏度去增强感应量来达到该功能的目的。
2.实现原理
感应距离:
固件软件支持的功能:
2.框架理解
从框架角度来讲,该功能也是利用平台本身的框架去完成的。在touchpanel的driver中新建一个虚拟的sensor,在suspend/resume和interrupt等情况下完成sensor该做的操作,利用一个变量来标志在各个阶段下是应该灭屏还是亮屏,然后上报事件到对应sensor上层接口。整体来说,上层的框架都没有改变,只是需要在touchpanel的driver中新建虚拟设备,完成特殊的上报,并去上层对接。
虽然对上层的框架没有做调整或者优化,但对上层的一些处理和流程,对该功能的开发也是十分有帮助的。尤其是在和driver层交接数据的接口等。
3.Sensor的上层以及HAL
Sensor关灭屏的应用层及client,server等基本都是android原生的,高通和MTK没有多大差别。主要的区别在与hal层的不同。高通的sensor架构 方面,没有MTK的hwm或者alsps在驱动层去管理各个sensor。而是由hal层直接访问驱动层的enable节点。MTK会有一个hwmsen的软件设备创建,然后用它去连接新增的虚拟sensor的接口,下面软件向上报enable或者disable,上层继续沿用以前的框架。
而Qcom是新建一个节点enable,在其节点中写1和0来表示是否需要灭屏,在sys/device/event下回有设备enable/disable来表示有无元使能。
4.HAL层大体流程如下:
1.sensors.cpp创建NativeSensorManager,NativeSensorManager维护一个list并通过getDataInfo找到驱动的设备,并创建各个sensor的driver对象,如:new ProximitySensor(list);。
2.其中,getDataInfo通过getSensorListInner扫描获取/sys/class/sensors/下 面的sensor节点等信息添加到list。在上层enable时,通过list调用各个sensor的driver对象的方法进行 enable,在sensor driver对象的enable方法里,会在找到的sensor节点后面的enable等节点进行写来调用驱动的enable。
3.而input节点在getSensorListInner中通过getEventPathOld扫描/dev/input下 面的节点,并通过name匹配获取各个sensor对应的input节点路径。这样上层读取数据时就可以通过各自的方法,访问各自的input节 点,获取数据。
4.这样的话,驱动会比较乱,没有统一的方法与hal层链接,因此高通提供了sensors_classdev_register函 数来供驱动调用创建固定的enable等节点,供hal层访问。而input和MTK 相同,采用linux的input_register_device进行注册即可在/dev/input下面创建对应的input节点供hal层访问。
5.因此,主要的驱动修改可以参考平台里面的例子,主要在probe里面通过sensors_classdev_register创 建/sys/class/sensors/下面的sensor节点,及input_register_device进行/dev/input下面的input创建。
6.MTK的话就还需要再依附alsps和hwm来进行sensor的注册和管理
5.底层驱动原理
在驱动层方面,首先要注册一个虚拟sensor的时候首先是需要一个cdev的结构体来列举在该sensor节点下面所有的属性和参数,该结构标准最好按照物理真实sensor来操作,这样的话,以便HAL的调用和管理。如下
struct virtualpsensor {
char const *name;
// struct i2c_client *client;
struct input_dev *virtualdevice;
bool vps_enabled;
struct sensors_classdev vps_cdev;
bool virtual_proximity_data;
};
struct sensors_classdev virtual_sensors_proximity_cdev = {
.name = VPS_NAME,
.vendor = "NULL",
.version = 1,
.handle = SENSORS_PROXIMITY_HANDLE,
.type = SENSOR_TYPE_PROXIMITY,
.max_range = "5",
.resolution = "5.0",
.sensor_power = "3",
.min_delay = 0, /* in microseconds */
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
.enabled = 0,
.delay_msec = 100,
.sensors_enable = NULL,
.sensors_poll_delay = NULL,
};
然后是将其enable,详细代码不再赘述,如有兴趣请参考pixi4-4.5 TF ATT的两块TP driver中vps_set_enable()。下一步是分析register,首先需要明白是需要register一个虚拟sensor和一个input_device。那么需要分配输入设备的内存,且先register input device。再声明上报事件和事件上报的范围。调用cdev和enable,同理还可以设置cdev中其余参数,如sensors_poll_delay = NULL。然后使用<linux/sensors>中的sensors_classdev_register()来注册虚拟sensor。如下:
int virtual_psensor_input_register(struct i2c_client *pClient)
{
s32 nRetVal = 0;
pr_err("*** %s() ***\n", __func__);
vps = kzalloc(sizeof(struct virtualpsensor), GFP_KERNEL);
// vps->client = pClient;
// i2c_set_clientdata(pClient, vps);
vps->virtualdevice= input_allocate_device();
if (vps->virtualdevice == NULL)
{
pr_err("*** input device allocation failed ***\n");
return -ENOMEM;
}
vps->virtualdevice->name = "proximity";
vps->virtualdevice->id.bustype = BUS_I2C;
/* set the supported event type for input device */
set_bit(EV_ABS, vps->virtualdevice->evbit);
set_bit(ABS_DISTANCE, vps->virtualdevice->absbit);
input_set_abs_params(vps->virtualdevice, ABS_DISTANCE, 0, 1, 0, 0);
nRetVal = input_register_device(vps->virtualdevice);
if (nRetVal < 0)
{
pr_err("*** Unable to register virtual P-sensor input device ***\n");
return nRetVal;
}
vps->vps_cdev = virtual_sensors_proximity_cdev;
vps->vps_cdev.sensors_enable = vps_set_enable;
vps->vps_cdev.sensors_poll_delay = NULL;
nRetVal = sensors_classdev_register(&pClient->dev, &vps->vps_cdev);
if (nRetVal) {
pr_err("%s: Unable to register to sensors class: %d\n",__func__, nRetVal);
return nRetVal;
}
return 0;
}
该段register的函数放在probe中,建议register device后面,并按照register的顺序,在remove()中也相应的添加unregister以便注销。至此,虚拟的sensor设备就应该能在对应的input设备和节点中能够生成。那么接下来需要具体对该sensor的功能做设计。首先必须将有一个变量(tpd_proximity_ft6336_far)来辨别fay away 和close to两种状态。并在interrupt中添加相应的事件和上报值。以便hal接收。如下:
if(tpd_proximity_ft6336_enable)
{
reg_addr = FT_FACE_DETECT_REG;
if( ft6x06_read_reg(data->client, reg_addr, ®_value) < 0)
{
dev_err(&data->client->dev, "%s get face detect enable information failed.\n",
__func__);
}
else
{
if(!(reg_value&0x01))
{
vps_set_enable(&virtual_sensors_proximity_cdev,1);
}
is_face_detect = buf[FT_FACE_DETECT_POS] & 0xE0;
if(is_face_detect==0xC0)
{
tpd_proximity_ft6336_far = 0;
}
else if(is_face_detect==0xE0)
{
tpd_proximity_ft6336_far = 1;
}
if(tpd_proximity_ft6336_last_far != tpd_proximity_ft6336_far)
{
printk("tpd_virtual_sensors_proximity = %d (0:near / 1:far) \n",tpd_proximity_ft6336_far);
}
input_report_abs(vps->virtualdevice, ABS_DISTANCE, tpd_proximity_ft6336_far);
input_sync(vps->virtualdevice);
tpd_proximity_ft6336_last_far = tpd_proximity_ft6336_far;
}
对变量tpd_proximity_ft6336_far的操作,存在几个函数之前,如suspend,resume。
例如在suspend的时候,将判断是否enable该功能,然后enable中断,将suspend作为一个特殊的变量来标志。因为在resume的时候再判断是否处于这种特殊状态进入suspend的情况。如下例代码:
if(tpd_proximity_ft6336_enable)
{
err = enable_irq_wake(ft_g_client->irq);
if (err) {
pr_err("[ft6xx6] enable_irq_wake failed.\n");
}
if (device_may_wakeup(&ft_g_client->dev))
{
err = enable_irq_wake(data->client->irq);
if (err)
{
printk("%s: enable_irq_wake(%d) failed, err=(%d)\n", __func__, data->client->irq, err);
}
else
{
printk("%s: not support wakeup source", __func__);
}
}
tpd_proximity_ft6336_suspend= 1;
printk("== virtual proximity %s() ==\n",__func__);
return 0;
if(tpd_proximity_ft6336_enable)
{
err = disable_irq_wake(ft_g_client->irq);
if (err) {
pr_err("[ft6xx6] disable_irq_wake failed.\n");
}
if (device_may_wakeup(&ft_g_client->dev))
{
err = disable_irq_wake(data->client->irq);
if (err)
{
printk("%s: disable_irq_wake(%d) failed, err=(%d)\n", __func__, data->client->irq, err);
}
else
{
printk("%s: not support wakeup source", __func__);
}
}
if(tpd_proximity_ft6336_suspend == 1)
{
tpd_proximity_ft6336_suspend = 0;
printk("== virtual proximity %s() ==\n",__func__);
return 0;
另外,因上层会有特定的server,在通话等界面会讲enable节点写1,即开始enable虚拟sensor的这套判定机制。那么就存在着对enable节点权限的关系,如果权限不适配的话也可以能导致功能无法ok。正常情况可设置为system root 0664,详细情况可以检查enable没变值之后手动改变节点尝试。
该功能软件设计建议
由于模拟P-Sensor是需要CTP来提供接近或远离的状态,因此在启动模拟P-Sensor功能前,需要确保CTP已经被唤醒。否则模拟P-Sensor将由于获取不到CTP的数据而失效:
if(enable){
TPD_DEBUG("[MSG2133A]Beforethe PS enable wake up the CTP\n");
mt_set_gpio_mode(GPIO_CTP_EN_PIN,GPIO_CTP_EN_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_EN_PIN,GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_EN_PIN,GPIO_OUT_ONE);
mt_set_gpio_out(GPIO_CTP_EN_PIN,GPIO_OUT_ZERO);
msleep(10);
mt_set_gpio_out(GPIO_CTP_EN_PIN,GPIO_OUT_ONE);
msleep(300);
mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
dbbus_tx_data[0] =0x52; dbbus_tx_data[1] =0x00;
dbbus_tx_data[2] =0x4A; dbbus_tx_data[3] =0xA0;
err =i2c_master_send(i2c_clientma, &dbbus_tx_data[0],4);
if(err>=0){
TPD_DMESG(“ ctppsopen OK\n");
模拟P-Sensor是需要CTP来提供接近或远离状态的,因此在启动模拟P-Sensor功能后,CTP需要一直
处于唤醒状态:
static int mstar2133_pls_open(struct inode *inode, struct file*file)
{
mstar2133_pls_opened = 1;
…
}
static int mstar2133_pls_release(struct inode *inode, struct file*file)
{
mstar2133_pls_opened = 0;
…
}
static int tpd_suspend(structi2c_client *client, pm_message_tmessage)
{
if(mstar2133_pls_opened)
{
printk("==%s==mstar2133_pls_opened ! return \n", __func__);
return;
}
…
}
if(ret>=0) {
if((ps_data&0xf0)==FACE_DETECT_NEAR)
{
fps_state=0;
sensor_data.values[0] =fps_state;
}
elseif((ps_data&0xf0)==FACE_DETECT_FAR)
{
fps_state=1;
sensor_data.values[0] =fps_state;
}
else //phone closed
fps_state=-1;
sensor_data.value_divide = 1;
sensor_data.status =SENSOR_STATUS_ACCURACY_MEDIUM;
if((ret=hwmsen_get_interrupt_data(ID_PROXIMITY,&sensor_data)))
{
TPD_DMESG("callhwmsen_get_interrupt_data fail= %d\n", ret);
}
}
如此,每当启动模拟Psensor功能时,状态改变量直接由中断途径通知上层。
static void _HalTscrHWReset(void)
{
printk("%s\n",__func__);
mutex_lock(&i2c_transfer_mutex);//------------在进行resetCTP之前进行加锁操作
mt_set_gpio_mode(GPIO_CTP_EN_PIN,GPIO_CTP_EN_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_EN_PIN,GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_EN_PIN,GPIO_OUT_ONE);
mdelay(2);
mt_set_gpio_out(GPIO_CTP_EN_PIN,GPIO_OUT_ZERO);
mdelay(10);
mt_set_gpio_out(GPIO_CTP_EN_PIN,GPIO_OUT_ONE);
mdelay(300);
#ifdefCTP_PSENSOR_SUPPORT
msg2133_tp_resume_flag=0;
#endif
mutex_unlock(&i2c_transfer_mutex);------------在resetCTP完成之后释放锁
}
static int msg2133_ps_mode_enable(boolenable)
{
mutex_lock(&i2c_transfer_mutex);//--------------------------在进行psenable之前加锁
msg2133_tp_resume_flag=0;
if(enable)
{
…..
err =i2c_master_send(i2c_clientma, &dbbus_tx_data[0],4);//打开模拟Psensor的功能操作
}
mutex_unlock(&i2c_transfer_mutex);//---------------------在psenable结束之后释放锁
return 0;
}
如此,每当启动模拟Psensor功能都必定会成功,就不会出现模拟Psensor失效的情况了。
static void FT3407_ps_mode_enable(boolenable){
int err= 0;
unsigned char rx_data; mutex_lock(&i2c_transfer_mutex
if(enable) //enter phone open
{
err=ft3407_write_reg(0xB0,0x01);
err=ft3407_read_reg(0xB0,&rx_data);
if(!(err>=0&& ((rx_data&0x01)==0x01)))
{
mutex_unlock(&i2c_transfer_mutex);
Ft3407_tp_reset(); mutex_lock(&i2c_transfer_mutex);
err= ft3407_write_reg(0xB0,0x01); err=ft3407_read_reg(0xB0,&rx_data);