blob: a6e11501dfd0cafda36f1dfcc78922435b4934b7 [file] [log] [blame]
/*
*
* FocalTech TouchScreen driver.
*
* Copyright (c) 2010-2018, Focaltech 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_ex_fun.c
*
* Author: Focaltech Driver Team
*
* Created: 2016-08-08
*
* Abstract:
*
* Reference:
*
*****************************************************************************/
/*****************************************************************************
* 1.Included header files
*****************************************************************************/
#include "focaltech_core.h"
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
/*create apk debug channel*/
#define PROC_UPGRADE 0
#define PROC_READ_REGISTER 1
#define PROC_WRITE_REGISTER 2
#define PROC_AUTOCLB 4
#define PROC_UPGRADE_INFO 5
#define PROC_WRITE_DATA 6
#define PROC_READ_DATA 7
#define PROC_SET_TEST_FLAG 8
#define PROC_SET_SLAVE_ADDR 10
#define PROC_HW_RESET 11
#define PROC_NAME "ftxxxx-debug"
#define PROC_WRITE_BUF_SIZE 256
#define PROC_READ_BUF_SIZE 256
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
/*****************************************************************************
* Static variables
*****************************************************************************/
enum {
RWREG_OP_READ = 0,
RWREG_OP_WRITE = 1,
};
static struct rwreg_operation_t {
int type; /* 0: read, 1: write */
int reg; /* register */
int len; /* read/write length */
int val; /* length = 1; read: return value, write: op return */
int res; /* 0: success, otherwise: fail */
char *opbuf; /* length >= 1, read return value, write: op return */
} rw_op;
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
/************************************************************************
* Name: fts_debug_write
* Brief:interface of write proc
* Input: file point, data buf, data len, no use
* Output: no
* Return: data len
***********************************************************************/
static ssize_t fts_debug_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
{
u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 };
int buflen = count;
int writelen = 0;
int ret = 0;
char tmp[25];
struct fts_ts_data *ts_data = fts_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) {
FTS_ERROR("apk proc wirte count(%d) fail", (int)count);
return -EINVAL;
}
if (copy_from_user(&writebuf, buff, count)) {
FTS_ERROR("[APK]: copy from user error!!");
return -EFAULT;
}
ts_data->proc_opmode = writebuf[0];
switch (ts_data->proc_opmode) {
case PROC_SET_TEST_FLAG:
FTS_INFO("[APK]: PROC_SET_TEST_FLAG = %x!!", writebuf[1]);
#if FTS_ESDCHECK_EN
if (writebuf[1] == 0) {
fts_esdcheck_switch(ENABLE);
} else {
fts_esdcheck_switch(DISABLE);
}
#endif
break;
case PROC_READ_REGISTER:
writelen = 1;
ret = fts_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
FTS_ERROR("[APK]: write iic error!!");
}
break;
case PROC_WRITE_REGISTER:
writelen = 2;
ret = fts_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
FTS_ERROR("[APK]: write iic error!!");
}
break;
case PROC_SET_SLAVE_ADDR:
#if (FTS_CHIP_TYPE == _FT8201)
FTS_INFO("Original i2c addr 0x%x", client->addr << 1);
if (writebuf[1] != client->addr) {
client->addr = writebuf[1];
FTS_INFO("Change i2c addr 0x%x to 0x%x", client->addr << 1, writebuf[1] << 1);
}
#endif
break;
case PROC_HW_RESET:
snprintf(tmp, PAGE_SIZE, "%s", writebuf + 1);
tmp[buflen - 1] = '\0';
if (strncmp(tmp, "focal_driver", 12) == 0) {
FTS_INFO("APK execute HW Reset");
fts_reset_proc(1);
}
break;
case PROC_READ_DATA:
case PROC_WRITE_DATA:
writelen = buflen - 1;
if (writelen > 0) {
ret = fts_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
FTS_ERROR("[APK]: write iic error!!");
}
}
break;
default:
break;
}
if (ret < 0) {
return ret;
} else {
return count;
}
}
/************************************************************************
* Name: fts_debug_read
* Brief:interface of read proc
* Input: point to the data, no use, no use, read len, no use, no use
* Output: page point to data
* Return: read char number
***********************************************************************/
static ssize_t fts_debug_read(struct file *filp, char __user *buff, size_t count, loff_t *ppos)
{
int ret = 0;
int num_read_chars = 0;
int readlen = 0;
u8 buf[PROC_READ_BUF_SIZE] = { 0 };
struct fts_ts_data *ts_data = fts_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_READ_BUF_SIZE)) {
FTS_ERROR("apk proc read count(%d) fail", (int)count);
return -EINVAL;
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(1);
#endif
switch (ts_data->proc_opmode) {
case PROC_READ_REGISTER:
readlen = 1;
ret = fts_i2c_read(client, NULL, 0, buf, readlen);
if (ret < 0) {
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
FTS_ERROR("[APK]: read iic error!!");
return ret;
}
num_read_chars = 1;
break;
case PROC_READ_DATA:
readlen = count;
ret = fts_i2c_read(client, NULL, 0, buf, readlen);
if (ret < 0) {
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
FTS_ERROR("[APK]: read iic error!!");
return ret;
}
num_read_chars = readlen;
break;
case PROC_WRITE_DATA:
break;
default:
break;
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
if (copy_to_user(buff, buf, num_read_chars)) {
FTS_ERROR("[APK]: copy to user error!!");
return -EFAULT;
}
return num_read_chars;
}
static const struct file_operations fts_proc_fops = {
.owner = THIS_MODULE,
.read = fts_debug_read,
.write = fts_debug_write,
};
#else
/* interface of write proc */
/************************************************************************
* Name: fts_debug_write
* Brief:interface of write proc
* Input: file point, data buf, data len, no use
* Output: no
* Return: data len
***********************************************************************/
static int fts_debug_write(struct file *filp,
const char __user *buff, unsigned long len, void *data)
{
int ret = 0;
u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 };
int buflen = len;
int writelen = 0;
char tmp[25];
struct fts_ts_data *ts_data = fts_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) {
FTS_ERROR("apk proc wirte count(%d) fail", (int)count);
return -EINVAL;
}
if (copy_from_user(&writebuf, buff, buflen)) {
FTS_ERROR("[APK]: copy from user error!!");
return -EFAULT;
}
ts_data->proc_opmode = writebuf[0];
switch (ts_data->proc_opmode) {
case PROC_SET_TEST_FLAG:
FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x!!", writebuf[1]);
#if FTS_ESDCHECK_EN
if (writebuf[1] == 0) {
fts_esdcheck_switch(ENABLE);
} else {
fts_esdcheck_switch(DISABLE);
}
#endif
break;
case PROC_READ_REGISTER:
writelen = 1;
ret = fts_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
FTS_ERROR("[APK]: write iic error!!n");
}
break;
case PROC_WRITE_REGISTER:
writelen = 2;
ret = fts_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
FTS_ERROR("[APK]: write iic error!!");
}
break;
case PROC_SET_SLAVE_ADDR:
#if (FTS_CHIP_TYPE == _FT8201)
ret = client->addr;
FTS_DEBUG("Original i2c addr 0x%x ", ret << 1 );
if (writebuf[1] != client->addr) {
client->addr = writebuf[1];
FTS_DEBUG("Change i2c addr 0x%x to 0x%x", ret << 1, writebuf[1] << 1);
}
#endif
break;
case PROC_HW_RESET:
snprintf(tmp, PAGE_SIZE, "%s", writebuf + 1);
tmp[buflen - 1] = '\0';
if (strncmp(tmp, "focal_driver", 12) == 0) {
FTS_INFO("Begin HW Reset");
fts_reset_proc(1);
}
break;
case PROC_READ_DATA:
case PROC_WRITE_DATA:
writelen = len - 1;
if (writelen > 0) {
ret = fts_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
FTS_ERROR("[APK]: write iic error!!");
}
}
break;
default:
break;
}
if (ret < 0) {
return ret;
} else {
return len;
}
}
/* interface of read proc */
/************************************************************************
* Name: fts_debug_read
* Brief:interface of read proc
* Input: point to the data, no use, no use, read len, no use, no use
* Output: page point to data
* Return: read char number
***********************************************************************/
static int fts_debug_read( char *page, char **start,
off_t off, int count, int *eof, void *data )
{
int ret = 0;
u8 buf[PROC_READ_BUF_SIZE] = { 0 };
int num_read_chars = 0;
int readlen = 0;
struct fts_ts_data *ts_data = fts_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_READ_BUF_SIZE)) {
FTS_ERROR("apk proc read count(%d) fail", (int)count);
return -EINVAL;
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(1);
#endif
switch (ts_data->proc_opmode) {
case PROC_READ_REGISTER:
readlen = 1;
ret = fts_i2c_read(client, NULL, 0, buf, readlen);
if (ret < 0) {
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
FTS_ERROR("[APK]: read iic error!!");
return ret;
}
num_read_chars = 1;
break;
case PROC_READ_DATA:
readlen = count;
ret = fts_i2c_read(client, NULL, 0, buf, readlen);
if (ret < 0) {
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
FTS_ERROR("[APK]: read iic error!!");
return ret;
}
num_read_chars = readlen;
break;
case PROC_WRITE_DATA:
break;
default:
break;
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
memcpy(page, buf, num_read_chars);
return num_read_chars;
}
#endif
/************************************************************************
* Name: fts_create_apk_debug_channel
* Brief: create apk debug channel
* Input: i2c info
* Output:
* Return: return 0 if success
***********************************************************************/
int fts_create_apk_debug_channel(struct fts_ts_data *ts_data)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
ts_data->proc = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops);
#else
ts_data->proc = create_proc_entry(PROC_NAME, 0777, NULL);
#endif
if (NULL == ts_data->proc) {
FTS_ERROR("create proc entry fail");
return -ENOMEM;
} else {
FTS_INFO("Create proc entry success!");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0))
ts_data->proc->write_proc = fts_debug_write;
ts_data->proc->read_proc = fts_debug_read;
#endif
}
return 0;
}
/************************************************************************
* Name: fts_release_apk_debug_channel
* Brief: release apk debug channel
* Input:
* Output:
* Return:
***********************************************************************/
void fts_release_apk_debug_channel(struct fts_ts_data *ts_data)
{
if (ts_data->proc) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
proc_remove(ts_data->proc);
#else
remove_proc_entry(PROC_NAME, NULL);
#endif
}
}
/************************************************************************
* sysfs interface
***********************************************************************/
/*
* fts_hw_reset interface
*/
static ssize_t fts_hw_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return -EPERM;
}
static ssize_t fts_hw_reset_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct input_dev *input_dev = fts_data->input_dev;
ssize_t count = 0;
mutex_lock(&input_dev->mutex);
fts_reset_proc(1);
count = snprintf(buf, PAGE_SIZE, "hw reset executed\n");
mutex_unlock(&input_dev->mutex);
return count;
}
/*
* fts_toggle_hw_reset interface
* This grants userspace access to toggle reset pin so caller can manage timing
*/
static ssize_t fts_toggle_reset_pin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
if (FTS_SYSFS_ECHO_ON(buf)){
gpio_direction_output(fts_data->pdata->reset_gpio, 1);
}
else if (FTS_SYSFS_ECHO_OFF(buf)) {
gpio_direction_output(fts_data->pdata->reset_gpio, 0);
}
msleep(20);
return count;
}
static ssize_t fts_toggle_reset_pin_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return -EPERM;
}
/*
* fts_irq interface
*/
static ssize_t fts_irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
if (FTS_SYSFS_ECHO_ON(buf)) {
FTS_INFO("[EX-FUN]enable irq");
fts_irq_enable();
} else if (FTS_SYSFS_ECHO_OFF(buf)) {
FTS_INFO("[EX-FUN]disable irq");
fts_irq_disable();
}
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_irq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return -EPERM;
}
/*
* fts_tpfwver interface
*/
static ssize_t fts_tpfwver_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct fts_ts_data *ts_data = fts_data;
struct input_dev *input_dev = ts_data->input_dev;
struct i2c_client *client = ts_data->client;
ssize_t num_read_chars = 0;
u8 fwver = 0;
mutex_lock(&input_dev->mutex);
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(1);
#endif
if (fts_i2c_read_reg(client, FTS_REG_FW_VER, &fwver) < 0) {
num_read_chars = snprintf(buf, PAGE_SIZE, "I2c transfer error!\n");
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
if ((fwver == 0xFF) || (fwver == 0x00))
num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n");
else
num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver);
mutex_unlock(&input_dev->mutex);
return num_read_chars;
}
static ssize_t fts_tpfwver_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return -EPERM;
}
/************************************************************************
* Name: fts_tprwreg_show
* Brief: no
* Input: device, device attribute, char buf
* Output: no
* Return: EPERM
***********************************************************************/
static ssize_t fts_tprwreg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int count;
int i;
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
if (rw_op.len < 0) {
count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n");
} else if (rw_op.len == 1) {
if (RWREG_OP_READ == rw_op.type) {
if (rw_op.res == 0) {
count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val);
} else {
count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg, rw_op.res);
}
} else {
if (rw_op.res == 0) {
count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg, rw_op.val);
} else {
count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg, rw_op.res);
}
}
} else {
if (RWREG_OP_READ == rw_op.type) {
count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len);
count += snprintf(buf + count, PAGE_SIZE, "Result: ");
if (rw_op.res) {
count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res);
} else {
if (rw_op.opbuf) {
for (i = 0; i < rw_op.len; i++) {
count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
}
count += snprintf(buf + count, PAGE_SIZE, "\n");
}
}
} else {
;
count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1);
count += snprintf(buf + count, PAGE_SIZE, "Write Data: ");
if (rw_op.opbuf) {
for (i = 1; i < rw_op.len; i++) {
count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
}
count += snprintf(buf + count, PAGE_SIZE, "\n");
}
if (rw_op.res) {
count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res);
} else {
count += snprintf(buf + count, PAGE_SIZE, "Result: success\n");
}
}
/*if (rw_op.opbuf) {
kfree(rw_op.opbuf);
rw_op.opbuf = NULL;
}*/
}
mutex_unlock(&input_dev->mutex);
return count;
}
static int shex_to_int(const char *hex_buf, int size)
{
int i;
int base = 1;
int value = 0;
char single;
for (i = size - 1; i >= 0; i--) {
single = hex_buf[i];
if ((single >= '0') && (single <= '9')) {
value += (single - '0') * base;
} else if ((single >= 'a') && (single <= 'z')) {
value += (single - 'a' + 10) * base;
} else if ((single >= 'A') && (single <= 'Z')) {
value += (single - 'A' + 10) * base;
} else {
return -EINVAL;
}
base *= 16;
}
return value;
}
static u8 shex_to_u8(const char *hex_buf, int size)
{
return (u8)shex_to_int(hex_buf, size);
}
/*
* Format buf:
* [0]: '0' write, '1' read(reserved)
* [1-2]: addr, hex
* [3-4]: length, hex
* [5-6]...[n-(n+1)]: data, hex
*/
static int fts_parse_buf(const char *buf, size_t cmd_len)
{
int length;
int i;
char *tmpbuf;
rw_op.reg = shex_to_u8(buf + 1, 2);
length = shex_to_int(buf + 3, 2);
if (buf[0] == '1') {
rw_op.len = length;
rw_op.type = RWREG_OP_READ;
FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len);
} else {
if (cmd_len < (length * 2 + 5)) {
pr_err("data invalided!\n");
return -EINVAL;
}
FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length);
/* first byte is the register addr */
rw_op.type = RWREG_OP_WRITE;
rw_op.len = length + 1;
}
if (rw_op.len > 0) {
tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL);
if (!tmpbuf) {
FTS_ERROR("allocate memory failed!\n");
return -ENOMEM;
}
if (RWREG_OP_WRITE == rw_op.type) {
tmpbuf[0] = rw_op.reg & 0xFF;
FTS_DEBUG("write buffer: ");
for (i = 1; i < rw_op.len; i++) {
tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2);
FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF);
}
}
rw_op.opbuf = tmpbuf;
}
return rw_op.len;
}
/************************************************************************
* Name: fts_tprwreg_store
* Brief: read/write register
* Input: device, device attribute, char buf, char count
* Output: print register value
* Return: char count
***********************************************************************/
static ssize_t fts_tprwreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct input_dev *input_dev = fts_data->input_dev;
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
ssize_t cmd_length = 0;
mutex_lock(&input_dev->mutex);
cmd_length = count - 1;
if (rw_op.opbuf) {
kfree(rw_op.opbuf);
rw_op.opbuf = NULL;
}
FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf);
/* compatible old ops */
if (2 == cmd_length) {
rw_op.type = RWREG_OP_READ;
rw_op.len = 1;
rw_op.reg = shex_to_int(buf, 2);
} else if (4 == cmd_length) {
rw_op.type = RWREG_OP_WRITE;
rw_op.len = 1;
rw_op.reg = shex_to_int(buf, 2);
rw_op.val = shex_to_int(buf + 2, 2);
} else if (cmd_length < 5) {
FTS_ERROR("Invalid cmd buffer");
mutex_unlock(&input_dev->mutex);
return -EINVAL;
} else {
rw_op.len = fts_parse_buf(buf, cmd_length);
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(1);
#endif
if (rw_op.len < 0) {
FTS_ERROR("cmd buffer error!");
} else {
if (RWREG_OP_READ == rw_op.type) {
if (rw_op.len == 1) {
u8 reg, val;
reg = rw_op.reg & 0xFF;
rw_op.res = fts_i2c_read_reg(client, reg, &val);
rw_op.val = val;
} else {
char reg;
reg = rw_op.reg & 0xFF;
rw_op.res = fts_i2c_read(client, &reg, 1, rw_op.opbuf, rw_op.len);
}
if (rw_op.res < 0) {
FTS_ERROR("Could not read 0x%02x", rw_op.reg);
} else {
FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len);
rw_op.res = 0;
}
} else {
if (rw_op.len == 1) {
u8 reg, val;
reg = rw_op.reg & 0xFF;
val = rw_op.val & 0xFF;
rw_op.res = fts_i2c_write_reg(client, reg, val);
} else {
rw_op.res = fts_i2c_write(client, rw_op.opbuf, rw_op.len);
}
if (rw_op.res < 0) {
FTS_ERROR("Could not write 0x%02x", rw_op.reg);
} else {
FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len);
rw_op.res = 0;
}
}
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
mutex_unlock(&input_dev->mutex);
return count;
}
/*
* fts_upgrade_bin interface
*/
static ssize_t fts_fwupgradebin_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return -EPERM;
}
static ssize_t fts_fwupgradebin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
char fwname[FILE_NAME_LENGTH];
struct fts_ts_data *ts_data = fts_data;
struct input_dev *input_dev = ts_data->input_dev;
struct i2c_client *client = ts_data->client;
if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) {
FTS_ERROR("fw bin name's length(%d) fail", (int)count);
return -EINVAL;
}
memset(fwname, 0, sizeof(fwname));
snprintf(fwname, PAGE_SIZE, "%s", buf);
fwname[count - 1] = '\0';
FTS_INFO("upgrade with bin file through sysfs node");
mutex_lock(&input_dev->mutex);
ts_data->fw_loading = 1;
fts_irq_disable();
#if FTS_ESDCHECK_EN
fts_esdcheck_switch(DISABLE);
#endif
fts_upgrade_bin(client, fwname, 0);
#if FTS_ESDCHECK_EN
fts_esdcheck_switch(ENABLE);
#endif
fts_irq_enable();
ts_data->fw_loading = 0;
mutex_unlock(&input_dev->mutex);
return count;
}
/*
* fts_force_upgrade interface
*/
static ssize_t fts_fwforceupg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return -EPERM;
}
static ssize_t fts_fwforceupg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
char fwname[FILE_NAME_LENGTH];
struct fts_ts_data *ts_data = fts_data;
struct input_dev *input_dev = ts_data->input_dev;
struct i2c_client *client = ts_data->client;
if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) {
FTS_ERROR("fw bin name's length(%d) fail", (int)count);
return -EINVAL;
}
memset(fwname, 0, sizeof(fwname));
snprintf(fwname, PAGE_SIZE, "%s", buf);
fwname[count - 1] = '\0';
FTS_INFO("force upgrade through sysfs node");
mutex_lock(&input_dev->mutex);
ts_data->fw_loading = 1;
fts_irq_disable();
#if FTS_ESDCHECK_EN
fts_esdcheck_switch(DISABLE);
#endif
fts_upgrade_bin(client, fwname, 1);
#if FTS_ESDCHECK_EN
fts_esdcheck_switch(ENABLE);
#endif
fts_irq_enable();
ts_data->fw_loading = 0;
mutex_unlock(&input_dev->mutex);
return count;
}
/*
* fts_driver_info interface
*/
static ssize_t fts_driverinfo_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int count = 0;
struct fts_ts_data *ts_data = fts_data;
struct fts_ts_platform_data *pdata = ts_data->pdata;
struct input_dev *input_dev = ts_data->input_dev;
mutex_lock(&input_dev->mutex);
count += snprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n", FTS_DRIVER_VERSION);
count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n",
pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max);
count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n", pdata->max_touch_number);
count += snprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n",
pdata->reset_gpio, pdata->irq_gpio, ts_data->irq);
count += snprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n",
ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl);
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_driverinfo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return -EPERM;
}
/*
* fts_dump_reg interface
*/
static ssize_t fts_dumpreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return -EPERM;
}
static ssize_t fts_dumpreg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int count = 0;
u8 val = 0;
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(1);
#endif
fts_i2c_read_reg(client, FTS_REG_POWER_MODE, &val);
count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_FW_VER, &val);
count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_LIC_VER, &val);
count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_IDE_PARA_VER_ID, &val);
count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_IDE_PARA_STATUS, &val);
count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &val);
count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_LCD_BUSY_NUM, &val);
count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &val);
count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_CHARGER_MODE_EN, &val);
count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_INT_CNT, &val);
count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val);
fts_i2c_read_reg(client, FTS_REG_FLOW_WORK_CNT, &val);
count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val);
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
mutex_unlock(&input_dev->mutex);
return count;
}
#define GET_FRAME_NUM 20
int data_flag = 0;
int get_frame_num = 10;
/*
* fts_test_save_test_data - Save test data to /tmp/
*/
static int fts_save_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_FUNC_ENTER();
memset(filepath, 0, sizeof(filepath));
sprintf(filepath, "%s%s", "/tmp/", file_name);
if (NULL == pfile) {
pfile = filp_open(filepath, O_TRUNC | O_CREAT | O_RDWR, 0);
}
if (IS_ERR(pfile)) {
FTS_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_FUNC_EXIT();
return 0;
}
static int fts_read_differ(struct i2c_client *client)
{
int count = 0;
int i = 0;
int j = 0;
int k = 0;
u8 tx_num = 0;
u8 rx_num = 0;
u8 regvalue = 0x00;
u8 regaddr = 0x00;
char *read_buffer = NULL;
u8 *tmp_buffer = NULL;
int frame_num = 0;
short rawdata = 0;
u8 regval = 0x00;
/*1 enter factory mode */
fts_i2c_write_reg(client, 0x00, 0x40);
msleep(200);
/*2 get channel num*/
fts_i2c_read_reg(client, 0x02, &tx_num);
fts_i2c_read_reg(client, 0x03, &rx_num);
/*3 switch differ mode */
fts_i2c_read_reg(client, 0x06, &regval);
fts_i2c_write_reg(client, 0x06, 0x01);
msleep(100);
read_buffer = vmalloc(tx_num*rx_num*2*sizeof(char)*get_frame_num*10);
if(read_buffer==NULL)
return -1;
memset(read_buffer, 0x00, tx_num*rx_num*2*sizeof(char)*get_frame_num*10);
tmp_buffer = vmalloc(tx_num*rx_num*2*sizeof(u8));
if(tmp_buffer==NULL)
goto release_read_buffer;
memset(tmp_buffer, 0x00, tx_num*rx_num*2*sizeof(u8));
count += snprintf(read_buffer + count, PAGE_SIZE, "tp channel: tx = %d, rx = %d\n", tx_num, rx_num);
/*4 read rawdata*/
for (frame_num = 0; frame_num < (get_frame_num+1); frame_num++) {
/*scan*/
fts_i2c_read_reg(client, 0x00, &regvalue);
regvalue |= 0x80;
fts_i2c_write_reg(client, 0x00, regvalue);
for (i = 0; i < 50; i++) {
msleep(16);
fts_i2c_read_reg(client, 0x00, &regvalue);
if (0x40 == regvalue)
break;
}
/*get rawdata*/
fts_i2c_write_reg(client, 0x01, 0xAA);
regaddr = 0x36;
fts_i2c_read(client, &regaddr, 1, tmp_buffer, tx_num*rx_num*2);
if(frame_num == 0)
continue;
FTS_DEBUG("Frame_num : %d \n", frame_num);
count += snprintf(read_buffer + count, PAGE_SIZE, "Frame_num : %d \n", frame_num);
for (i = 0; i < tx_num; i++) {
for (j = 0; j < rx_num; j++) {
k = rx_num * i + j;
rawdata = ((short)(tmp_buffer[k * 2] << 8) + tmp_buffer[k * 2 + 1]);
count += snprintf(read_buffer + count, PAGE_SIZE, "%5d ", rawdata);
}
count += snprintf(read_buffer + count, PAGE_SIZE, "\n");
}
}
fts_save_data("differ.txt", read_buffer, count);
vfree(tmp_buffer);
release_read_buffer:
vfree(read_buffer);
/*switch rawdata*/
fts_i2c_write_reg(client, 0x06, regval);
/*5 enter work mode*/
fts_i2c_write_reg(client, 0x00, 0x00);
msleep(200);
return 0;
}
static int fts_read_rawdata(struct i2c_client *client)
{
int count = 0;
int i = 0;
int j = 0;
int k = 0;
u8 tx_num = 0;
u8 rx_num = 0;
u8 regvalue = 0x00;
u8 regaddr = 0x00;
char *read_buffer = NULL;
u8 *tmp_buffer = NULL;
int frame_num = 0;
int rawdata = 0;
/*1 enter factory mode */
fts_i2c_write_reg(client, 0x00, 0x40);
msleep(200);
/*2 get channel num*/
fts_i2c_read_reg(client, 0x02, &tx_num);
fts_i2c_read_reg(client, 0x03, &rx_num);
read_buffer = vmalloc(tx_num*rx_num*2*sizeof(char)*get_frame_num*10);
if(read_buffer==NULL)
return -1;
memset(read_buffer, 0x00, tx_num*rx_num*2*sizeof(char)*get_frame_num*10);
tmp_buffer = vmalloc(tx_num*rx_num*2*sizeof(u8));
if(tmp_buffer==NULL)
goto release_read_buffer;
memset(tmp_buffer, 0x00, tx_num*rx_num*2*sizeof(u8));
count += snprintf(read_buffer + count, PAGE_SIZE, "tp channel: tx = %d, rx = %d\n", tx_num, rx_num);
/*4 read rawdata*/
for (frame_num = 0; frame_num < (get_frame_num+1); frame_num++) {
/*scan*/
fts_i2c_read_reg(client, 0x00, &regvalue);
regvalue |= 0x80;
fts_i2c_write_reg(client, 0x00, regvalue);
for (i = 0; i < 50; i++) {
msleep(16);
fts_i2c_read_reg(client, 0x00, &regvalue);
if (0x40 == regvalue)
break;
}
/*get rawdata*/
fts_i2c_write_reg(client, 0x01, 0xAA);
regaddr = 0x36;
fts_i2c_read(client, &regaddr, 1, tmp_buffer, tx_num*rx_num*2);
if(frame_num == 0)
continue;
FTS_INFO("Frame_num : %d \n", frame_num);
count += snprintf(read_buffer + count, PAGE_SIZE, "Frame_num : %d \n", frame_num);
for (i = 0; i < tx_num; i++) {
for (j = 0; j < rx_num; j++) {
k = rx_num * i + j;
rawdata = (tmp_buffer[k * 2] << 8) + tmp_buffer[k * 2 + 1];
count += snprintf(read_buffer + count, PAGE_SIZE, "%5d ", rawdata);
}
count += snprintf(read_buffer + count, PAGE_SIZE, "\n");
}
}
fts_save_data("rawdata.txt", read_buffer, count);
vfree(tmp_buffer);
release_read_buffer:
vfree(read_buffer);
/*5 enter work mode*/
fts_i2c_write_reg(client, 0x00, 0x00);
msleep(200);
return 0;
}
/*
* fts_rawdata interface
*/
static ssize_t fts_rawdata_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
sscanf(buf, "%d %d", &data_flag, &get_frame_num);
FTS_INFO("data_flag = %d, read frame num = %d", data_flag, get_frame_num);
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_rawdata_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int count = 0;
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(1);
#endif
if (data_flag == 1) {
FTS_INFO("read differ frame num = %d", get_frame_num);
fts_read_differ(client);
}
else {
FTS_INFO("read rawdata frame num = %d", get_frame_num);
fts_read_rawdata(client);
}
#if FTS_ESDCHECK_EN
fts_esdcheck_proc_busy(0);
#endif
count = snprintf(buf, PAGE_SIZE, "cat data finish\n");
mutex_unlock(&input_dev->mutex);
return count;
}
/* get the fw version example:cat fw_version */
static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store);
/* read and write register(s)
* All data type is **HEX**
* Single Byte:
* read: echo 88 > rw_reg ---read register 0x88
* write: echo 8807 > rw_reg ---write 0x07 into register 0x88
* Multi-bytes:
* [0:rw-flag][1-2: reg addr, hex][3-4: length, hex][5-6...n-n+1: write data, hex]
* rw-flag: 0, write; 1, read
* read: echo 10005 > rw_reg ---read reg 0x00-0x05
* write: echo 000050102030405 > rw_reg ---write reg 0x00-0x05 as 01,02,03,04,05
* Get result:
* cat rw_reg
*/
static DEVICE_ATTR(fts_rw_reg, S_IRUGO | S_IWUSR, fts_tprwreg_show, fts_tprwreg_store);
/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */
static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store);
static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_store);
static DEVICE_ATTR(fts_driver_info, S_IRUGO | S_IWUSR, fts_driverinfo_show, fts_driverinfo_store);
static DEVICE_ATTR(fts_dump_reg, S_IRUGO | S_IWUSR, fts_dumpreg_show, fts_dumpreg_store);
static DEVICE_ATTR(fts_hw_reset, S_IRUGO | S_IWUSR, fts_hw_reset_show, fts_hw_reset_store);
static DEVICE_ATTR(fts_toggle_reset_pin, S_IRUGO|S_IWUSR, fts_toggle_reset_pin_show, fts_toggle_reset_pin_store);
static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store);
static DEVICE_ATTR(fts_rawdata, S_IRUGO | S_IWUSR, fts_rawdata_show, fts_rawdata_store);
/* add your attr in here*/
static struct attribute *fts_attributes[] = {
&dev_attr_fts_fw_version.attr,
&dev_attr_fts_rw_reg.attr,
&dev_attr_fts_dump_reg.attr,
&dev_attr_fts_upgrade_bin.attr,
&dev_attr_fts_force_upgrade.attr,
&dev_attr_fts_driver_info.attr,
&dev_attr_fts_hw_reset.attr,
&dev_attr_fts_toggle_reset_pin.attr,
&dev_attr_fts_irq.attr,
&dev_attr_fts_rawdata.attr,
NULL
};
static struct attribute_group fts_attribute_group = {
.attrs = fts_attributes
};
/************************************************************************
* Name: fts_create_sysfs
* Brief: create sysfs interface
* Input:
* Output:
* Return: return 0 if success
***********************************************************************/
int fts_create_sysfs(struct i2c_client *client)
{
int ret = 0;
ret = sysfs_create_group(&client->dev.kobj, &fts_attribute_group);
if (ret) {
FTS_ERROR("[EX]: sysfs_create_group() failed!!");
sysfs_remove_group(&client->dev.kobj, &fts_attribute_group);
return -ENOMEM;
} else {
FTS_INFO("[EX]: sysfs_create_group() succeeded!!");
}
return ret;
}
/************************************************************************
* Name: fts_remove_sysfs
* Brief: remove sysfs interface
* Input:
* Output:
* Return:
***********************************************************************/
int fts_remove_sysfs(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &fts_attribute_group);
return 0;
}