触摸按键文件 /kernel/driver/input/touchscreen/ft6x0x_ts.c
针对触摸屏按键驱动,我们来做一些简单的分析和了解。
static int
ft6x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
// 探测函数
{
struct jztsc_platform_data *pdata = NULL;
struct input_dev *input_dev;
int err = 0;
uint8_t uc_reg_value, uc_reg_addr;
pdata = (struct jztsc_platform_data *)client->dev.platform_data;
// 获取I2C客户端数据
if (!pdata) {
dev_info(&client->dev, "ERROR : %s --> platform data is NULL! will exit!\n",__func__);
err = -EINVAL;
goto
exit_pdata_is_null;
}
if (!
i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
// 判定适配器功能
err = -ENODEV;
goto
exit_check_functionality_failed;
}
ft6x0x_ts =
kzalloc(sizeof(struct ft6x0x_ts_data), GFP_KERNEL);
// 申请触摸屏按键内存空间
if (!ft6x0x_ts) {
err = -ENOMEM;
goto
exit_alloc_data_failed;
}
ft6x0x_ts->pdata = pdata;
// 设置触摸屏按键数据
ft6x0x_gpio_init(ft6x0x_ts, client);
// 初始化引脚功能
i2c_set_clientdata(client, ft6x0x_ts);
// 设置 I2C 数据
ft6x0x_ts->power =
regulator_get(&client->dev, "
vtsc");
// 更改电源输入引脚
if (IS_ERR(ft6x0x_ts->power)) {
dev_warn(&client->dev, "get regulator vtsc failed, try board power interface.\n");
if (pdata->power_init) {
pdata->
power_init(&client->dev);
} else {
dev_warn(&client->dev, "board power control interface is NULL !\n");
}
}
atomic_set(&ft6x0x_ts->regulator_enabled, 0);
//使能校准器
ft6x0x_ts_power_on(ft6x0x_ts);
// 打开电源
/*make sure CTP already finish startup process */
ft6x0x_ts_reset(ft6x0x_ts);
//重新设置引脚状态
msleep(100);
/*get some register information */
uc_reg_addr = FT6X0X_REG_FW_VER;
err =
ft6x0x_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1);
// 接收客户端信息
if(err < 0){
dev_info(&client->dev, "ft6x0x_ts probe failed\n");
goto exit_read_reg_failed;
}
#if defined(CONFIG_FT6X0X_EXT_FUNC)
err =
fts_ctpm_auto_upgrade(client);
// 自动更新客户端数据
if (err < 0) {
dev_info(&client->dev, "fts_ctpm_auto_upgrade return %d\n",err);
}
#endif
dev_dbg(&client->dev, "[FTS] Firmware version = 0x%x\n", uc_reg_value);
uc_reg_addr = FT6X0X_REG_POINT_RATE;
err =
ft6x0x_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1);
// 接收客户端 数据
if(err < 0){
dev_info(&client->dev, "ft6x0x_ts probe failed\n");
goto exit_read_reg_failed;
}
dev_dbg(&client->dev, "[FTS] report rate is %dHz.\n",
uc_reg_value * 10);
uc_reg_addr = FT6X0X_REG_THGROUP;
err =
ft6x0x_i2c_Read(client, &uc_reg_addr, 1, &uc_reg_value, 1);
// 接收客户端 数据
if(err < 0){
dev_info(&client->dev, "ft6x0x_ts probe failed\n");
goto exit_read_reg_failed;
}
dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n",
uc_reg_value * 4);
// ft6x0x_set_reg(ft6x0x_ts, FT6X0X_REG_THCAL, 4);
mutex_init(&ft6x0x_ts->lock);
mutex_init(&ft6x0x_ts->rwlock);
//初始化客户端设备名
client->dev.init_name=client->name;
#ifndef FTTP_THREAD_MODE
INIT_WORK(&ft6x0x_ts->work,
ft6x0x_work_handler);
// 初始化工作队列
ft6x0x_ts->workqueue =
create_singlethread_workqueue("ft6x0x_tsc");
// 创建工作队列
if (!ft6x0x_ts->workqueue) {
dev_info(&client->dev, "create_singlethread_workqueue failed!\n");
goto
exit_create_singlethread_workqueue;
}
#else
ft6x0x_ts->thread =
kthread_run(
touch_event_handler, (void *)ft6x0x_ts, "ft6x0x_ts");
// 创建并启动线程
if (IS_ERR(ft6x0x_ts->thread))
goto
exit_create_singlethread_workqueue;
#endif
client->irq =
gpio_to_irq(ft6x0x_ts->gpio.irq->num);
// 将gpio_pin 映射为中断形式
// 申请中断处理函数 ft6x0x_ts_interrupt
err =
request_irq(client->irq,
ft6x0x_ts_interrupt,
IRQF_TRIGGER_FALLING | IRQF_DISABLED,
"ft6x0x_ts", ft6x0x_ts);
if (err < 0) {
dev_err(&client->dev, "ft6x0x_probe: request irq failed\n");
goto
exit_irq_request_failed;
}
//初始化相关数据
ft6x0x_ts->irq = client->irq;
ft6x0x_ts->client = client;
ft6x0x_ts->x_max = pdata->x_max;
ft6x0x_ts->y_max = pdata->y_max;
ft6x0x_ts->is_suspend = 0;
disable_irq(client->irq);
#ifdef CONFIG_KEY_SPECIAL_POWER_KEY
setup_timer(&ft6x0x_ts->tp_blk_delay,
tp_off_blk_timer,
(unsigned long)ft6x0x_ts); // 设置背光灯处理函数
#endif
input_dev =
input_allocate_device();
//分配内存空间
if (!input_dev) {
err = -ENOMEM;
dev_err(&client->dev, "failed to allocate input device\n");
goto
exit_input_dev_alloc_failed;
}
// 设置输入设备
ft6x0x_ts->input_dev = input_dev;
// 设置触摸按键支持的事件类型
// set_bit(KEY_MENU, input_dev->keybit);
set_bit(KEY_BACK, input_dev->keybit);
set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
set_bit(ABS_MT_POSITION_X, input_dev->absbit);
set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
set_bit(ABS_MT_TRACKING_ID,input_dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
// 初始化输入设备信息
input_set_abs_params(input_dev,
ABS_MT_POSITION_X, 0, ft6x0x_ts->x_max, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_POSITION_Y, 0, ft6x0x_ts->y_max, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_TOUCH_MAJOR, 0, 250, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_ABS, input_dev->evbit);
input_dev->name = FTS_NAME;
err =
input_register_device(input_dev);
// 注册输入子系统设备
if (err) {
dev_err(&client->dev,
"ft6x0x_ts_probe: failed to register input device: %s\n",
dev_name(&client->dev));
goto
exit_input_register_device_failed;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
ft6x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ft6x0x_ts->early_suspend.suspend =
ft6x0x_ts_suspend;
ft6x0x_ts->early_suspend.resume =
ft6x0x_ts_resume;
register_early_suspend(&ft6x0x_ts->early_suspend);
#endif
#if defined(CONFIG_FT6X0X_EXT_FUNC)
ft6x0x_create_sysfs(client);
// 创建文件系统
#endif
enable_irq(client->irq);
// 使能中断
return 0;
exit_input_register_device_failed:
input_free_device(input_dev);
// 释放设备
exit_input_dev_alloc_failed:
free_irq(client->irq, ft6x0x_ts);
// 释放中断
exit_irq_request_failed:
#ifndef FTTP_THREAD_MODE
destroy_workqueue(ft6x0x_ts->workqueue);
// 销毁工作队列
#else
kthread_stop(ft6x0x_ts->thread);
// 结束线程运行
#endif
exit_read_reg_failed:
exit_create_singlethread_workqueue:
ft6x0x_ts_power_off(ft6x0x_ts);
// 关闭电源供电
if (!IS_ERR(ft6x0x_ts->power))
regulator_put(ft6x0x_ts->power);
// 释放校准器资源
gpio_free(ft6x0x_ts->gpio.irq->num);
// 释放gpio中断端
gpio_free(ft6x0x_ts->gpio.wake->num);
//释放gpio 唤醒端
i2c_set_clientdata(client, NULL);
//将I2C客户端数据清空
kfree(ft6x0x_ts);
// 释放触摸按键内存空间
exit_alloc_data_failed:
exit_check_functionality_failed:
exit_pdata_is_null:
return err;
}
探测函数主要做一些驱动的初始化工作,固件自动更新的处理、初始化工作队列、触摸按键事件的处理、中断的处理和背光的处理等工作,下面来分别介绍这些部分的功能。
下面我们了解一下它的核心部分,固件自动更新部分的代码;
int
fts_ctpm_auto_upgrade(struct i2c_client *client)
{
int i_ret;
u8 uc_host_fm_ver = FT6x06_REG_FW_VER;
u8 uc_tp_fm_ver;
struct ft6x0x_ts_data *ts = (struct ft6x0x_ts_data *)
i2c_get_clientdata(client);
// 获得I2C 客户端数据结构体指针
if ((NULL == ts) || (0 == ts->pdata->fw_ver)) {
return -EINVAL;
}
ft6x06_read_reg(client, FT6x06_REG_FW_VER, &uc_tp_fm_ver);
// 接收客户端数据
uc_host_fm_ver =
fts_ctpm_get_i_file_ver();
//获得固件参数文件列表
if (0x00 == uc_host_fm_ver) {
return -ENOENT;
}
/*the firmware in touch panel maybe corrupted */
/*the firmware in host flash is new, need upgrade */
if (
c_tp_fm_ver == FT6x06_REG_FW_VER) ||
uc_tp_fm_ver != uc_host_fm_ver
)
{
msleep(100);
dev_dbg(&client->dev, "[FTS] uc_tp_fm_ver = 0x%x, uc_host_fm_ver = 0x%x\n",
uc_tp_fm_ver, uc_host_fm_ver);
i_ret =
fts_ctpm_fw_upgrade_with_i_file(client);
// 更新固件参数文件列表
if (i_ret == 0) {
msleep(300);
uc_host_fm_ver =
fts_ctpm_get_i_file_ver();
//获得固件参数文件列表
dev_dbg(&client->dev, "[FTS] upgrade to new version 0x%x\n",
uc_host_fm_ver);
} else {
pr_err("[FTS] upgrade failed ret=%d.\n", i_ret);
return -EIO;
}
}
dev_info(&client->dev,"OLD VERSION = 0x%x\n", uc_tp_fm_ver);
ft6x06_read_reg(client, FT6x06_REG_FW_VER, &uc_tp_fm_ver);
// 接收客户端数据
dev_info(&client->dev,"NEW VERSION = 0x%x\n", uc_tp_fm_ver);
return 0;
}
下面我们来介绍上面函数调用的几个重要的函数,来简单分析下它的功能。
首先看看
i2c_get_clientdata函数,其实它是调用
dev_get_drvdata函数,下面我们来看看它的实现代码,其实只是做了一个简单的动作。
/*
* These exports can't be _GPL due to .h files using this within them, and it
* might break something that was previously working...
*/
void *
dev_get_drvdata(const struct device *dev)
{
if (dev && dev->p)
return dev->p->driver_data;
// 该函数主要是获取驱动数据结构体指针
return NULL;
}
再来看看另外一个函数
ft6x06_read_reg其实是调用了
ft6x0x_i2c_Read函数,看看它具体做了些什么工作。
int
ft6x0x_i2c_Read(struct i2c_client *client, char *writebuf,
int writelen, char *readbuf, int readlen)
{
int ret;
struct ft6x0x_ts_data *ft6x0x_ts =
i2c_get_clientdata(client);
// 获得客户端数据结构体指针
if (writelen > 0) {
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
// 发送数据
.len = writelen,
.buf = writebuf,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
// 接收数据
.len = readlen,
.buf = readbuf,
},
};
ret =
i2c_transfer(client->adapter, msgs, 2);
// 调用平台接口读写数据
if (ret < 0){
dev_err(&client->dev, "%s: -i2c read error.\n",
__func__);
ft6x0x_ts_release(ft6x0x_ts);
// 释放按键值
ft6x0x_ts_reset(ft6x0x_ts);
// 重新启动
}
} else {
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = I2C_M_RD,
// 接收数据
.len = readlen,
.buf = readbuf,
},
};
ret =
i2c_transfer(client->adapter, msgs, 1);
// 调用平台接口接收数据
if (ret < 0){
dev_err(&client->dev, "%s:i2c read error.\n", __func__);
ft6x0x_ts_reset(ft6x0x_ts);
}
}
return ret;
}
再看看另外一个函数,它的具体实现内容。
/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
*/
/**
* i2c_transfer - execute a single or combined I2C message
* @adap: Handle to I2C bus
* @msgs: One or more messages to execute before STOP is issued to
* terminate the operation; each message begins with a START.
* @num: Number of messages to be executed.
*
* Returns negative errno, else the number of messages executed.
*
* Note that there is no requirement that each message be sent to
* the same slave address, although that is the most common model.
*/
int
i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret =
i2c_trylock_adapter(adap);
//对I2C适配器尝试加锁
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
//对I2C 适配器加锁
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
// 核心部分,提交数据给总线驱动层
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
i2c_unlock_adapter(adap);
//对I2C 适配器解锁
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
本函数主要是调用平台接口来读写I2C客户端的数据,它是读写函数的核心部分。
下面再来看看另外一个函数接口fts_ctpm_get_i_file_ver函数,看看它的功能和实现代码。
u8
fts_ctpm_get_i_file_ver(void)
// 获得固件参数表
{
u16 ui_sz;
ui_sz = sizeof(
CTPM_FW);
if (ui_sz > 2)
return
CTPM_FW[ui_sz - 2];
return 0x00;
/*default value */
}
上面函数的功能很简单就是获取CTPM_FW数组的值,下面我们来看看这个数组到底存放什么数据;
static unsigned char CTPM_FW[] = {
// 触摸按键类型的参数
#if defined(CONFIG_TOUCHSCREEN_FT5X06_TWS)
#include "TWS20176_FT5206_V08_LCD_20140821_1S_app.dat"
//根据包含的路径找的文件,打开发现其实就是触摸板的一些参数。
#elif defined(CONFIG_TOUCHSCREEN_FT6X0X_TWS)
#include "TWS20074-FT6206-v11_20140620_app.dat"
#elif defined(CONFIG_TOUCHSCREEN_FT6X0X_YIYANG_10S)
#include "IT_YY16-G1409+X1_V12_20140626_app.dat"
#elif defined(CONFIG_TOUCHSCREEN_FT6X0X_YIYANG_1S)
#include "IT_YY16_1409OGS_V22_20140913_app.dat"
#endif
};
// 固件参数表
我们再来看看另外一个自动更新固件列表的函数,看看它的具体实现和功能。
/*
upgrade with *.i file
*/
int
fts_ctpm_fw_upgrade_with_i_file(struct i2c_client *client)
// 升级固件参数表
{
u8 *pbt_buf = NULL;
int i_ret;
int fw_len = sizeof(
CTPM_FW);
/*judge the fw that will be upgraded
* if illegal, then stop upgrade and return.
*/
if (fw_len < 8 || fw_len > 32 * 1024) {
dev_err(&client->dev, "%s:FW length error\n", __func__);
return -EIO;
}
if ((CTPM_FW[fw_len - 8] ^ CTPM_FW[fw_len - 6]) == 0xFF
&& (CTPM_FW[fw_len - 7] ^ CTPM_FW[fw_len - 5]) == 0xFF
&& (CTPM_FW[fw_len - 3] ^ CTPM_FW[fw_len - 4]) == 0xFF) {
/*FW upgrade */
pbt_buf =
CTPM_FW;
/*call the upgrade function */
i_ret =
fts_ctpm_fw_upgrade(client, pbt_buf, sizeof(
CTPM_FW));
if (i_ret != 0)
dev_err(&client->dev, "%s:upgrade failed. err.\n",
__func__);
#ifdef AUTO_CLB
else
fts_ctpm_auto_clb(client);
/*start auto CLB */ 自动校准客户端
#endif
} else {
dev_err(&client->dev, "%s:FW format error\n", __func__);
return -EBADFD;
}
return i_ret;
}
我们再看上面函数调用的fts_ctpm_fw_upgrade函数,看看它的代码实现。
//upgrade firmware
int
fts_ctpm_fw_upgrade(struct i2c_client *client, u8 *pbt_buf,
u32 dw_lenth)
{
u8 reg_val[2] = {0};
u32 i = 0;
u32 packet_number;
u32 j;
u32 temp;
u32 lenght;
u8 packet_buf[FTS_PACKET_LENGTH + 6];
u8 auc_i2c_write_buf[10];
u8 bt_ecc;
int i_ret;
for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
/*********Step 1:Reset CTPM *****/
#if defined(CONFIG_TOUCHSCREEN_FT5X06_TWS)
/*write 0xaa to register 0xfc */
ft6x06_write_reg(client, 0xfc, FT_UPGRADE_AA);
msleep(FT5X06_UPGRADE_AA_DELAY);
/*write 0x55 to register 0xfc */
ft6x06_write_reg(client, 0xfc, FT_UPGRADE_55);
msleep(FT5X06_UPGRADE_55_DELAY);
#else
/*write 0xaa to register 0xbc */
ft6x06_write_reg(client, 0xbc, FT_UPGRADE_AA);
msleep(FT6X06_UPGRADE_AA_DELAY);
/*write 0x55 to register 0xbc */
ft6x06_write_reg(client, 0xbc, FT_UPGRADE_55);
msleep(FT6X06_UPGRADE_55_DELAY);
#endif
/*********Step 2:Enter upgrade mode *****/
auc_i2c_write_buf[0] = FT_UPGRADE_55;
auc_i2c_write_buf[1] = FT_UPGRADE_AA;
do {
i++;
i_ret =
ft6x0x_i2c_Write(client, auc_i2c_write_buf, 2);
msleep(5);
} while (i_ret <= 0 && i < 5);
/*********Step 3:check READ-ID***********************/
msleep(FT6X06_UPGRADE_READID_DELAY);
auc_i2c_write_buf[0] = 0x90;
auc_i2c_write_buf[1] = auc_i2c_write_buf[2] = auc_i2c_write_buf[3] =
0x00;
ft6x0x_i2c_Read(client, auc_i2c_write_buf, 4, reg_val, 2);
#if defined(CONFIG_TOUCHSCREEN_FT5X06_TWS)
if (reg_val[0] == FT5X06_UPGRADE_ID_1
&& reg_val[1] == FT5X06_UPGRADE_ID_2)
#else
if (reg_val[0] == FT6X06_UPGRADE_ID_1
&& reg_val[1] == FT6X06_UPGRADE_ID_2)
#endif
{
//dev_dbg(&client->dev, "[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",
//reg_val[0], reg_val[1]);
DBG("[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",
reg_val[0], reg_val[1]);
break;
} else {
dev_err(&client->dev, "[FTS] Step 3: CTPM ID,ID1 = 0x%x,ID2 = 0x%x\n",
reg_val[0], reg_val[1]);
}
}
if (i > FTS_UPGRADE_LOOP)
return -EIO;
auc_i2c_write_buf[0] = 0xcd;
ft6x0x_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1);
/*Step 4:erase app and panel paramenter area*/ 擦除app 和 参数
DBG("Step 4:erase app and panel paramenter area\n");
auc_i2c_write_buf[0] = 0x61;
ft6x0x_i2c_Write(client, auc_i2c_write_buf, 1);
/*erase app area */
msleep(FT6X06_UPGRADE_EARSE_DELAY);
/*erase panel parameter area */
auc_i2c_write_buf[0] = 0x63;
ft6x0x_i2c_Write(client, auc_i2c_write_buf, 1);
msleep(100);
/*********Step 5:write firmware(FW) to ctpm flash*********/
bt_ecc = 0;
DBG("Step 5:write firmware(FW) to ctpm flash\n");
dw_lenth = dw_lenth - 8;
packet_number = (dw_lenth) / FTS_PACKET_LENGTH;
packet_buf[0] = 0xbf;
packet_buf[1] = 0x00;
for (j = 0; j < packet_number; j++) {
temp = j * FTS_PACKET_LENGTH;
packet_buf[2] = (u8) (temp >> 8);
packet_buf[3] = (u8) temp;
lenght = FTS_PACKET_LENGTH;
packet_buf[4] = (u8) (lenght >> 8);
// 获得8 -15 bit 数据
packet_buf[5] = (u8) lenght;
// 获得0 - 7 bit 数据
for (i = 0; i < FTS_PACKET_LENGTH; i++) {
packet_buf[6 + i] =
pbt_buf[j * FTS_PACKET_LENGTH + i];
bt_ecc ^= packet_buf[6 + i];
}
ft6x0x_i2c_Write(client, packet_buf, FTS_PACKET_LENGTH + 6);
msleep(FTS_PACKET_LENGTH / 6 + 1);
//DBG("write bytes:0x%04x\n", (j+1) * FTS_PACKET_LENGTH);
//delay_qt_ms(FTS_PACKET_LENGTH / 6 + 1);
}
if ((dw_lenth) % FTS_PACKET_LENGTH > 0) {
temp = packet_number * FTS_PACKET_LENGTH;
packet_buf[2] = (u8) (temp >> 8);
packet_buf[3] = (u8) temp;
temp = (dw_lenth) % FTS_PACKET_LENGTH;
packet_buf[4] = (u8) (temp >> 8);
packet_buf[5] = (u8) temp;
for (i = 0; i < temp; i++) {
packet_buf[6 + i] =
pbt_buf[packet_number * FTS_PACKET_LENGTH + i];
bt_ecc ^= packet_buf[6 + i];
}
ft6x0x_i2c_Write(client, packet_buf, temp + 6);
msleep(20);
}
/*send the last six byte */
for (i = 0; i < 6; i++) {
temp = 0x6ffa + i;
packet_buf[2] = (u8) (temp >> 8);
packet_buf[3] = (u8) temp;
temp = 1;
packet_buf[4] = (u8) (temp >> 8);
packet_buf[5] = (u8) temp;
packet_buf[6] =
pbt_buf[dw_lenth + i];
bt_ecc ^= packet_buf[6];
ft6x0x_i2c_Write(client, packet_buf, 7);
msleep(20);
}
/*********Step 6: read out checksum***********************/
/*send the opration head */
DBG("Step 6: read out checksum\n");
auc_i2c_write_buf[0] = 0xcc;
ft6x0x_i2c_Read(client, auc_i2c_write_buf, 1, reg_val, 1);
if (reg_val[0] != bt_ecc) {
dev_err(&client->dev, "[FTS]--ecc error! FW=%02x bt_ecc=%02x\n",
reg_val[0],
bt_ecc);
return -EIO;
}
/*********Step 7: reset the new FW***********************/
DBG("Step 7: reset the new FW\n");
auc_i2c_write_buf[0] = 0x07;
ft6x0x_i2c_Write(client, auc_i2c_write_buf, 1);
msleep(300);
/*make sure CTP startup normally */
return 0;
}
下面我们对上面的调用的
ft6x0x_i2c_Write函数进行分析下,其实他也是调用ft6x0x_i2c_Write函数而已,具体的调用关系如下:
int ft6x06_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue) // send data
{
unsigned char buf[2] = {0};
buf[0] = regaddr;
buf[1] = regvalue;
return ft6x0x_i2c_Write(client, buf, sizeof(buf)); // 发送客户端数据
}
来看看
ft6x0x_i2c_Write函数,代码如下:
/*write data by i2c*/
int
ft6x0x_i2c_Write(struct i2c_client *client, char *writebuf, int writelen)
{
int ret;
struct ft6x0x_ts_data *ft6x0x_ts =
i2c_get_clientdata
(client); // 获得客户端驱动数据结构体指针地址
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0, // 发送数据
.len = writelen,
.buf = writebuf,
},
};
ret =
i2c_transfer(client->adapter, msg, 1);
//发送数据
if (ret < 0){
dev_err(&client->dev, "%s i2c write error.\n", __func__);
ft6x0x_ts_release(ft6x0x_ts);
//释放触摸按键数据
ft6x0x_ts_reset(ft6x0x_ts);
//重启
}
return ret;
}
我们再来看看自动更新固件函数中的另外一个重要的函数,自动校正函数
fts_ctpm_auto_clb函数的实现。
int
fts_ctpm_auto_clb(struct i2c_client *client)
// 自动校准
{
unsigned char uc_temp = 0x00;
unsigned char i = 0;
/*start auto CLB */
msleep(200);
ft6x06_write_reg(client, 0, FTS_FACTORYMODE_VALUE);
/*make sure already enter factory mode */
msleep(100);
/*write command to start calibration */
ft6x06_write_reg(client, 2, 0x4);
msleep(300);
for (i = 0; i < 100; i++) {
ft6x06_read_reg(client, 0, &uc_temp);
/*return to normal mode, calibration finish */
if (0x0 == ((uc_temp & 0x70) >> 4))
break;
}
msleep(200);
/*calibration OK */
msleep(300);
ft6x06_write_reg(client, 0, FTS_FACTORYMODE_VALUE);
/*goto factory mode for store */
msleep(100);
/*make sure already enter factory mode */
ft6x06_write_reg(client, 2, 0x5);
/*store CLB result */
msleep(300);
ft6x06_write_reg(client, 0, FTS_WORKMODE_VALUE);
/*return to normal mode */
msleep(300);
/*store CLB result OK */
return 0;
}
自动更新固件模块的函数我们先就了解到这里,我们再来看看另外一个模块工作队列函数的实现工程。
static void
ft6x0x_work_handler(struct work_struct *work)
// 上报参数
{
struct ft6x0x_ts_data *ft6x0x_ts =
container_of(work, struct ft6x0x_ts_data,work);
int ret = 0;
ret =
ft6x0x_read_Touchdata(ft6x0x_ts);
// 读触摸按键信息
if (ret == 0)
ft6x0x_report_value(ft6x0x_ts);
// 上报按键值
enable_irq(ft6x0x_ts->client->irq);
// 使能中断
}
下面再来看看被调用的函数
ft6x0x_read_Touchdata函数的代码实现。
/*Read touch point information when the interrupt is asserted.*/
static int
ft6x0x_read_Touchdata(struct ft6x0x_ts_data *data)
{
struct ts_event *event = &data->event;
int ret = -1, i = 0;
uint8_t buf[POINT_READ_BUF] = { 0 };
uint8_t finger_regs[] = {0x03,0x09,0x0F,0x15,0x1B};
memset(event, 0, sizeof(struct ts_event));
// 初始化触摸事件结构体为0.
event->touch_point = 0;
buf[0] = 0;
ret =
ft6x0x_i2c_Read(data->client, buf, 1, buf, 3);
if (ret < 0) {
return ret;
}
event->touch_point = buf[2] & 0x0f;
if (0 == event->touch_point) {
ft6x0x_ts_release(data);
return 1;
}
if (event->touch_point > sizeof(finger_regs)) {
if (event->touch_point == 0x0F)
return 1;
else {
dev_info(&data->client->dev, "ERROR : %s --> %d check finger_regs!\n", __func__, __LINE__);
return 1;
}
}
//设置触摸事件的基本参数信息
for (i = 0; i < event->touch_point; i ++) {
buf[0] = finger_regs[i];
ft6x0x_i2c_Read(data->client,buf,1,buf,6);
event->au16_x[i] = (s16) (buf[0] & 0x0F) << 8 | (s16) buf[1];
event->au16_y[i] = (s16) (buf[2] & 0x0F) << 8 | (s16) buf[3];
event->au8_touch_event[i] = buf[0] >> 6;
event->au8_finger_id[i] = (buf[2]) >> 4;
event->weight[i] = buf[4];
}
if(event->au16_y[0] < 260){
finger_up = false;
}
if ((1 == event->touch_point) && finger_up == true) {
if(!
ft6x0x_touchket_detec(event->au16_x[0], event->au16_y[0])) {
//判断触摸按键事件是否触发
ft6x0x_touchkey_report(data->input_dev);
//设置按键事件触发值
}
}
event->pressure = FT_PRESS;
return 0;
}
上面函数的主要功能是设置触摸按键事件触发时所对应的按键值,以便于按键事件处理时使用。
下面看看被调用的函数
ft6x0x_touchket_detec函数的代码实现,看看它的具体工作内容是什么.
static int
ft6x0x_touchket_detec(int x, int y)
// 判断按键事件类型
{
//根据触摸按键的坐标值来区分按键事件的类型
if(x >= tk_info.x_min_back && x <= tk_info.x_max_back &&
y >=tk_info.y_min_back && y <= tk_info.y_max_back){
tp_key_back_down = 1;
return 0;
}
if(x >=tk_info.x_min_home && x <= tk_info.x_max_home &&
y >=tk_info.y_min_home && y <= tk_info.y_max_home){
tp_key_home_down = 1;
return 0;
}
if(x >=tk_info.x_min_menu && x <= tk_info.x_max_menu &&
y >=tk_info.y_min_menu && y <= tk_info.y_max_menu){
tp_key_menu_down = 1;
return 0;
}
if(x >=tk_info.x_min_search && x <= tk_info.x_max_search &&
y >=tk_info.y_min_search && y <= tk_info.y_max_search){
tp_key_search_down = 1;
return 0;
}
return 1;
}
下面看看另外一个函数
ft6x0x_touchkey_report函数,主要是设置按键事件被触发的按键值
static void
ft6x0x_touchkey_report(struct input_dev *dev)
// 设置按键事件被触发的按键值
{
if(tp_key_search_down) {
input_report_key(dev, KEY_SEARCH, 1);
//设置被触发的按键值
input_sync(dev);
//同步事件
} else if(tp_key_back_down) {
input_report_key(dev, KEY_BACK, 1);
input_sync(dev);
} else if(tp_key_home_down) {
input_report_key(dev, KEY_HOME, 1);
input_sync(dev);
} else if(tp_key_menu_down) {
input_report_key(dev, KEY_MENU, 1);
input_sync(dev);
}
}
我们再来看看另外一个函数
ft6x0x_report_value函数的代码,看看它的具体功能。
/*
*report the point information
*/
static int
ft6x0x_report_value(struct ft6x0x_ts_data *data)
// 上报event 参数
{
struct ts_event *event = &data->event;
int i = 0;
for (i = 0; i < event->touch_point; i++) {
if(event->au16_x[i] > data->x_max || event->au16_y[i] > data->y_max)
continue;
#ifdef CONFIG_TSC_SWAP_XY
tsc_swap_xy(&(event->au16_x[i]),&(event->au16_y[i]));
// exchange x axis and y axis
#endif
#ifdef CONFIG_TSC_SWAP_X
tsc_swap_x(&(event->au16_x[i]),data->x_max);
// x_max - x_current
#endif
#ifdef CONFIG_TSC_SWAP_Y
tsc_swap_y(&(event->au16_y[i]),data->y_max);
// y_max - y_current
#endif
pr_pos("x[%d]: %d,\ty[%d]: %d\n", i, event->au16_x[i], i, event->au16_y[i]);
// 上报参数值
input_report_abs(data->input_dev, ABS_MT_POSITION_X,
event->au16_x[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
event->au16_y[i]);
input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
event->au8_finger_id[i]);
input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,
event->weight[i]);
input_report_abs(data->input_dev,ABS_MT_WIDTH_MAJOR,
event->weight[i]);
input_mt_sync(data->input_dev);
}
input_sync(data->input_dev);
//同步事件
return 0;
}
对于上面的函数是将参数上报到事件层,我再来看看3个交换函数的代码。
static inline void
tsc_swap_xy(u16 * x,u16 * y)
{
u16 tmp = 0;
tmp = *x;
*x = *y;
*y = tmp;
}
static inline void
tsc_swap_x(u16 * x,u16 max_x)
{
*x = max_x - *x;
}
static inline void
tsc_swap_y(u16 * y,u16 max_y)
{
*y = max_y - *y;
}
工作队列的初始化工作已经简单的看了一下,下面我们再来触摸屏按键事件的处理机制,看看它的实现原理和代码结构。
static int
touch_event_handler (void *unused)
{
int ret = -1;
struct ft6x0x_ts_data *ft6x0x_ts = (struct ft6x0x_ts_data *)unused;
struct sched_param param = { .sched_priority = RTPM_PRIO_TPD};
sched_setscheduler(current, SCHED_RR, ¶m);
// 设置进程调度策略和时实优先级
set_freezable();
// 使当前线程挂起或者休眠
do {
set_current_state(TASK_INTERRUPTIBLE);
// 设置当前状态值
wait_event_freezable(waiter, ft6x0x_ts->tpd_flag != 0);
//等待队列进入休眠的条件是第二个参数为假时。
ft6x0x_ts->tpd_flag = 0;
set_current_state(TASK_RUNNING);
ret =
ft6x0x_read_Touchdata(ft6x0x_ts);
// 读取触摸按键值
if (ret == 0)
ft6x0x_report_value(ft6x0x_ts);
// 上报按键值
enable_irq(ft6x0x_ts->client->irq);
} while (!
kthread_should_stop());
// 完成工作后主动结束线程
return 0;
}
这个函数调用的函数的具体的实现和代码我就不在拿出来看了,如果有兴趣可以自己在内核中代码中查看,下面我们来看看另外一个模块中断处理机制。
/*The ft6x0x device will signal the host about TRIGGER_FALLING.
*Processed when the interrupt is asserted.
*/
static irqreturn_t
ft6x0x_ts_interrupt(int irq, void *dev_id)
{
struct ft6x0x_ts_data *ft6x0x_ts = dev_id;
disable_irq_nosync(ft6x0x_ts->irq);
// 禁止中断并立即返回
if(ft6x0x_ts->is_suspend == 1)
return IRQ_HANDLED;
#ifdef CONFIG_KEY_SPECIAL_POWER_KEY
if(bkl_flag != 1) {
if(
timer_pending(&ft6x0x_ts->tp_blk_delay))
del_timer_sync(&ft6x0x_ts->tp_blk_delay);
//取消定时器
bkl_flag = 2;
mod_timer(&ft6x0x_ts->tp_blk_delay, get_jiffies_64()
+ msecs_to_jiffies(ft6x0x_ts->pdata->blight_off_timer));
}
#endif
#ifdef FTTP_THREAD_MODE
ft6x0x_ts->tpd_flag = 1;
wake_up_interruptible(&waiter);
// 唤醒休眠等待的队列
#else
queue_work(ft6x0x_ts->workqueue, &ft6x0x_ts->work);
// 调度执行工作队列
#endif
return IRQ_HANDLED;
}
下面我们再来看看最后一个模块背光处理机制,看看他是如何实现的。其实背光函数tp_off_blk_timer函数实际上是在调用set_backlight_light函数,我们来看看它的具体实现。
int
set_backlight_light(int state)
//背光灯的控制
{
int value;
value =
gpio_get_value(GPIO_BL_PWR_EN);
// 获得背光电源引脚的gpio值
switch (state) {
case BKL_ON:
break;
case BKL_TP_OFF:
if(value)
gpio_direction_output(GPIO_BL_PWR_EN, 0);
// 设置背光电源关闭
break;
case BKL_KEY_OFF:
if(value)
gpio_direction_output(GPIO_BL_PWR_EN, 0);
// 设置背光电源关闭
break;
case BKL_TURN:
if(value)
gpio_direction_output(GPIO_BL_PWR_EN, 0);
// 设置背光电源关闭
else
gpio_direction_output(GPIO_BL_PWR_EN, 1);
// 设置背光电源打开
break;
default:
break;
}
bkl_flag = 0;
return
gpio_get_value(GPIO_BL_PWR_EN);
// 返回 背光电源引脚的当前gpio值
}