需求:某些客戶為了節約成本吧,将距離傳感器拿掉,相應的功能用TP觸摸屏來模拟實作。流程圖如下

移植原理:去掉手機上的感光Sensor,用TP來模拟實作感光sensor在通話時亮屏和滅屏的功能。
當然了TP本身是需要支援相應的功能的。可以聯系模組廠進行調試,導入firmware,然後才在代碼中進行相應的修改。
1. 首先在目錄/sys/bus/i2c/devices下添加相應的I2C裝置,在ProximitySensor.cpp中添加:
//the below characters is the origin codes,modified to make the JNI work corretly
#define ENALBE_PS_SENSOR "/sys/bus/i2c/devices/i2c-2/2-0038/enable_ps_sensor"
2. 在sensor.cpp中取代感光sensor
修改原則是用模拟距離傳感器的代碼替換原先感光sensor的代碼。
在sensor清單中添加支援模拟距離傳感器的子產品代碼,以敦太的ft5306為例。
static const struct sensor_t sSensorList[] ={
。。。
。。。};
接下來是聲明,替換感光sensor的相關部分。
3. 加入編譯選項,在Android.mk.3rdparty
sensors.cpp \
4. 在tp驅動裡加入代碼
先在代碼裡聲明個宏開關吧
#define TP_PROXIMITY_SENSOR
在結構體中添加必要的成員變量
struct pixcir_i2c_ts_data {
struct i2c_client *client;
struct input_dev *input;
struct ts_event event;
//const struct pixcir_ts_platform_data *chip;
bool exiting;
#ifdef TP_PROXIMITY_SENSOR
//struct mutex update_lock;
//struct delayed_work dwork;
struct input_dev *input_dev_ps;
unsigned int enable;
unsigned int control;
unsigned int enable_ps_sensor;
unsigned int ps_detection;
//unsigned int ps_data;
#endif
};
建立sysfs接口,用于hal層調用
static DEVICE_ATTR(enable_ps_sensor, S_IWUGO | S_IRUGO,
ft5306_show_enable_ps_sensor, ft5306_store_enable_ps_sensor);
static struct attribute *ft5306_attributes[] = {
&dev_attr_enable_ps_sensor.attr,
NULL
};
當你想要實作的接口名字是enable_ps_sensor的時候,需要實作結構體struct attribute *dev_attrs[]
其中成員變量的名字必須是&dev_attr_enable_ps_sensor.attr,然後再封裝
static const struct attribute_group ft5306_attr_group = {
.attrs = ft5306_attributes,
};
最後就可以建立接口了,這個是在probe裡面建立的哦
#ifdef TP_PROXIMITY_SENSOR
error = sysfs_create_group(&client->dev.kobj, &ft5306_attr_group);
if (error)
input_unregister_device(ps_input);
#endif
這裡隻是建立了android到kernel的橋梁,真正實作對硬體操作的還是show和store兩個函數。
#ifdef TP_PROXIMITY_SENSOR
static ssize_t ft5306_show_enable_ps_sensor(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct pixcir_i2c_ts_data *data = i2c_get_clientdata(client);
return sprintf(buf, "%d\n", data->enable_ps_sensor);
}
static ssize_t ft5306_store_enable_ps_sensor(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct pixcir_i2c_ts_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
unsigned long flags;
int err;
char data_cmd[2]={0, 0};
printk("%s: enable ps senosr ( %ld)\n", __func__, val);
if ((val != 0) && (val != 1)) {
printk("%s:store unvalid value=%ld\n", __func__, val);
return count;
}
if(val == 1) {
rgt_ps_mode = true;
//turn on p sensor
if (data->enable_ps_sensor==0) {
data->enable_ps_sensor= 1;
err = pixcir_i2c_write_data(0xB0, 0x01);
if(err==0)
printk("tp_ps: i2c write sucess, err:%d\n", err);
//}
else
printk("tp_ps: i2c write fail, err:%d\n", err);
}
}
else {
//turn off p sensor - kk 25 Apr 2011 we can't turn off the entire sensor, the light sensor may be needed by HAL
rgt_ps_mode=false;
data->enable_ps_sensor = 0;
err=pixcir_i2c_txdata(0xB0, 0x00);//out ps mode
if(err==0)
//{
printk("tp_ps: i2c write sucess\n");
//}
else
printk("tp_ps: i2c write fail\n");
}
return count;
}
接下來在probe裡各種添加了
static int __devinit pixcir_i2c_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
//const struct pixcir_ts_platform_data *pdata = client->dev.platform_data;
struct pixcir_i2c_ts_data *tsdata;
struct input_dev *input;
struct input_dev *ps_input;
struct device *dev;
struct i2c_dev *i2c_dev;
int i, error;
this_client = client;
client->irq = pixcir_ts_config_pins(); //reset pin set to 0 or 1 and platform init
for(i=0; i<MAX_FINGER_NUM*2; i++) {
point_slot[i].active = 0;
}
tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
input = input_allocate_device();
#ifdef TP_PROXIMITY_SENSOR
ps_input = input_allocate_device();
#endif
if (!tsdata || !input || !ps_input) {
dev_err(&client->dev, "Failed to allocate driver data!\n");
error = -ENOMEM;
goto err_free_mem;
}
#ifdef TOUCH_VIRTUAL_KEYS
pixcir_ts_virtual_keys_init();
#endif
tsdata->client = client;
tsdata->input = input;
#ifdef TP_PROXIMITY_SENSOR
tsdata->input_dev_ps = ps_input;
#endif
//tsdata->chip = pdata;
global_irq = client->irq;
input->name = client->name;
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
#ifdef TP_PROXIMITY_SENSOR
ps_input->name = "FTPS";
ps_input->id.bustype = BUS_I2C;
#endif
__set_bit(EV_KEY, input->evbit);
__set_bit(EV_ABS, input->evbit);
__set_bit(EV_SYN, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
#ifdef TP_PROXIMITY_SENSOR
__set_bit(EV_ABS, ps_input->evbit);
#endif
__set_bit(ABS_MT_TOUCH_MAJOR, input->absbit);
__set_bit(ABS_MT_POSITION_X, input->absbit);
__set_bit(ABS_MT_POSITION_Y, input->absbit);
__set_bit(ABS_MT_WIDTH_MAJOR, input->absbit);
__set_bit(KEY_MENU, input->keybit);
__set_bit(KEY_BACK, input->keybit);
//__set_bit(KEY_HOME, input->keybit);
//__set_bit(KEY_SEARCH, input->keybit);
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_X, 0, X_MAX, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, Y_MAX, 0, 0);
input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
#ifdef TP_PROXIMITY_SENSOR
input_set_abs_params(ps_input, ABS_DISTANCE, 0, 1, 0, 0);
#endif
input_set_drvdata(input, tsdata);
error = request_threaded_irq(client->irq, NULL, pixcir_ts_isr,
IRQF_TRIGGER_FALLING,
client->name, tsdata);
if (error) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
goto err_free_mem;
}
disable_irq_nosync(client->irq);
error = input_register_device(input);
if (error)
goto err_free_irq;
#ifdef TP_PROXIMITY_SENSOR
error = input_register_device(ps_input);
if (error)
goto err_free_irq;
#endif
i2c_set_clientdata(client, tsdata);
device_init_wakeup(&client->dev, 1);
i2c_dev = get_free_i2c_dev(client->adapter);
if (IS_ERR(i2c_dev)) {
error = PTR_ERR(i2c_dev);
return error;
}
//hong 找到了,上述說的就是在這裡的。
#ifdef TP_PROXIMITY_SENSOR
error = sysfs_create_group(&client->dev.kobj, &ft5306_attr_group);
if (error)
input_unregister_device(ps_input);
#endif
dev = device_create(i2c_dev_class, &client->adapter->dev, MKDEV(I2C_MAJOR,
client->adapter->nr), NULL, "ft5206_ts%d", 0);
if (IS_ERR(dev)) {
error = PTR_ERR(dev);
return error;
}
pixcir_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
pixcir_early_suspend.suspend = pixcir_ts_suspend;
pixcir_early_suspend.resume = pixcir_ts_resume;
register_early_suspend(&pixcir_early_suspend);
if(pixcir_config_intmode()<0) {
#ifdef PIXCIR_DEBUG
printk("%s: I2C error\n",__func__);
#endif
goto err_free_irq;
}
pixcir_create_sysfs(client);
#ifdef PIXCIR_DEBUG
dev_err(&tsdata->client->dev, "insmod successfully!\n");
#endif
enable_irq(client->irq);
msleep(100);
pixcir_i2c_write_data(0x80, 0x10);
#if 0//def TP_PROXIMITY_SENSOR
//struct ft5306_ps_data ps_data;
int err;
ps_data->enable = 0;
ps_data->detection= 0;
ps_data->enable_ps_sensor = 0;
if(err = hwmsen_attach(ID_PROXIMITY, &tp_ps))
{
printk("tp_ps:attach fail = %d\n", err);
//goto exit_create_attr_failed;
}
data->enable_ps_sensor = 0;
#endif
return 0;
err_free_irq:
free_irq(client->irq, tsdata);
sprd_free_gpio_irq(pixcir_irq);
err_free_mem:
input_free_device(input);
kfree(tsdata);
return error;
}
中斷read資料,打電話時模拟距離控制屏的休眠和喚醒
static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
{
struct pixcir_i2c_ts_data *tsdata = dev_id;
int ret;
disable_irq_nosync(irq);
ret = ft5x0x_read_data(tsdata);
if (ret == 0) {
ft5x0x_report_value(tsdata);
}
enable_irq(irq);
return IRQ_HANDLED;
}
static int ft5x0x_read_data(struct pixcir_i2c_ts_data *data)
{
// struct pixcir_i2c_ts_data *data = i2c_get_clientdata(this_client);
struct ts_event *event = &data->event;
// u8 buf[14] = {0};
u8 buf[32] = {0};
int ret = -1;
int touch_point = 0;
//add detect function by liuhui 20120530. start
#ifdef TP_PROXIMITY_SENSOR
int err;
u8 buf_ps[2] = {0};
if(rgt_ps_mode == true)
{
err=pixcir_i2c_rxdata(&buf_ps, 2);
if(err==1)
printk("tp_ps: i2c read sucess\n");
else
printk("tp_ps: i2c read fail, err = %d\n", err);
printk("tp_ps:ps data:%d\n", buf_ps[1]);
if(buf_ps[1]==0xc0)//close
{
data->ps_detection = 1;
input_report_abs(data->input_dev_ps, ABS_DISTANCE, 1);
input_sync(data->input_dev_ps);
return 0;
}
else if(buf_ps[1]==0xe0)//far away
{
data->ps_detection = 0;
input_report_abs(data->input_dev_ps, ABS_DISTANCE, 0);
input_sync(data->input_dev_ps);
//return 0;
}
}
#endif
。。。
}
suspend休眠
static void pixcir_ts_suspend(struct early_suspend *handler)
{
if(rgt_ps_mode==true)
return -1;
disable_irq_nosync(global_irq);
pixcir_ts_pwroff();
}
resume喚醒
static void pixcir_ts_resume(struct early_suspend *handler)
{
int ret = 0;
unsigned char reg_val[8] = {0};
struct i2c_client *client = this_client;
if(rgt_ps_mode==true)
return -1;
pixcir_ts_pwron();
pixcir_reset();
msleep(50);
pixcir_i2c_write_data(0x80, 0x12);
enable_irq(global_irq);
}
到此,整個移植也算結束了。
5.關于bug
這個在調試的過程中還得和模組廠的FAE們,一起掌控TP的靈敏度,這個要多次反複的實驗才行。
做了很多專項測試,依照上面的代碼來看,當打電話的時候離開人臉,有時會出現喚不醒螢幕的問題,手動喚醒後,GOD,TP居然失效了,
這是難以接受的。這就是模拟的缺陷了,經過很多調試才避免了這種TP失效的發生,當然了偶爾還是出現自動喚不醒的情況。具體的優化,在
下一章中會詳細講到,ps 解決這個bug,我搞了兩天。。。
事情到此結束了?有興趣的話,可以考慮下為什麼在pixcir_ts_resume中加入rgt_ps_mode==true的判斷,當時偶也是很迷惑的。
最終,還是有别的隐性bug的,但目前就優化到這裡。可能在你看來還是有很多問題,不妨給點建議。
ps:第一次在csdn上些東西,排版好難整啊。。