/* * * FocalTech TouchScreen driver. * * Copyright (c) 2012-2019, 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 #include "focaltech_core.h" /***************************************************************************** * Private constant and macro definitions using #define *****************************************************************************/ #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_READ_STATUS 12 #define PROC_SET_BOOT_MODE 13 #define PROC_ENTER_TEST_ENVIRONMENT 14 #define PROC_NAME "ftxxxx-debug" #define PROC_BUF_SIZE 256 /***************************************************************************** * Private enumerations, structures and unions using typedef *****************************************************************************/ enum { RWREG_OP_READ = 0, RWREG_OP_WRITE = 1, }; /***************************************************************************** * Static variables *****************************************************************************/ 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)) static ssize_t fts_debug_write( struct file *filp, const char __user *buff, size_t count, loff_t *ppos) { u8 *writebuf = NULL; u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; int buflen = count; int writelen = 0; int ret = 0; char tmp[PROC_BUF_SIZE]; struct fts_ts_data *ts_data = fts_data; struct ftxxxx_proc *proc = &ts_data->proc; if ((buflen <= 1) || (buflen > PAGE_SIZE)) { FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE); return -EINVAL; } if (buflen > PROC_BUF_SIZE) { writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); if (NULL == writebuf) { FTS_ERROR("apk proc wirte buf zalloc fail"); return -ENOMEM; } } else { writebuf = tmpbuf; } if (copy_from_user(writebuf, buff, buflen)) { FTS_ERROR("[APK]: copy from user error!!"); ret = -EFAULT; goto proc_write_err; } proc->opmode = writebuf[0]; switch (proc->opmode) { case PROC_SET_TEST_FLAG: FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); if (writebuf[1] == 0) { #if FTS_ESDCHECK_EN fts_esdcheck_switch(ENABLE); #endif } else { #if FTS_ESDCHECK_EN fts_esdcheck_switch(DISABLE); #endif } break; case PROC_READ_REGISTER: proc->cmd[0] = writebuf[1]; break; case PROC_WRITE_REGISTER: ret = fts_write_reg(writebuf[1], writebuf[2]); if (ret < 0) { FTS_ERROR("PROC_WRITE_REGISTER write error"); goto proc_write_err; } break; case PROC_READ_DATA: writelen = buflen - 1; if (writelen >= FTX_MAX_COMMMAND_LENGTH) { FTS_ERROR("cmd(PROC_READ_DATA) len(%d) fail", writelen); goto proc_write_err; } memcpy(proc->cmd, writebuf + 1, writelen); proc->cmd_len = writelen; ret = fts_write(writebuf + 1, writelen); if (ret < 0) { FTS_ERROR("PROC_READ_DATA write error"); goto proc_write_err; } break; case PROC_WRITE_DATA: writelen = buflen - 1; ret = fts_write(writebuf + 1, writelen); if (ret < 0) { FTS_ERROR("PROC_WRITE_DATA write error"); goto proc_write_err; } break; case PROC_SET_SLAVE_ADDR: break; case PROC_HW_RESET: snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); tmp[buflen - 1] = '\0'; if (strncmp(tmp, "focal_driver", 12) == 0) { FTS_INFO("APK execute HW Reset"); fts_reset_proc(0); } break; case PROC_SET_BOOT_MODE: FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); if (0 == writebuf[1]) { ts_data->fw_is_running = true; } else { ts_data->fw_is_running = false; } break; case PROC_ENTER_TEST_ENVIRONMENT: FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); if (0 == writebuf[1]) { fts_enter_test_environment(0); } else { fts_enter_test_environment(1); } break; default: break; } ret = buflen; proc_write_err: if ((buflen > PROC_BUF_SIZE) && writebuf) { kfree(writebuf); writebuf = NULL; } return ret; } 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 buflen = count; u8 *readbuf = NULL; u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; struct fts_ts_data *ts_data = fts_data; struct ftxxxx_proc *proc = &ts_data->proc; if ((buflen <= 0) || (buflen > PAGE_SIZE)) { FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE); return -EINVAL; } if (buflen > PROC_BUF_SIZE) { readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); if (NULL == readbuf) { FTS_ERROR("apk proc wirte buf zalloc fail"); return -ENOMEM; } } else { readbuf = tmpbuf; } #if FTS_ESDCHECK_EN fts_esdcheck_proc_busy(1); #endif switch (proc->opmode) { case PROC_READ_REGISTER: num_read_chars = 1; ret = fts_read_reg(proc->cmd[0], &readbuf[0]); if (ret < 0) { FTS_ERROR("PROC_READ_REGISTER read error"); goto proc_read_err; } break; case PROC_WRITE_REGISTER: break; case PROC_READ_DATA: num_read_chars = buflen; ret = fts_read(NULL, 0, readbuf, num_read_chars); if (ret < 0) { FTS_ERROR("PROC_READ_DATA read error"); goto proc_read_err; } break; case PROC_WRITE_DATA: break; default: break; } #if FTS_ESDCHECK_EN fts_esdcheck_proc_busy(0); #endif if (copy_to_user(buff, readbuf, num_read_chars)) { FTS_ERROR("copy to user error"); ret = -EFAULT; goto proc_read_err; } ret = num_read_chars; proc_read_err: if ((buflen > PROC_BUF_SIZE) && readbuf) { kfree(readbuf); readbuf = NULL; } return ret; } static const struct proc_ops fts_proc_fops = { .proc_read = fts_debug_read, .proc_write = fts_debug_write, }; #else static int fts_debug_write(struct file *filp, const char __user *buff, unsigned long len, void *data) { u8 *writebuf = NULL; u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; int buflen = count; int writelen = 0; int ret = 0; char tmp[PROC_BUF_SIZE]; struct fts_ts_data *ts_data = fts_data; struct ftxxxx_proc *proc = &ts_data->proc; if ((buflen <= 1) || (buflen > PAGE_SIZE)) { FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE); return -EINVAL; } if (buflen > PROC_BUF_SIZE) { writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); if (NULL == writebuf) { FTS_ERROR("apk proc wirte buf zalloc fail"); return -ENOMEM; } } else { writebuf = tmpbuf; } if (copy_from_user(writebuf, buff, buflen)) { FTS_ERROR("[APK]: copy from user error!!"); ret = -EFAULT; goto proc_write_err; } proc->opmode = writebuf[0]; switch (proc->opmode) { case PROC_SET_TEST_FLAG: FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); if (writebuf[1] == 0) { #if FTS_ESDCHECK_EN fts_esdcheck_switch(ENABLE); #endif } else { #if FTS_ESDCHECK_EN fts_esdcheck_switch(DISABLE); #endif } break; case PROC_READ_REGISTER: proc->cmd[0] = writebuf[1]; break; case PROC_WRITE_REGISTER: ret = fts_write_reg(writebuf[1], writebuf[2]); if (ret < 0) { FTS_ERROR("PROC_WRITE_REGISTER write error"); goto proc_write_err; } break; case PROC_READ_DATA: writelen = buflen - 1; if (writelen >= FTX_MAX_COMMMAND_LENGTH) { FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen); goto proc_write_err; } memcpy(proc->cmd, writebuf + 1, writelen); proc->cmd_len = writelen; ret = fts_write(writebuf + 1, writelen); if (ret < 0) { FTS_ERROR("PROC_READ_DATA write error"); goto proc_write_err; } break; case PROC_WRITE_DATA: writelen = buflen - 1; ret = fts_write(writebuf + 1, writelen); if (ret < 0) { FTS_ERROR("PROC_WRITE_DATA write error"); goto proc_write_err; } break; case PROC_SET_SLAVE_ADDR: break; case PROC_HW_RESET: snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); tmp[buflen - 1] = '\0'; if (strncmp(tmp, "focal_driver", 12) == 0) { FTS_INFO("APK execute HW Reset"); fts_reset_proc(0); } break; case PROC_SET_BOOT_MODE: FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); if (0 == writebuf[1]) { ts_data->fw_is_running = true; } else { ts_data->fw_is_running = false; } break; case PROC_ENTER_TEST_ENVIRONMENT: FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); if (0 == writebuf[1]) { fts_enter_test_environment(0); } else { fts_enter_test_environment(1); } break; default: break; } ret = buflen; proc_write_err: if ((buflen > PROC_BUF_SIZE) && writebuf) { kfree(writebuf); writebuf = NULL; } return ret; } static int fts_debug_read( char *page, char **start, off_t off, int count, int *eof, void *data ) { int ret = 0; int num_read_chars = 0; int buflen = count; u8 *readbuf = NULL; u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; struct fts_ts_data *ts_data = fts_data; struct ftxxxx_proc *proc = &ts_data->proc; if ((buflen <= 0) || (buflen > PAGE_SIZE)) { FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE); return -EINVAL; } if (buflen > PROC_BUF_SIZE) { readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); if (NULL == readbuf) { FTS_ERROR("apk proc wirte buf zalloc fail"); return -ENOMEM; } } else { readbuf = tmpbuf; } #if FTS_ESDCHECK_EN fts_esdcheck_proc_busy(1); #endif switch (proc->opmode) { case PROC_READ_REGISTER: num_read_chars = 1; ret = fts_read_reg(proc->cmd[0], &readbuf[0]); if (ret < 0) { FTS_ERROR("PROC_READ_REGISTER read error"); goto proc_read_err; } break; case PROC_WRITE_REGISTER: break; case PROC_READ_DATA: num_read_chars = buflen; ret = fts_read(NULL, 0, readbuf, num_read_chars); if (ret < 0) { FTS_ERROR("PROC_READ_DATA read error"); goto proc_read_err; } break; case PROC_WRITE_DATA: break; default: break; } #if FTS_ESDCHECK_EN fts_esdcheck_proc_busy(0); #endif if (copy_to_user(buff, readbuf, num_read_chars)) { FTS_ERROR("copy to user error"); ret = -EFAULT; goto proc_read_err; } ret = num_read_chars; proc_read_err: if ((buflen > PROC_BUF_SIZE) && readbuf) { kfree(readbuf); readbuf = NULL; } return ret; } #endif int fts_create_apk_debug_channel(struct fts_ts_data *ts_data) { struct ftxxxx_proc *proc = &ts_data->proc; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) proc->proc_entry = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops); if (NULL == proc->proc_entry) { FTS_ERROR("create proc entry fail"); return -ENOMEM; } #else proc->proc_entry = create_proc_entry(PROC_NAME, 0777, NULL); if (NULL == proc->proc_entry) { FTS_ERROR("create proc entry fail"); return -ENOMEM; } proc->proc_entry->write_proc = fts_debug_write; proc->proc_entry->read_proc = fts_debug_read; #endif FTS_INFO("Create proc entry success!"); return 0; } void fts_release_apk_debug_channel(struct fts_ts_data *ts_data) { struct ftxxxx_proc *proc = &ts_data->proc; if (proc->proc_entry) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) proc_remove(proc->proc_entry); #else remove_proc_entry(PROC_NAME, NULL); #endif } } /************************************************************************ * sysfs interface ***********************************************************************/ /* fts_hw_reset interface */ 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(0); count = snprintf(buf, PAGE_SIZE, "hw reset executed\n"); mutex_unlock(&input_dev->mutex); return count; } static ssize_t fts_hw_reset_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return -EPERM; } /* fts_irq interface */ static ssize_t fts_irq_show( struct device *dev, struct device_attribute *attr, char *buf) { ssize_t count = 0; struct irq_desc *desc = irq_to_desc(fts_data->irq); count = snprintf(buf, PAGE_SIZE, "irq_depth:%d\n", desc->depth); return count; } 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("enable irq"); fts_irq_enable(); } else if (FTS_SYSFS_ECHO_OFF(buf)) { FTS_INFO("disable irq"); fts_irq_disable(); } mutex_unlock(&input_dev->mutex); return count; } /* fts_boot_mode interface */ static ssize_t fts_bootmode_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct input_dev *input_dev = fts_data->input_dev; FTS_FUNC_ENTER(); mutex_lock(&input_dev->mutex); if (FTS_SYSFS_ECHO_ON(buf)) { FTS_INFO("[EX-FUN]set to boot mode"); fts_data->fw_is_running = false; } else if (FTS_SYSFS_ECHO_OFF(buf)) { FTS_INFO("[EX-FUN]set to fw mode"); fts_data->fw_is_running = true; } mutex_unlock(&input_dev->mutex); FTS_FUNC_EXIT(); return count; } static ssize_t fts_bootmode_show( struct device *dev, struct device_attribute *attr, char *buf) { ssize_t count = 0; struct input_dev *input_dev = fts_data->input_dev; FTS_FUNC_ENTER(); mutex_lock(&input_dev->mutex); if (true == fts_data->fw_is_running) { count = snprintf(buf, PAGE_SIZE, "tp is in fw mode\n"); } else { count = snprintf(buf, PAGE_SIZE, "tp is in boot mode\n"); } mutex_unlock(&input_dev->mutex); FTS_FUNC_EXIT(); return count; } /* fts_tpfwver interface */ static ssize_t fts_fw_version_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; ssize_t num_read_chars = 0; u8 fwver = 0; mutex_lock(&input_dev->mutex); #if FTS_ESDCHECK_EN fts_esdcheck_proc_busy(1); #endif fts_read_reg(FTS_REG_FW_VER, &fwver); #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_fw_version_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return -EPERM; } /* fts_rw_reg */ 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; } 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; 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!"); goto exit; } if (RWREG_OP_READ == rw_op.type) { if (rw_op.len == 1) { u8 reg, val; reg = rw_op.reg & 0xFF; rw_op.res = fts_read_reg(reg, &val); rw_op.val = val; } else { char reg; reg = rw_op.reg & 0xFF; rw_op.res = fts_read(®, 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_write_reg(reg, val); } else { rw_op.res = fts_write(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; } } exit: #if FTS_ESDCHECK_EN fts_esdcheck_proc_busy(0); #endif 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 += scnprintf(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 += scnprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n", pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); count += scnprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n", ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); if (ts_data->bus_type == BUS_TYPE_I2C) count += scnprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", "I2C", ts_data->client->addr); else count += scnprintf(buf + count, PAGE_SIZE, "BUS:%s,mode:%d,max_freq:%d\n", "SPI", ts_data->spi->mode, ts_data->spi->max_speed_hz); 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_show( struct device *dev, struct device_attribute *attr, char *buf) { int count = 0; u8 val = 0; 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_read_reg(FTS_REG_POWER_MODE, &val); count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val); fts_read_reg(FTS_REG_FW_VER, &val); count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val); fts_read_reg(FTS_REG_LIC_VER, &val); count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val); fts_read_reg(FTS_REG_IDE_PARA_VER_ID, &val); count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val); fts_read_reg(FTS_REG_IDE_PARA_STATUS, &val); count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val); fts_read_reg(FTS_REG_VENDOR_ID, &val); count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val); fts_read_reg(FTS_REG_LCD_BUSY_NUM, &val); count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val); fts_read_reg(FTS_REG_GESTURE_EN, &val); count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val); fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val); fts_read_reg(FTS_REG_INT_CNT, &val); count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val); fts_read_reg(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; } static ssize_t fts_dumpreg_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return -EPERM; } /* fts_dump_reg interface */ static ssize_t fts_tpbuf_show( struct device *dev, struct device_attribute *attr, char *buf) { int count = 0; int i = 0; struct input_dev *input_dev = fts_data->input_dev; mutex_lock(&input_dev->mutex); count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n"); for (i = 0; i < fts_data->pnt_buf_size; i++) { count += snprintf(buf + count, PAGE_SIZE, "%02x ", fts_data->point_buf[i]); } count += snprintf(buf + count, PAGE_SIZE, "\n"); mutex_unlock(&input_dev->mutex); return count; } static ssize_t fts_tpbuf_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return -EPERM; } /* fts_log_level interface */ static ssize_t fts_log_level_show( struct device *dev, struct device_attribute *attr, char *buf) { int count = 0; struct input_dev *input_dev = fts_data->input_dev; mutex_lock(&input_dev->mutex); count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n", fts_data->log_level); mutex_unlock(&input_dev->mutex); return count; } static ssize_t fts_log_level_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value = 0; struct input_dev *input_dev = fts_data->input_dev; FTS_FUNC_ENTER(); mutex_lock(&input_dev->mutex); sscanf(buf, "%d", &value); FTS_DEBUG("log level:%d->%d", fts_data->log_level, value); fts_data->log_level = value; mutex_unlock(&input_dev->mutex); FTS_FUNC_EXIT(); return count; } #ifdef CONFIG_FTS_TRUSTED_TOUCH static ssize_t trusted_touch_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fts_ts_data *info = fts_data; return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&info->trusted_touch_enabled)); } static ssize_t trusted_touch_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fts_ts_data *info = fts_data; unsigned long value; int err = 0; if (count > 2) return -EINVAL; err = kstrtoul(buf, 10, &value); if (err != 0) return err; if (!atomic_read(&info->trusted_touch_initialized)) return -EIO; #ifdef CONFIG_ARCH_QTI_VM err = fts_ts_handle_trusted_touch_tvm(info, value); if (err) { pr_err("Failed to handle trusted touch in tvm\n"); return -EINVAL; } #else err = fts_ts_handle_trusted_touch_pvm(info, value); if (err) { pr_err("Failed to handle trusted touch in pvm\n"); return -EINVAL; } #endif err = count; return err; } static ssize_t trusted_touch_event_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fts_ts_data *info = fts_data; return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&info->trusted_touch_event)); } static ssize_t trusted_touch_event_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fts_ts_data *info = fts_data; unsigned long value; int err = 0; if (count > 2) return -EINVAL; err = kstrtoul(buf, 10, &value); if (err != 0) return err; if (!atomic_read(&info->trusted_touch_initialized)) return -EIO; if (value) return -EIO; atomic_set(&info->trusted_touch_event, value); return count; } static ssize_t trusted_touch_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fts_ts_data *info = fts_data; return scnprintf(buf, PAGE_SIZE, "%s", info->vm_info->trusted_touch_type); } #endif /* get the fw version example:cat fw_version */ static DEVICE_ATTR_RW(fts_fw_version); /* 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); 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_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store); static DEVICE_ATTR(fts_boot_mode, S_IRUGO | S_IWUSR, fts_bootmode_show, fts_bootmode_store); static DEVICE_ATTR(fts_touch_point, S_IRUGO | S_IWUSR, fts_tpbuf_show, fts_tpbuf_store); static DEVICE_ATTR(fts_log_level, S_IRUGO | S_IWUSR, fts_log_level_show, fts_log_level_store); #ifdef CONFIG_FTS_TRUSTED_TOUCH static DEVICE_ATTR_RW(trusted_touch_enable); static DEVICE_ATTR_RW(trusted_touch_event); static DEVICE_ATTR_RO(trusted_touch_type); #endif /* 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_driver_info.attr, &dev_attr_fts_hw_reset.attr, &dev_attr_fts_irq.attr, &dev_attr_fts_boot_mode.attr, &dev_attr_fts_touch_point.attr, &dev_attr_fts_log_level.attr, #ifdef CONFIG_FTS_TRUSTED_TOUCH &dev_attr_trusted_touch_enable.attr, &dev_attr_trusted_touch_event.attr, &dev_attr_trusted_touch_type.attr, #endif NULL }; static struct attribute_group fts_attribute_group = { .attrs = fts_attributes }; int fts_create_sysfs(struct fts_ts_data *ts_data) { int ret = 0; ret = sysfs_create_group(&ts_data->dev->kobj, &fts_attribute_group); if (ret) { FTS_ERROR("[EX]: sysfs_create_group() failed!!"); sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); return -ENOMEM; } else { FTS_INFO("[EX]: sysfs_create_group() succeeded!!"); } return ret; } int fts_remove_sysfs(struct fts_ts_data *ts_data) { sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); return 0; }