/*
|
*
|
* FocalTech TouchScreen driver.
|
*
|
* Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved.
|
*
|
* This software is licensed under the terms of the GNU General Public
|
* License version 2, as published by the Free Software Foundation, and
|
* may be copied, distributed, and modified under those terms.
|
*
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
*
|
*/
|
|
/************************************************************************
|
*
|
* File Name: focaltech_test.c
|
*
|
* Author: Focaltech Driver Team
|
*
|
* Created: 2016-08-01
|
*
|
* Modify:
|
*
|
* Abstract: create char device and proc node for the comm between APK and TP
|
*
|
************************************************************************/
|
|
/*****************************************************************************
|
* Included header files
|
*****************************************************************************/
|
#include "focaltech_test.h"
|
|
/*****************************************************************************
|
* Private constant and macro definitions using #define
|
*****************************************************************************/
|
|
/*****************************************************************************
|
* Global variable or extern global variabls/functions
|
*****************************************************************************/
|
struct fts_test *fts_ftest;
|
|
struct test_funcs *test_func_list[] = {
|
&test_func_ft8201,
|
};
|
|
/*****************************************************************************
|
* Static function prototypes
|
*****************************************************************************/
|
|
/*****************************************************************************
|
* functions body
|
*****************************************************************************/
|
void sys_delay(int ms)
|
{
|
msleep(ms);
|
}
|
|
int focal_abs(int value)
|
{
|
if (value < 0)
|
value = 0 - value;
|
|
return value;
|
}
|
|
void *fts_malloc(size_t size)
|
{
|
return kzalloc(size, GFP_KERNEL);
|
}
|
|
void fts_free_proc(void *p)
|
{
|
return kfree(p);
|
}
|
|
void print_buffer(int *buffer, int length, int line_num)
|
{
|
int i = 0;
|
|
if (NULL == buffer) {
|
FTS_TEST_DBG("buffer is null");
|
return;
|
}
|
|
for (i = 0; i < length; i++) {
|
printk("%5d ", buffer[i]);
|
if ((0 == (i + 1) % line_num))
|
printk("\n");
|
}
|
printk("\n");
|
}
|
|
/********************************************************************
|
* test i2c read/write interface
|
*******************************************************************/
|
static int fts_test_i2c_read(u8 *writebuf, int writelen, u8 *readbuf, int readlen)
|
{
|
int ret = 0;
|
#if 1
|
if (NULL == fts_data) {
|
FTS_TEST_ERROR("fts_data is null, no test");
|
return -EINVAL;
|
}
|
ret = fts_i2c_read(fts_data->client, writebuf, writelen, readbuf, readlen);
|
#else
|
ret = fts_i2c_read(writebuf, writelen, readbuf, readlen);
|
#endif
|
|
if (ret < 0)
|
return ret;
|
else
|
return 0;
|
}
|
|
static int fts_test_i2c_write(u8 *writebuf, int writelen)
|
{
|
int ret = 0;
|
#if 1
|
if (NULL == fts_data) {
|
FTS_TEST_ERROR("fts_data is null, no test");
|
return -EINVAL;
|
}
|
ret = fts_i2c_write(fts_data->client, writebuf, writelen);
|
#else
|
ret = fts_i2c_write(writebuf, writelen);
|
#endif
|
|
if (ret < 0)
|
return ret;
|
else
|
return 0;
|
}
|
|
int fts_test_read_reg(u8 addr, u8 *val)
|
{
|
return fts_test_i2c_read(&addr, 1, val, 1);
|
}
|
|
int fts_test_write_reg(u8 addr, u8 val)
|
{
|
int ret;
|
u8 cmd[2] = {0};
|
|
cmd[0] = addr;
|
cmd[1] = val;
|
ret = fts_test_i2c_write(cmd, 2);
|
|
return ret;
|
}
|
|
int fts_test_read(u8 addr, u8 *readbuf, int readlen)
|
{
|
int ret = 0;
|
int i = 0;
|
int packet_length = 0;
|
int packet_num = 0;
|
int packet_remainder = 0;
|
int offset = 0;
|
int byte_num = readlen;
|
|
packet_num = byte_num / BYTES_PER_TIME;
|
packet_remainder = byte_num % BYTES_PER_TIME;
|
if (packet_remainder)
|
packet_num++;
|
|
if (byte_num < BYTES_PER_TIME) {
|
packet_length = byte_num;
|
} else {
|
packet_length = BYTES_PER_TIME;
|
}
|
// FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder);
|
|
ret = fts_test_i2c_read(&addr, 1, &readbuf[offset], packet_length);
|
if (ret < 0) {
|
FTS_TEST_ERROR("read buffer fail");
|
return ret;
|
}
|
for (i = 1; i < packet_num; i++) {
|
offset += packet_length;
|
if ((i == (packet_num - 1)) && packet_remainder) {
|
packet_length = packet_remainder;
|
}
|
|
ret = fts_test_i2c_read(NULL, 0, &readbuf[offset], packet_length);
|
if (ret < 0) {
|
FTS_TEST_ERROR("read buffer fail");
|
return ret;
|
}
|
}
|
|
return 0;
|
}
|
|
int fts_test_write(u8 addr, u8 *writebuf, int writelen)
|
{
|
int ret = 0;
|
int i = 0;
|
u8 data[BYTES_PER_TIME + 1] = { 0 };
|
int packet_length = 0;
|
int packet_num = 0;
|
int packet_remainder = 0;
|
int offset = 0;
|
int byte_num = writelen;
|
|
packet_num = byte_num / BYTES_PER_TIME;
|
packet_remainder = byte_num % BYTES_PER_TIME;
|
if (packet_remainder)
|
packet_num++;
|
|
if (byte_num < BYTES_PER_TIME) {
|
packet_length = byte_num;
|
} else {
|
packet_length = BYTES_PER_TIME;
|
}
|
FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder);
|
|
data[0] = addr;
|
for (i = 0; i < packet_num; i++) {
|
if (i != 0) {
|
data[0] = addr + 1;
|
}
|
if ((i == (packet_num - 1)) && packet_remainder) {
|
packet_length = packet_remainder;
|
}
|
memcpy(&data[1], &writebuf[offset], packet_length);
|
|
ret = fts_test_i2c_write(data, packet_length + 1);
|
if (ret < 0) {
|
FTS_TEST_ERROR("write buffer fail");
|
return ret;
|
}
|
|
offset += packet_length;
|
}
|
|
return 0;
|
}
|
|
/********************************************************************
|
* test global function enter work/factory mode
|
*******************************************************************/
|
int enter_work_mode(void)
|
{
|
int ret = 0;
|
u8 mode = 0;
|
int i = 0;
|
int j = 0;
|
|
FTS_TEST_FUNC_ENTER();
|
|
ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode);
|
if ((ret >= 0) && (0x00 == mode))
|
return 0;
|
|
for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) {
|
ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x00);
|
if (ret >= 0) {
|
sys_delay(FACTORY_TEST_DELAY);
|
for (j = 0; j < 20; j++) {
|
ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode);
|
if ((ret >= 0) && (0x00 == mode)) {
|
FTS_TEST_INFO("enter work mode success");
|
return 0;
|
} else
|
sys_delay(FACTORY_TEST_DELAY);
|
}
|
}
|
|
sys_delay(50);
|
}
|
|
if (i >= ENTER_WORK_FACTORY_RETRIES) {
|
FTS_TEST_ERROR("Enter work mode fail");
|
return -EIO;
|
}
|
|
FTS_TEST_FUNC_EXIT();
|
return 0;
|
}
|
|
int enter_factory_mode(void)
|
{
|
int ret = 0;
|
u8 mode = 0;
|
int i = 0;
|
int j = 0;
|
|
ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode);
|
if ((ret >= 0) && (0x40 == mode))
|
return 0;
|
|
for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) {
|
ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x40);
|
if (ret >= 0) {
|
sys_delay(FACTORY_TEST_DELAY);
|
for (j = 0; j < 20; j++) {
|
ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode);
|
if ((ret >= 0) && (0x40 == mode)) {
|
FTS_TEST_INFO("enter factory mode success");
|
sys_delay(200);
|
return 0;
|
} else
|
sys_delay(FACTORY_TEST_DELAY);
|
}
|
}
|
|
sys_delay(50);
|
}
|
|
if (i >= ENTER_WORK_FACTORY_RETRIES) {
|
FTS_TEST_ERROR("Enter factory mode fail");
|
return -EIO;
|
}
|
|
return 0;
|
}
|
|
/*
|
* read_mass_data - read rawdata/short test data
|
* addr - register addr which read data from
|
* byte_num - read data length, unit:byte
|
* buf - save data
|
*
|
* return 0 if read data succuss, otherwise return error code
|
*/
|
int read_mass_data(u8 addr, int byte_num, int *buf)
|
{
|
int ret = 0;
|
int i = 0;
|
u8 *data = NULL;
|
|
data = (u8 *)fts_malloc(byte_num * sizeof(u8));
|
if (NULL == data) {
|
FTS_TEST_SAVE_ERR("mass data buffer malloc fail\n");
|
return -ENOMEM;
|
}
|
|
/* read rawdata buffer */
|
FTS_TEST_INFO("mass data len:%d", byte_num);
|
ret = fts_test_read(addr, data, byte_num);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("read mass data fail\n");
|
goto read_massdata_err;
|
}
|
|
for (i = 0; i < byte_num; i = i + 2) {
|
buf[i >> 1] = (int)(((int)(data[i]) << 8) + data[i + 1]);
|
}
|
|
ret = 0;
|
read_massdata_err:
|
fts_free(data);
|
return ret;
|
}
|
|
int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf)
|
{
|
int ret = 0;
|
int times = 0;
|
u8 short_state = 0;
|
|
FTS_TEST_FUNC_ENTER();
|
|
/* Start ADC sample */
|
ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_EN, 0x01);
|
if (ret) {
|
FTS_TEST_SAVE_ERR("start short test fail\n");
|
goto adc_err;
|
}
|
|
sys_delay(ch_num * FACTORY_TEST_DELAY);
|
for (times = 0; times < FACTORY_TEST_RETRY; times++) {
|
ret = fts_test_read_reg(FACTORY_REG_SHORT_TEST_STATE, &short_state);
|
if ((ret >= 0) && (retval == short_state))
|
break;
|
else
|
FTS_TEST_DBG("reg%x=%x,retry:%d",
|
FACTORY_REG_SHORT_TEST_STATE, short_state, times);
|
|
sys_delay(FACTORY_TEST_RETRY_DELAY);
|
}
|
if (times >= FACTORY_TEST_RETRY) {
|
FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n");
|
ret = -EIO;
|
goto adc_err;
|
}
|
|
ret = read_mass_data(FACTORY_REG_SHORT_ADDR, byte_num, adc_buf);
|
if (ret) {
|
FTS_TEST_SAVE_ERR("get short(adc) data fail\n");
|
}
|
|
adc_err:
|
FTS_TEST_FUNC_EXIT();
|
return ret;
|
}
|
|
/*
|
* wait_state_update - wait fw status update
|
*/
|
int wait_state_update(u8 retval)
|
{
|
int ret = 0;
|
int times = 0;
|
u8 state = 0xFF;
|
|
while (times++ < FACTORY_TEST_RETRY) {
|
sys_delay(FACTORY_TEST_DELAY);
|
/* Wait register status update */
|
state = 0xFF;
|
ret = fts_test_read_reg(FACTORY_REG_PARAM_UPDATE_STATE, &state);
|
if ((ret >= 0) && (retval == state))
|
break;
|
else
|
FTS_TEST_DBG("reg%x=%x,retry:%d", \
|
FACTORY_REG_PARAM_UPDATE_STATE, state, times);
|
}
|
|
if (times >= FACTORY_TEST_RETRY) {
|
FTS_TEST_SAVE_ERR("Wait State Update fail\n");
|
return -EIO;
|
}
|
|
return 0;
|
}
|
|
/*
|
* start_scan - start to scan a frame
|
*/
|
int start_scan(void)
|
{
|
int ret = 0;
|
u8 addr = 0;
|
u8 val = 0;
|
u8 finish_val = 0;
|
int times = 0;
|
struct fts_test *tdata = fts_ftest;
|
|
if ((NULL == tdata) || (NULL == tdata->func) ) {
|
FTS_TEST_ERROR("test/func is null\n");
|
return -EINVAL;
|
}
|
|
if (SCAN_SC == tdata->func->startscan_mode) {
|
/* sc ic */
|
addr = FACTORY_REG_SCAN_ADDR2;
|
val = 0x01;
|
finish_val = 0x00;
|
} else {
|
addr = DEVIDE_MODE_ADDR;
|
val = 0xC0;
|
finish_val = 0x40;
|
}
|
|
/* write register to start scan */
|
ret = fts_test_write_reg(addr, val);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("write start scan mode fail\n");
|
return ret;
|
}
|
|
/* Wait for the scan to complete */
|
while (times++ < FACTORY_TEST_RETRY) {
|
sys_delay(FACTORY_TEST_DELAY);
|
|
ret = fts_test_read_reg(addr, &val);
|
if ((ret >= 0) && (val == finish_val)) {
|
break;
|
} else
|
FTS_TEST_DBG("reg%x=%x,retry:%d", addr, val, times);
|
}
|
|
if (times >= FACTORY_TEST_RETRY) {
|
FTS_TEST_SAVE_ERR("scan timeout\n");
|
return -EIO;
|
}
|
|
return 0;
|
}
|
|
static int read_rawdata(
|
u8 off_addr,
|
u8 off_val,
|
u8 rawdata_addr,
|
int byte_num,
|
int *data)
|
{
|
int ret = 0;
|
|
/* set line addr or rawdata start addr */
|
ret = fts_test_write_reg(off_addr, off_val);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("wirte line/start addr fail\n");
|
return ret;
|
}
|
|
ret = read_mass_data(rawdata_addr, byte_num, data);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("read rawdata fail\n");
|
return ret;
|
}
|
|
return 0;
|
}
|
|
int get_rawdata(int *data)
|
{
|
int ret = 0;
|
u8 val = 0;
|
u8 addr = 0;
|
u8 rawdata_addr = 0;
|
int byte_num = 0;
|
struct fts_test *tdata = fts_ftest;
|
|
if ((NULL == tdata) || (NULL == tdata->func) ) {
|
FTS_TEST_ERROR("test/func is null\n");
|
return -EINVAL;
|
}
|
|
/* enter factory mode */
|
ret = enter_factory_mode();
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret);
|
return ret;
|
}
|
|
/* start scanning */
|
ret = start_scan();
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("scan fail\n");
|
return ret;
|
}
|
|
/* read rawdata */
|
if (IC_HW_INCELL == tdata->func->hwtype) {
|
val = 0xAD;
|
addr = FACTORY_REG_LINE_ADDR;
|
rawdata_addr = FACTORY_REG_RAWDATA_ADDR;
|
} else if (IC_HW_MC_SC == tdata->func->hwtype) {
|
val = 0xAA;
|
addr = FACTORY_REG_LINE_ADDR;
|
rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC;
|
} else {
|
val = 0x0;
|
addr = FACTORY_REG_RAWDATA_SADDR_SC;
|
rawdata_addr = FACTORY_REG_RAWDATA_ADDR_SC;
|
}
|
|
byte_num = tdata->node.node_num * 2;
|
ret = read_rawdata(addr, val, rawdata_addr, byte_num, data);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("read rawdata fail\n");
|
return ret;
|
}
|
|
return 0;
|
}
|
|
/*
|
* chip_clb - auto clb
|
*/
|
int chip_clb(void)
|
{
|
int ret = 0;
|
u8 val = 0;
|
int times = 0;
|
|
/* start clb */
|
ret = fts_test_write_reg(FACTORY_REG_CLB, 0x04);
|
if (ret) {
|
FTS_TEST_SAVE_ERR("write start clb fail\n");
|
return ret;
|
}
|
|
while (times++ < FACTORY_TEST_RETRY) {
|
sys_delay(FACTORY_TEST_RETRY_DELAY);
|
ret = fts_test_read_reg(FACTORY_REG_CLB, &val);
|
if ((0 == ret) && (0x02 == val)) {
|
/* clb ok */
|
break;
|
} else
|
FTS_TEST_DBG("reg%x=%x,retry:%d", FACTORY_REG_CLB, val, times);
|
}
|
|
if (times >= FACTORY_TEST_RETRY) {
|
FTS_TEST_SAVE_ERR("chip clb timeout\n");
|
return -EIO;
|
}
|
|
return 0;
|
}
|
|
/*
|
* get_cb_incell - get cb data for incell IC
|
*/
|
int get_cb_incell(u16 saddr, int byte_num, int *cb_buf)
|
{
|
int ret = 0;
|
int i = 0;
|
u8 cb_addr = 0;
|
u8 addr_h = 0;
|
u8 addr_l = 0;
|
int read_num = 0;
|
int packet_num = 0;
|
int packet_remainder = 0;
|
int offset = 0;
|
int addr = 0;
|
u8 *data = NULL;
|
|
data = (u8 *)fts_malloc(byte_num * sizeof(u8));
|
if (NULL == data) {
|
FTS_TEST_SAVE_ERR("cb buffer malloc fail\n");
|
return -ENOMEM;
|
}
|
|
packet_num = byte_num / BYTES_PER_TIME;
|
packet_remainder = byte_num % BYTES_PER_TIME;
|
if (packet_remainder)
|
packet_num++;
|
read_num = BYTES_PER_TIME;
|
|
FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder);
|
cb_addr = FACTORY_REG_CB_ADDR;
|
for (i = 0; i < packet_num; i++) {
|
offset = read_num * i;
|
addr = saddr + offset;
|
addr_h = (addr >> 8) & 0xFF;
|
addr_l = addr & 0xFF;
|
if ((i == (packet_num - 1)) && packet_remainder) {
|
read_num = packet_remainder;
|
}
|
|
ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_H, addr_h);
|
if (ret) {
|
FTS_TEST_SAVE_ERR("write cb addr high fail\n");
|
goto TEST_CB_ERR;
|
}
|
ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_L, addr_l);
|
if (ret) {
|
FTS_TEST_SAVE_ERR("write cb addr low fail\n");
|
goto TEST_CB_ERR;
|
}
|
|
ret = fts_test_read(cb_addr, data + offset, read_num);
|
if (ret) {
|
FTS_TEST_SAVE_ERR("read cb fail\n");
|
goto TEST_CB_ERR;
|
}
|
}
|
|
for (i = 0; i < byte_num; i++) {
|
cb_buf[i] = data[i];
|
}
|
|
TEST_CB_ERR:
|
fts_free(data);
|
return ret;
|
}
|
|
int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode)
|
{
|
int ret = 0;
|
int i = 0;
|
int read_num = 0;
|
int packet_num = 0;
|
int packet_remainder = 0;
|
int offset = 0;
|
u8 cb_addr = 0;
|
u8 off_addr = 0;
|
struct fts_test *tdata = fts_ftest;
|
u8 *cb = NULL;
|
|
if ((NULL == tdata) || (NULL == tdata->func) ) {
|
FTS_TEST_ERROR("test/func is null\n");
|
return -EINVAL;
|
}
|
|
cb = (u8 *)fts_malloc(byte_num * sizeof(u8));
|
if (NULL == cb) {
|
FTS_TEST_SAVE_ERR("malloc memory for cb buffer fail\n");
|
return -ENOMEM;
|
}
|
|
if (IC_HW_MC_SC == tdata->func->hwtype) {
|
cb_addr = FACTORY_REG_MC_SC_CB_ADDR;
|
off_addr = FACTORY_REG_MC_SC_CB_ADDR_OFF;
|
} else if (IC_HW_SC == tdata->func->hwtype) {
|
cb_addr = FACTORY_REG_SC_CB_ADDR;
|
off_addr = FACTORY_REG_SC_CB_ADDR_OFF;
|
}
|
|
packet_num = byte_num / BYTES_PER_TIME;
|
packet_remainder = byte_num % BYTES_PER_TIME;
|
if (packet_remainder)
|
packet_num++;
|
read_num = BYTES_PER_TIME;
|
offset = 0;
|
|
FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder);
|
for (i = 0; i < packet_num; i++) {
|
if ((i == (packet_num - 1)) && packet_remainder) {
|
read_num = packet_remainder;
|
}
|
|
ret = fts_test_write_reg(off_addr, offset);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("write cb addr offset fail\n");
|
goto cb_err;
|
}
|
|
ret = fts_test_read(cb_addr, cb + offset, read_num);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("read cb fail\n");
|
goto cb_err;
|
}
|
|
offset += read_num;
|
}
|
|
if (DATA_ONE_BYTE == mode) {
|
for (i = 0; i < byte_num; i++) {
|
cb_buf[i] = cb[i];
|
}
|
} else if (DATA_TWO_BYTE == mode) {
|
for (i = 0; i < byte_num; i = i + 2) {
|
cb_buf[i >> 1] = (int)(((int)(cb[i]) << 8) + cb[i + 1]);
|
}
|
}
|
|
ret = 0;
|
cb_err:
|
fts_free(cb);
|
return ret;
|
}
|
|
bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key)
|
{
|
int i = 0;
|
bool result = true;
|
struct fts_test *tdata = fts_ftest;
|
int rx = tdata->node.rx_num;
|
int node_va = tdata->node.node_num - tdata->node.key_num;
|
|
if (!data || !tdata->node_valid) {
|
FTS_TEST_SAVE_ERR("data/node_valid is null\n");
|
return false;
|
}
|
|
for (i = 0; i < node_va; i++) {
|
if (0 == tdata->node_valid[i])
|
continue;
|
|
if ((data[i] < min) || (data[i] > max)) {
|
FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n",
|
i / rx + 1, i % rx + 1, data[i], min, max);
|
result = false;
|
}
|
}
|
|
if (key) {
|
for (i = node_va; i < tdata->node.node_num; i++) {
|
if (0 == tdata->node_valid[i])
|
continue;
|
|
if ((data[i] < min_vk) || (data[i] > max_vk)) {
|
FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n",
|
i / rx + 1, i % rx + 1,
|
data[i], min_vk, max_vk);
|
result = false;
|
}
|
}
|
}
|
|
return result;
|
}
|
|
bool compare_array(int *data, int *min, int *max, bool key)
|
{
|
int i = 0;
|
bool result = true;
|
struct fts_test *tdata = fts_ftest;
|
int rx = tdata->node.rx_num;
|
int node_num = tdata->node.node_num;
|
|
if (!data || !min || !max || !tdata->node_valid) {
|
FTS_TEST_SAVE_ERR("data/min/max/node_valid is null\n");
|
return false;
|
}
|
|
if (!key) {
|
node_num -= tdata->node.key_num;
|
}
|
for (i = 0; i < node_num; i++) {
|
if (0 == tdata->node_valid[i])
|
continue;
|
|
if ((data[i] < min[i]) || (data[i] > max[i])) {
|
FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n",
|
i / rx + 1, i % rx + 1, data[i], min[i], max[i]);
|
result = false;
|
}
|
}
|
|
return result;
|
}
|
|
/*
|
* show_data - show and save test data to testresult.txt
|
*/
|
void show_data(int *data, bool key)
|
{
|
int i = 0;
|
int j = 0;
|
struct fts_test *tdata = fts_ftest;
|
int node_num = tdata->node.node_num;
|
int tx_num = tdata->node.tx_num;
|
int rx_num = tdata->node.rx_num;
|
|
FTS_TEST_FUNC_ENTER();
|
for (i = 0; i < tx_num; i++) {
|
FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", i + 1);
|
for (j = 0; j < rx_num; j++) {
|
FTS_TEST_SAVE_INFO("%5d, ", data[i * rx_num + j]);
|
}
|
FTS_TEST_SAVE_INFO("\n");
|
}
|
|
if (key) {
|
FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", tx_num + 1);
|
for (i = tx_num * rx_num; i < node_num; i++) {
|
FTS_TEST_SAVE_INFO("%5d, ", data[i]);
|
}
|
FTS_TEST_SAVE_INFO("\n");
|
}
|
FTS_TEST_FUNC_EXIT();
|
}
|
|
/*
|
* save_testdata_incell - save data to testdata.csv
|
*/
|
void save_data_csv(int *data, char *name, u8 code, bool mc_sc, bool key)
|
{
|
#if CSV_SUPPORT
|
int i = 0;
|
int tx = 0;
|
int rx = 0;
|
int csv_node_num = 0;
|
struct fts_test *tdata = fts_ftest;
|
struct fts_test_node *node = NULL;
|
struct csv_format *csv = &tdata->csv;
|
|
FTS_TEST_FUNC_ENTER();
|
if (!csv || !csv->line2_buffer || !csv->data_buffer) {
|
FTS_TEST_ERROR("csv buffer is null");
|
return;
|
}
|
|
if (mc_sc) {
|
node = &tdata->sc_node;
|
tx = 2;
|
} else {
|
node = &tdata->node;
|
tx = node->tx_num;
|
}
|
if (key) {
|
tx++;
|
}
|
rx = node->rx_num;
|
csv_node_num = tx * rx;
|
|
/* line 2 */
|
csv->line2_len += snprintf(csv->line2_buffer + csv->line2_len, \
|
CSV_LINE2_BUFFER_LEN - csv->line2_len,
|
"%s, %d, %d, %d, %d, %d, ", \
|
name, code, tx, rx,
|
csv->start_line, csv->item_count);
|
|
if (csv->line2_len >= CSV_LINE2_BUFFER_LEN - 1) {
|
FTS_TEST_ERROR("csv line2 buffer length(%d) fail", csv->line2_len);
|
}
|
csv->start_line += tx;
|
csv->item_count++;
|
|
/* test data */
|
for (i = 0; i < csv_node_num; i++) {
|
if (((i + 1) % rx) == 0) {
|
csv->data_len += snprintf(csv->data_buffer + csv->data_len, \
|
CSV_DATA_BUFFER_LEN - csv->data_len, \
|
"%d, \n", data[i]);
|
} else {
|
csv->data_len += snprintf(csv->data_buffer + csv->data_len, \
|
CSV_DATA_BUFFER_LEN - csv->data_len, \
|
"%d, ", data[i]);
|
}
|
|
if (csv->data_len >= CSV_DATA_BUFFER_LEN - 1) {
|
FTS_TEST_ERROR("csv data buffer length(%d) fail", csv->data_len);
|
}
|
}
|
|
|
FTS_TEST_FUNC_EXIT();
|
#endif
|
}
|
|
|
/* mc_sc only */
|
/* Only V3 Pattern has mapping & no-mapping */
|
int mapping_switch(u8 mapping)
|
{
|
int ret = 0;
|
u8 val = 0xFF;
|
struct fts_test *tdata = fts_ftest;
|
|
if (tdata->v3_pattern) {
|
ret = fts_test_read_reg(FACTORY_REG_NOMAPPING, &val);
|
if (ret < 0) {
|
FTS_TEST_ERROR("read 0x54 register fail");
|
return ret;
|
}
|
|
if (val != mapping) {
|
ret = fts_test_write_reg(FACTORY_REG_NOMAPPING, mapping);
|
if (ret < 0) {
|
FTS_TEST_ERROR("write 0x54 register fail");
|
return ret;
|
}
|
sys_delay(FACTORY_TEST_DELAY);
|
}
|
}
|
|
return 0;
|
}
|
|
bool get_fw_wp(u8 wp_ch_sel, enum wp_type water_proof_type)
|
{
|
bool fw_wp_state = false;
|
|
switch (water_proof_type) {
|
case WATER_PROOF_ON:
|
/* bit5: 0-check in wp on, 1-not check */
|
fw_wp_state = !(wp_ch_sel & 0x20);
|
break;
|
case WATER_PROOF_ON_TX:
|
/* Bit6: 0-check Rx+Tx in wp mode 1-check one channel
|
Bit2: 0-check Tx in wp mode; 1-check Rx in wp mode
|
*/
|
fw_wp_state = (!(wp_ch_sel & 0x40) || !(wp_ch_sel & 0x04));
|
break;
|
case WATER_PROOF_ON_RX:
|
fw_wp_state = (!(wp_ch_sel & 0x40) || (wp_ch_sel & 0x04));
|
break;
|
case WATER_PROOF_OFF:
|
/* bit7: 0-check in wp off, 1-not check */
|
fw_wp_state = !(wp_ch_sel & 0x80);
|
break;
|
case WATER_PROOF_OFF_TX:
|
/* Bit1-0: 00-check Tx in non-wp mode
|
01-check Rx in non-wp mode
|
10:check Rx+Tx in non-wp mode
|
*/
|
fw_wp_state = ((0x0 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03)));
|
break;
|
case WATER_PROOF_OFF_RX:
|
fw_wp_state = ((0x01 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03)));
|
break;
|
default:
|
break;
|
}
|
|
return fw_wp_state;
|
}
|
|
int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode)
|
{
|
int ret = 0;
|
|
/* 1:waterproof 0:non-waterproof */
|
ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, wp);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("get mc_sc mode fail\n");
|
return ret;
|
}
|
|
/* read cb */
|
ret = get_cb_sc(byte_num, cb_buf, mode);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("get sc cb fail\n");
|
return ret;
|
}
|
|
return 0;
|
}
|
|
int get_rawdata_mc_sc(enum wp_type wp, int *data)
|
{
|
int ret = 0;
|
u8 val = 0;
|
u8 addr = 0;
|
u8 rawdata_addr = 0;
|
int byte_num = 0;
|
struct fts_test *tdata = fts_ftest;
|
|
if ((NULL == tdata) || (NULL == tdata->func) ) {
|
FTS_TEST_ERROR("test/func is null\n");
|
return -EINVAL;
|
}
|
|
addr = FACTORY_REG_LINE_ADDR;
|
rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC;
|
if (WATER_PROOF_ON == wp) {
|
val = 0xAC;
|
} else {
|
val = 0xAB;
|
}
|
|
byte_num = tdata->sc_node.node_num * 2;
|
ret = read_rawdata(addr, val, rawdata_addr, byte_num, data);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("read rawdata fail\n");
|
return ret;
|
}
|
|
return 0;
|
}
|
|
int get_rawdata_mc(u8 fre, u8 fir, int *rawdata)
|
{
|
int ret = 0;
|
int i = 0;
|
|
if (NULL == rawdata ) {
|
FTS_TEST_SAVE_ERR("rawdata buffer is null\n");
|
return -EINVAL;
|
}
|
|
/* set frequecy high/low */
|
ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret);
|
return ret;
|
}
|
|
/* fir enable/disable */
|
ret = fts_test_write_reg(FACTORY_REG_FIR, 1);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", fir);
|
return ret;
|
}
|
|
/* get rawdata */
|
for (i = 0; i < 3; i++) {
|
/* lost 3 frames, in order to obtain stable data */
|
ret = get_rawdata(rawdata);
|
}
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret);
|
return ret;
|
}
|
|
return 0;
|
}
|
|
void short_print_mc(int *r, int num)
|
{
|
int i = 0;
|
|
for (i = 0; i < num; i++) {
|
printk("%4d ", r[i]);
|
}
|
|
printk("\n");
|
}
|
|
int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode)
|
{
|
int ret = 0;
|
int i = 0;
|
u8 short_state = 0;
|
|
FTS_TEST_FUNC_ENTER();
|
/* select short test mode & start test */
|
ret = fts_test_write_reg(FACTROY_REG_SHORT_TEST_EN, mode);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("write short test mode fail\n");
|
goto test_err;
|
}
|
|
for (i = 0; i < FACTORY_TEST_RETRY; i++) {
|
sys_delay(FACTORY_TEST_RETRY_DELAY);
|
|
ret = fts_test_read_reg(FACTROY_REG_SHORT_TEST_EN, &short_state);
|
if ((ret >= 0) && (retval == short_state))
|
break;
|
else
|
FTS_TEST_DBG("reg%x=%x,retry:%d",
|
FACTROY_REG_SHORT_TEST_EN, short_state, i);
|
}
|
if (i >= FACTORY_TEST_RETRY) {
|
FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n");
|
ret = -EIO;
|
goto test_err;
|
}
|
|
ret = read_mass_data(FACTORY_REG_SHORT_ADDR_MC, byte_num, adc_buf);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("get short(adc) data fail\n");
|
}
|
|
FTS_TEST_DBG("adc data:\n");
|
short_print_mc(adc_buf, byte_num / 2);
|
test_err:
|
FTS_TEST_FUNC_EXIT();
|
return ret;
|
}
|
|
bool compare_mc_sc(bool tx_check, bool rx_check, int *data, int *min, int *max)
|
{
|
int i = 0;
|
bool result = true;
|
struct fts_test *tdata = fts_ftest;
|
|
if (rx_check) {
|
for (i = 0; i < tdata->sc_node.rx_num; i++) {
|
if (0 == tdata->node_valid_sc[i])
|
continue;
|
|
if ((data[i] < min[i]) || (data[i] > max[i])) {
|
FTS_TEST_SAVE_ERR("test fail,rx%d=%5d,range=(%5d,%5d)\n",
|
i + 1, data[i], min[i], max[i]);
|
result = false;
|
}
|
}
|
}
|
|
if (tx_check) {
|
for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) {
|
if (0 == tdata->node_valid_sc[i])
|
continue;
|
|
if ((data[i] < min[i]) || (data[i] > max[i])) {
|
FTS_TEST_SAVE_INFO("test fail,tx%d=%5d,range=(%5d,%5d)\n",
|
i - tdata->sc_node.rx_num + 1,
|
data[i], min[i], max[i]);
|
result = false;
|
}
|
}
|
}
|
|
return result;
|
}
|
|
void show_data_mc_sc(int *data)
|
{
|
int i = 0;
|
struct fts_test *tdata = fts_ftest;
|
|
FTS_TEST_SAVE_INFO("SCap Rx: ");
|
for (i = 0; i < tdata->sc_node.rx_num; i++) {
|
FTS_TEST_SAVE_INFO( "%5d, ", data[i]);
|
}
|
FTS_TEST_SAVE_INFO("\n");
|
|
FTS_TEST_SAVE_INFO("SCap Tx: ");
|
for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) {
|
FTS_TEST_SAVE_INFO( "%5d, ", data[i]);
|
}
|
FTS_TEST_SAVE_INFO("\n");
|
}
|
/* mc_sc end*/
|
|
/*
|
* fts_test_save_test_data - Save test data to SD card etc.
|
*/
|
static int fts_test_save_test_data(char *file_name, char *data_buf, int len)
|
{
|
struct file *pfile = NULL;
|
char filepath[128];
|
loff_t pos;
|
mm_segment_t old_fs;
|
|
FTS_TEST_FUNC_ENTER();
|
memset(filepath, 0, sizeof(filepath));
|
sprintf(filepath, "%s%s", FTS_INI_FILE_PATH, file_name);
|
if (NULL == pfile) {
|
pfile = filp_open(filepath, O_TRUNC | O_CREAT | O_RDWR, 0);
|
}
|
if (IS_ERR(pfile)) {
|
FTS_TEST_ERROR("error occured while opening file %s.", filepath);
|
return -EIO;
|
}
|
|
old_fs = get_fs();
|
set_fs(KERNEL_DS);
|
pos = 0;
|
vfs_write(pfile, data_buf, len, &pos);
|
filp_close(pfile, NULL);
|
set_fs(old_fs);
|
|
FTS_TEST_FUNC_EXIT();
|
return 0;
|
}
|
|
static int fts_test_malloc_free_data_csv(struct fts_test *tdata, bool allocate)
|
{
|
#if CSV_SUPPORT
|
struct csv_format *csv = &tdata->csv;
|
|
if (true == allocate) {
|
csv->buffer = vmalloc(CSV_BUFFER_LEN);
|
if (NULL == csv->buffer) {
|
FTS_TEST_ERROR("csv->buffer malloc fail\n");
|
return -ENOMEM;
|
}
|
csv->line2_buffer = vmalloc(CSV_LINE2_BUFFER_LEN);
|
if (NULL == csv->line2_buffer) {
|
FTS_TEST_ERROR("csv->line2_buffer malloc fail\n");
|
return -ENOMEM;
|
}
|
csv->data_buffer = vmalloc(CSV_DATA_BUFFER_LEN);
|
if (NULL == csv->data_buffer) {
|
FTS_TEST_ERROR("csv->data_buffer malloc fail\n");
|
return -ENOMEM;
|
}
|
|
/* initialize variable */
|
csv->length = 0;
|
csv->line2_len = 0;
|
csv->data_len = 0;
|
csv->start_line = 11;
|
csv->item_count = 1;
|
|
} else {
|
if (csv->buffer) {
|
vfree(csv->buffer);
|
csv->buffer = NULL;
|
}
|
if (csv->line2_buffer) {
|
vfree(csv->line2_buffer);
|
csv->line2_buffer = NULL;
|
}
|
if (csv->data_buffer) {
|
vfree(csv->data_buffer);
|
csv->data_buffer = NULL;
|
}
|
}
|
#endif
|
|
return 0;
|
}
|
|
static int fts_test_malloc_free_data_txt(struct fts_test *tdata, bool allocate)
|
{
|
if (true == allocate) {
|
tdata->testresult = vmalloc(TXT_BUFFER_LEN);
|
if (NULL == tdata->testresult) {
|
FTS_TEST_ERROR("tdata->testresult malloc fail\n");
|
return -ENOMEM;
|
}
|
|
tdata->testresult_len = 0;
|
FTS_TEST_SAVE_INFO("FW version:0x%02x\n", tdata->fw_ver);
|
FTS_TEST_SAVE_INFO("tx_num:%d, rx_num:%d\n",
|
tdata->node.tx_num, tdata->node.rx_num);
|
} else {
|
if (tdata->testresult) {
|
vfree(tdata->testresult);
|
tdata->testresult = NULL;
|
}
|
}
|
|
return 0;
|
}
|
|
static void fts_test_save_data_csv(struct fts_test *tdata)
|
{
|
#if CSV_SUPPORT
|
struct csv_format *csv = &tdata->csv;
|
|
if (!csv || !csv->buffer || !csv->line2_buffer || !csv->data_buffer) {
|
FTS_TEST_ERROR("csv buffer is null");
|
return;
|
}
|
|
/* line 1 */
|
csv->length += snprintf(csv->buffer + csv->length, \
|
CSV_BUFFER_LEN - csv->length, \
|
"ECC, 85, 170, IC Name, %s, IC Code, %x\n", \
|
tdata->ini.ic_name, \
|
(tdata->ini.ic_code >> IC_CODE_OFFSET));
|
|
/* line 2 */
|
csv->length += snprintf(csv->buffer + csv->length, \
|
CSV_BUFFER_LEN - csv->length, \
|
"TestItem Num, %d, ", \
|
csv->item_count);
|
if (csv->line2_len > 0) {
|
memcpy(csv->buffer + csv->length, csv->line2_buffer, csv->line2_len);
|
csv->length += csv->line2_len;
|
}
|
|
/* line 3 ~ 10 "\n" */
|
csv->length += snprintf(csv->buffer + csv->length, \
|
CSV_BUFFER_LEN - csv->length, \
|
"\n\n\n\n\n\n\n\n\n");
|
|
/* line 11 ~ data area */
|
if (csv->data_len > 0) {
|
memcpy(csv->buffer + csv->length, csv->data_buffer, csv->data_len);
|
csv->length += csv->data_len;
|
}
|
|
FTS_TEST_INFO("csv length:%d", csv->length);
|
fts_test_save_test_data(FTS_CSV_FILE_NAME, csv->buffer, csv->length);
|
#endif
|
}
|
|
static void fts_test_save_result_txt(struct fts_test *tdata)
|
{
|
if (!tdata || !tdata->testresult) {
|
FTS_TEST_ERROR("test result is null");
|
return;
|
}
|
|
FTS_TEST_INFO("test result length in txt:%d", tdata->testresult_len);
|
fts_test_save_test_data(FTS_TXT_FILE_NAME, tdata->testresult, tdata->testresult_len);
|
}
|
|
static int fts_test_malloc_free_incell(struct fts_test *tdata, bool allocate)
|
{
|
struct incell_threshold *thr = &tdata->ic.incell.thr;
|
int buflen = tdata->node.node_num * sizeof(int);
|
|
if (true == allocate) {
|
FTS_TEST_INFO("buflen:%d", buflen);
|
fts_malloc_r(thr->rawdata_min, buflen);
|
fts_malloc_r(thr->rawdata_max, buflen);
|
if (tdata->func->rawdata2_support) {
|
fts_malloc_r(thr->rawdata2_min, buflen);
|
fts_malloc_r(thr->rawdata2_max, buflen);
|
}
|
fts_malloc_r(thr->cb_min, buflen);
|
fts_malloc_r(thr->cb_max, buflen);
|
} else {
|
fts_free(thr->rawdata_min);
|
fts_free(thr->rawdata_max);
|
if (tdata->func->rawdata2_support) {
|
fts_free(thr->rawdata2_min);
|
fts_free(thr->rawdata2_max);
|
}
|
fts_free(thr->cb_min);
|
fts_free(thr->cb_max);
|
}
|
|
return 0;
|
}
|
|
static int fts_test_malloc_free_mc_sc(struct fts_test *tdata, bool allocate)
|
{
|
struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr;
|
int buflen = tdata->node.node_num * sizeof(int);
|
int buflen_sc = tdata->sc_node.node_num * sizeof(int);
|
|
if (true == allocate) {
|
fts_malloc_r(thr->rawdata_h_min, buflen);
|
fts_malloc_r(thr->rawdata_h_max, buflen);
|
if (tdata->func->rawdata2_support) {
|
fts_malloc_r(thr->rawdata_l_min, buflen);
|
fts_malloc_r(thr->rawdata_l_max, buflen);
|
}
|
fts_malloc_r(thr->tx_linearity_max, buflen);
|
fts_malloc_r(thr->tx_linearity_min, buflen);
|
fts_malloc_r(thr->rx_linearity_max, buflen);
|
fts_malloc_r(thr->rx_linearity_min, buflen);
|
|
fts_malloc_r(thr->scap_cb_off_min, buflen_sc);
|
fts_malloc_r(thr->scap_cb_off_max, buflen_sc);
|
fts_malloc_r(thr->scap_cb_on_min, buflen_sc);
|
fts_malloc_r(thr->scap_cb_on_max, buflen_sc);
|
|
fts_malloc_r(thr->scap_rawdata_off_min, buflen_sc);
|
fts_malloc_r(thr->scap_rawdata_off_max, buflen_sc);
|
fts_malloc_r(thr->scap_rawdata_on_min, buflen_sc);
|
fts_malloc_r(thr->scap_rawdata_on_max, buflen_sc);
|
|
fts_malloc_r(thr->panel_differ_min, buflen);
|
fts_malloc_r(thr->panel_differ_max, buflen);
|
} else {
|
fts_free(thr->rawdata_h_min);
|
fts_free(thr->rawdata_h_max);
|
if (tdata->func->rawdata2_support) {
|
fts_free(thr->rawdata_l_min);
|
fts_free(thr->rawdata_l_max);
|
}
|
fts_free(thr->tx_linearity_max);
|
fts_free(thr->tx_linearity_min);
|
fts_free(thr->rx_linearity_max);
|
fts_free(thr->rx_linearity_min);
|
|
fts_free(thr->scap_cb_off_min);
|
fts_free(thr->scap_cb_off_max);
|
fts_free(thr->scap_cb_on_min);
|
fts_free(thr->scap_cb_on_max);
|
|
fts_free(thr->scap_rawdata_off_min);
|
fts_free(thr->scap_rawdata_off_max);
|
fts_free(thr->scap_rawdata_on_min);
|
fts_free(thr->scap_rawdata_on_max);
|
|
fts_free(thr->panel_differ_min);
|
fts_free(thr->panel_differ_max);
|
}
|
|
return 0;
|
}
|
|
static int fts_test_malloc_free_sc(struct fts_test *tdata, bool allocate)
|
{
|
struct sc_threshold *thr = &tdata->ic.sc.thr;
|
int buflen = tdata->node.node_num * sizeof(int);
|
|
if (true == allocate) {
|
fts_malloc_r(thr->rawdata_min, buflen);
|
fts_malloc_r(thr->rawdata_max, buflen);
|
fts_malloc_r(thr->cb_min, buflen);
|
fts_malloc_r(thr->cb_max, buflen);
|
fts_malloc_r(thr->dcb_sort, buflen);
|
fts_malloc_r(thr->dcb_base, buflen);
|
} else {
|
fts_free(thr->rawdata_min);
|
fts_free(thr->rawdata_max);
|
fts_free(thr->cb_min);
|
fts_free(thr->cb_max);
|
fts_free(thr->dcb_sort);
|
fts_free(thr->dcb_base);
|
}
|
|
return 0;
|
}
|
|
static int fts_test_malloc_free_thr(struct fts_test *tdata, bool allocate)
|
{
|
int ret = 0;
|
|
if ((NULL == tdata) || (NULL == tdata->func)) {
|
FTS_TEST_SAVE_ERR("tdata/func is NULL\n");
|
return -EINVAL;
|
}
|
|
if (true == allocate) {
|
fts_malloc_r(tdata->node_valid, tdata->node.node_num * sizeof(int));
|
fts_malloc_r(tdata->node_valid_sc, tdata->sc_node.node_num * sizeof(int));
|
} else {
|
fts_free(tdata->node_valid);
|
fts_free(tdata->node_valid_sc);
|
}
|
|
switch (tdata->func->hwtype) {
|
case IC_HW_INCELL:
|
ret = fts_test_malloc_free_incell(tdata, allocate);
|
break;
|
case IC_HW_MC_SC:
|
ret = fts_test_malloc_free_mc_sc(tdata, allocate);
|
break;
|
case IC_HW_SC:
|
ret = fts_test_malloc_free_sc(tdata, allocate);
|
break;
|
default:
|
FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype);
|
ret = -EINVAL;
|
break;
|
}
|
|
return ret;
|
}
|
|
/* default enable all test item */
|
static void fts_test_init_item(struct fts_test *tdata)
|
{
|
switch (tdata->func->hwtype) {
|
case IC_HW_INCELL:
|
tdata->ic.incell.u.tmp = 0xFFFFFFFF;
|
break;
|
case IC_HW_MC_SC:
|
tdata->ic.mc_sc.u.tmp = 0xFFFFFFFF;
|
break;
|
case IC_HW_SC:
|
tdata->ic.sc.u.tmp = 0xFFFFFFFF;
|
break;
|
}
|
}
|
|
static int get_tx_rx_num(u8 tx_rx_reg, u8 *ch_num, u8 ch_num_max)
|
{
|
int ret = 0;
|
int i = 0;
|
|
for (i = 0; i < 3; i++) {
|
ret = fts_test_read_reg(tx_rx_reg, ch_num);
|
if ((ret < 0) || (*ch_num > ch_num_max)) {
|
sys_delay(50);
|
} else
|
break;
|
}
|
|
if (i >= 3) {
|
FTS_TEST_ERROR("get channel num fail");
|
return -EIO;
|
}
|
|
return 0;
|
}
|
|
static int get_channel_num(struct fts_test *tdata)
|
{
|
int ret = 0;
|
u8 tx_num = 0;
|
u8 rx_num = 0;
|
int key_num = 0;
|
|
/* node structure */
|
if (IC_HW_SC == tdata->func->hwtype) {
|
ret = get_tx_rx_num(FACTORY_REG_CH_NUM_SC, &tx_num, NUM_MAX_SC);
|
if (ret < 0) {
|
FTS_TEST_ERROR("get channel number fail");
|
return ret;
|
}
|
|
ret = get_tx_rx_num(FACTORY_REG_KEY_NUM_SC, &rx_num, KEY_NUM_MAX);
|
if (ret < 0) {
|
FTS_TEST_ERROR("get key number fail");
|
return ret;
|
}
|
|
tdata->node.tx_num = 2;
|
tdata->node.rx_num = tx_num / 2;
|
tdata->node.channel_num = tx_num;
|
tdata->node.node_num = tx_num;
|
key_num = rx_num;
|
} else {
|
ret = get_tx_rx_num(FACTORY_REG_CHX_NUM, &tx_num, TX_NUM_MAX);
|
if (ret < 0) {
|
FTS_TEST_ERROR("get tx_num fail");
|
return ret;
|
}
|
|
ret = get_tx_rx_num(FACTORY_REG_CHY_NUM, &rx_num, RX_NUM_MAX);
|
if (ret < 0) {
|
FTS_TEST_ERROR("get rx_num fail");
|
return ret;
|
}
|
|
tdata->node.tx_num = tx_num;
|
tdata->node.rx_num = rx_num;
|
if (IC_HW_INCELL == tdata->func->hwtype)
|
tdata->node.channel_num = tx_num * rx_num;
|
else if (IC_HW_MC_SC == tdata->func->hwtype)
|
tdata->node.channel_num = tx_num + rx_num;
|
tdata->node.node_num = tx_num * rx_num;
|
key_num = tdata->func->key_num_total;
|
}
|
|
/* key */
|
tdata->node.key_num = key_num;
|
tdata->node.node_num += tdata->node.key_num;
|
|
/* sc node structure */
|
tdata->sc_node = tdata->node;
|
if (IC_HW_MC_SC == tdata->func->hwtype) {
|
if (tdata->v3_pattern) {
|
ret = get_tx_rx_num(FACTORY_REG_CHX_NUM_NOMAP, &tx_num, TX_NUM_MAX);
|
if (ret < 0) {
|
FTS_TEST_ERROR("get no-mappint tx_num fail");
|
return ret;
|
}
|
|
ret = get_tx_rx_num(FACTORY_REG_CHY_NUM_NOMAP, &rx_num, TX_NUM_MAX);
|
if (ret < 0) {
|
FTS_TEST_ERROR("get no-mapping rx_num fail");
|
return ret;
|
}
|
|
tdata->sc_node.tx_num = tx_num;
|
tdata->sc_node.rx_num = rx_num;
|
}
|
tdata->sc_node.channel_num = tx_num + rx_num;
|
tdata->sc_node.node_num = tx_num + rx_num;
|
}
|
|
if (tdata->node.tx_num > TX_NUM_MAX) {
|
FTS_TEST_ERROR("tx num(%d) fail", tdata->node.tx_num);
|
return -EIO;
|
}
|
|
if (tdata->node.rx_num > RX_NUM_MAX) {
|
FTS_TEST_ERROR("rx num(%d) fail", tdata->node.rx_num);
|
return -EIO;
|
}
|
|
FTS_TEST_INFO("node_num:%d, tx:%d, rx:%d", tdata->node.node_num,
|
tdata->node.tx_num, tdata->node.rx_num);
|
return 0;
|
}
|
|
static void get_ic_version(struct fts_test *tdata)
|
{
|
u8 val[4] = { 0 };
|
|
fts_test_read_reg(REG_FW_CHIP_IDH, &val[0]);
|
fts_test_read_reg(REG_FW_CHIP_IDL, &val[1]);
|
fts_test_read_reg(REG_FW_IC_TYPE, &val[2]);
|
fts_test_read_reg(REG_FW_IC_VERSION, &val[3]);
|
|
tdata->ic_ver = TEST_IC_VERSION(val[0], val[1], val[2], val[3]);
|
FTS_TEST_INFO("test ic version:0x%8x", tdata->ic_ver);
|
}
|
|
static int fts_test_init_basicinfo(struct fts_test *tdata)
|
{
|
int ret = 0;
|
u8 val = 0;
|
|
if ((NULL == tdata) || (NULL == tdata->func)) {
|
FTS_TEST_SAVE_ERR("tdata/func is NULL\n");
|
return -EINVAL;
|
}
|
|
get_ic_version(tdata);
|
|
fts_test_read_reg(REG_FW_VERSION, &val);
|
tdata->fw_ver = val;
|
|
if (IC_HW_INCELL == tdata->func->hwtype) {
|
fts_test_read_reg(REG_VA_TOUCH_THR, &val);
|
tdata->va_touch_thr = val;
|
fts_test_read_reg(REG_VKEY_TOUCH_THR, &val);
|
tdata->vk_touch_thr = val;
|
}
|
|
/* enter factory mode */
|
ret = enter_factory_mode();
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("enter factory mode fail\n");
|
return ret;
|
}
|
|
if (IC_HW_MC_SC == tdata->func->hwtype) {
|
fts_test_read_reg(FACTORY_REG_PATTERN, &val);
|
tdata->v3_pattern = (1 == val) ? true : false;
|
fts_test_read_reg(FACTORY_REG_NOMAPPING, &val);
|
tdata->mapping = val;
|
}
|
|
/* enter into factory mode and read tx/rx num */
|
ret = get_channel_num(tdata);
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("get channel number fail\n");
|
return ret;
|
}
|
|
return ret;
|
}
|
|
static int fts_test_main_init(void)
|
{
|
int ret = 0;
|
struct fts_test *tdata = fts_ftest;
|
|
FTS_TEST_FUNC_ENTER();
|
/* get basic information: tx/rx num ... */
|
ret = fts_test_init_basicinfo(tdata);
|
if (ret < 0) {
|
FTS_TEST_ERROR("test init basicinfo fail");
|
return ret;
|
}
|
|
/* allocate memory for test threshold */
|
ret = fts_test_malloc_free_thr(tdata, true);
|
if (ret < 0) {
|
FTS_TEST_ERROR("test malloc for threshold fail");
|
return ret;
|
}
|
|
/* default enable all test item */
|
fts_test_init_item(tdata);
|
|
/* allocate memory for test data:csv&txt */
|
ret = fts_test_malloc_free_data_csv(tdata, true);
|
if (ret < 0) {
|
FTS_TEST_ERROR("allocate memory for test data(csv) fail");
|
return ret;
|
}
|
|
ret = fts_test_malloc_free_data_txt(tdata, true);
|
if (ret < 0) {
|
FTS_TEST_ERROR("allocate memory for test data(txt) fail");
|
return ret;
|
}
|
|
/* allocate test data buffer */
|
tdata->buffer_length = (tdata->node.tx_num + 1) * tdata->node.rx_num;
|
tdata->buffer_length *= sizeof(int);
|
FTS_TEST_INFO("test buffer length:%d", tdata->buffer_length);
|
tdata->buffer = (int *)fts_malloc(tdata->buffer_length);
|
if (NULL == tdata->buffer) {
|
FTS_TEST_ERROR("test buffer(%d) malloc fail", tdata->buffer_length);
|
return -ENOMEM;
|
}
|
memset(tdata->buffer, 0, tdata->buffer_length);
|
|
FTS_TEST_FUNC_EXIT();
|
return ret;
|
}
|
|
static int fts_test_main_exit(void)
|
{
|
struct fts_test *tdata = fts_ftest;
|
|
FTS_TEST_FUNC_ENTER();
|
fts_test_save_data_csv(tdata);
|
fts_test_save_result_txt(tdata);
|
|
/* free memory */
|
fts_test_malloc_free_data_txt(tdata, false);
|
fts_test_malloc_free_data_csv(tdata, false);
|
fts_test_malloc_free_thr(tdata, false);
|
/*free test data buffer*/
|
fts_free(tdata->buffer);
|
|
FTS_TEST_FUNC_EXIT();
|
return 0;
|
}
|
|
|
/*
|
* fts_test_get_testparams - get test parameter from ini
|
*/
|
static int fts_test_get_testparams(char *config_name)
|
{
|
int ret = 0;
|
|
ret = fts_test_get_testparam_from_ini(config_name);
|
|
return ret;
|
}
|
|
static int fts_test_start(void)
|
{
|
int testresult = 0;
|
struct fts_test *tdata = fts_ftest;
|
|
if (tdata && tdata->func && tdata->func->start_test) {
|
testresult = tdata->func->start_test();
|
} else {
|
FTS_TEST_ERROR("test func/start_test func is null");
|
}
|
|
return testresult;
|
}
|
|
/*
|
* fts_test_entry - test main entry
|
*
|
* warning - need disable irq & esdcheck before call this function
|
*
|
*/
|
static int fts_test_entry(char *ini_file_name)
|
{
|
int ret = 0;
|
|
/* test initialize */
|
ret = fts_test_main_init();
|
if (ret < 0) {
|
FTS_TEST_ERROR("fts_test_main_init fail");
|
goto test_err;
|
}
|
|
/*Read parse configuration file*/
|
FTS_TEST_SAVE_INFO("ini_file_name:%s\n", ini_file_name);
|
ret = fts_test_get_testparams(ini_file_name);
|
if (ret < 0) {
|
FTS_TEST_ERROR("get testparam fail");
|
goto test_err;
|
}
|
|
/* Start testing according to the test configuration */
|
/* luoguojin ???? */
|
if (true == fts_test_start()) {
|
FTS_TEST_SAVE_INFO("\n\n=======Tp test pass.\n");
|
} else {
|
FTS_TEST_SAVE_INFO("\n\n=======Tp test failure.\n");
|
}
|
|
ret = 0;
|
test_err:
|
fts_test_main_exit();
|
enter_work_mode();
|
return ret;
|
}
|
|
/************************************************************************
|
* Name: fts_test_show
|
* Brief: no
|
* Input: device, device attribute, char buf
|
* Output: no
|
* Return: EPERM
|
***********************************************************************/
|
static ssize_t fts_test_show(struct device *dev, struct device_attribute *attr, char *buf)
|
{
|
return -EPERM;
|
}
|
|
/************************************************************************
|
* Name: fts_test_store
|
* Brief: upgrade from app.bin
|
* Input: device, device attribute, char buf, char count
|
* Output: no
|
* Return: char count
|
***********************************************************************/
|
static ssize_t fts_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
{
|
char fwname[128] = {0};
|
struct fts_ts_data *ts_data = fts_data;
|
struct input_dev *input_dev;
|
|
if (ts_data->suspended) {
|
FTS_INFO("In suspend, no test, return now");
|
return -EINVAL;
|
}
|
|
input_dev = ts_data->input_dev;
|
memset(fwname, 0, sizeof(fwname));
|
sprintf(fwname, "%s", buf);
|
fwname[count - 1] = '\0';
|
FTS_TEST_DBG("fwname:%s.", fwname);
|
|
mutex_lock(&input_dev->mutex);
|
disable_irq(ts_data->irq);
|
|
#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN)
|
fts_esdcheck_switch(DISABLE);
|
#endif
|
|
fts_test_entry(fwname);
|
|
#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN)
|
fts_esdcheck_switch(ENABLE);
|
#endif
|
|
enable_irq(ts_data->irq);
|
mutex_unlock(&input_dev->mutex);
|
|
return count;
|
}
|
|
/* test from test.ini
|
* example:echo "***.ini" > fts_test
|
*/
|
static DEVICE_ATTR(fts_test, S_IRUGO | S_IWUSR, fts_test_show, fts_test_store);
|
|
/* add your attr in here*/
|
static struct attribute *fts_test_attributes[] = {
|
&dev_attr_fts_test.attr,
|
NULL
|
};
|
|
static struct attribute_group fts_test_attribute_group = {
|
.attrs = fts_test_attributes
|
};
|
|
static int fts_test_func_init(void)
|
{
|
int i = 0;
|
int j = 0;
|
int ic_stype = fts_data->ic_info.ids.type;
|
struct test_funcs *func = NULL;
|
int func_count = sizeof(test_func_list) / sizeof(test_func_list[0]);
|
|
FTS_TEST_INFO("init test function");
|
if (0 == func_count) {
|
FTS_TEST_SAVE_ERR("test functions list is NULL, fail\n");
|
return -ENODATA;
|
}
|
|
fts_ftest = (struct fts_test *)kzalloc(sizeof(*fts_ftest), GFP_KERNEL);
|
if (NULL == fts_ftest) {
|
FTS_TEST_ERROR("malloc memory for test fail");
|
return -ENOMEM;
|
}
|
|
for (i = 0; i < func_count; i++) {
|
func = test_func_list[i];
|
for (j = 0; j < FTX_MAX_COMPATIBLE_TYPE; j++) {
|
if (0 == func->ctype[j])
|
break;
|
else if (func->ctype[j] == ic_stype) {
|
FTS_TEST_INFO("match test function,type:%x", (int)func->ctype[j]);
|
fts_ftest->func = func;
|
}
|
}
|
}
|
if (NULL == fts_ftest->func) {
|
FTS_TEST_ERROR("no test function match, can't test");
|
return -ENODATA;
|
}
|
|
return 0;
|
}
|
|
int fts_test_init(struct i2c_client *client)
|
{
|
int ret = 0;
|
|
FTS_TEST_FUNC_ENTER();
|
|
/* get test function, must be the first step */
|
ret = fts_test_func_init();
|
if (ret < 0) {
|
FTS_TEST_SAVE_ERR("test functions init fail\n");
|
return ret;
|
}
|
|
ret = sysfs_create_group(&client->dev.kobj, &fts_test_attribute_group);
|
if (0 != ret) {
|
FTS_TEST_ERROR( "[focal] %s() - ERROR: sysfs_create_group() failed.", __func__);
|
sysfs_remove_group(&client->dev.kobj, &fts_test_attribute_group);
|
} else {
|
FTS_TEST_DBG("[focal] %s() - sysfs_create_group() succeeded.", __func__);
|
}
|
|
FTS_TEST_FUNC_EXIT();
|
|
return ret;
|
}
|
|
int fts_test_exit(struct i2c_client *client)
|
{
|
FTS_TEST_FUNC_ENTER();
|
|
sysfs_remove_group(&client->dev.kobj, &fts_test_attribute_group);
|
fts_free(fts_ftest);
|
FTS_TEST_FUNC_EXIT();
|
return 0;
|
}
|