| /* |
| * |
| * FocalTech TouchScreen driver. |
| * |
| * Copyright (c) 2012-2020, 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 |
| *****************************************************************************/ |
| #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_WRITE_DATA_DIRECT 16 |
| #define PROC_READ_DATA_DIRECT 17 |
| #define PROC_CONFIGURE 18 |
| #define PROC_CONFIGURE_INTR 20 |
| #define PROC_NAME "ftxxxx-debug" |
| #define PROC_BUF_SIZE 512 |
| |
| /***************************************************************************** |
| * 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) { |
| FTS_ERROR("apk proc count(%d) fail", buflen); |
| return -EINVAL; |
| } |
| |
| if (buflen > PROC_BUF_SIZE) { |
| writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); |
| if (NULL == writebuf) { |
| FTS_ERROR("apk proc write 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 >= FTS_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; |
| 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: |
| if (buflen < PROC_BUF_SIZE) { |
| 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; |
| |
| case PROC_READ_DATA_DIRECT: |
| writelen = buflen - 1; |
| if (writelen >= FTS_MAX_COMMMAND_LENGTH) { |
| FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); |
| goto proc_write_err; |
| } |
| memcpy(proc->cmd, writebuf + 1, writelen); |
| proc->cmd_len = writelen; |
| break; |
| |
| case PROC_WRITE_DATA_DIRECT: |
| writelen = buflen - 1; |
| ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); |
| if (ret < 0) { |
| FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); |
| goto proc_write_err; |
| } |
| break; |
| |
| case PROC_CONFIGURE: |
| ts_data->spi->mode = writebuf[1]; |
| ts_data->spi->bits_per_word = writebuf[2]; |
| ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); |
| FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, |
| ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); |
| ret = spi_setup(ts_data->spi); |
| if (ret) { |
| FTS_ERROR("spi setup fail"); |
| goto proc_write_err; |
| } |
| break; |
| |
| case PROC_CONFIGURE_INTR: |
| if (writebuf[1] == 0) |
| fts_irq_disable(); |
| else |
| fts_irq_enable(); |
| 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) { |
| FTS_ERROR("apk proc read count(%d) fail", buflen); |
| return -EINVAL; |
| } |
| |
| if (buflen > PROC_BUF_SIZE) { |
| readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); |
| if (NULL == readbuf) { |
| FTS_ERROR("apk proc 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(proc->cmd, proc->cmd_len, readbuf, num_read_chars); |
| if (ret < 0) { |
| FTS_ERROR("PROC_READ_DATA read error"); |
| goto proc_read_err; |
| } |
| break; |
| |
| case PROC_READ_DATA_DIRECT: |
| num_read_chars = buflen; |
| ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); |
| if (ret < 0) { |
| FTS_ERROR("PROC_READ_DATA_DIRECT read error"); |
| goto proc_read_err; |
| } |
| break; |
| |
| case PROC_WRITE_DATA: |
| break; |
| |
| default: |
| break; |
| } |
| |
| #if FTS_ESDCHECK_EN |
| fts_esdcheck_proc_busy(0); |
| #endif |
| |
| ret = num_read_chars; |
| proc_read_err: |
| if (copy_to_user(buff, readbuf, num_read_chars)) { |
| FTS_ERROR("copy to user error"); |
| ret = -EFAULT; |
| } |
| |
| if ((buflen > PROC_BUF_SIZE) && readbuf) { |
| kfree(readbuf); |
| readbuf = NULL; |
| } |
| return ret; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops fts_proc_fops = { |
| .proc_read = fts_debug_read, |
| .proc_write = fts_debug_write, |
| }; |
| #else |
| static const struct file_operations fts_proc_fops = { |
| .owner = THIS_MODULE, |
| .read = fts_debug_read, |
| .write = fts_debug_write, |
| }; |
| #endif |
| |
| #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) { |
| FTS_ERROR("apk proc write count(%d) fail", buflen); |
| return -EINVAL; |
| } |
| |
| if (buflen > PROC_BUF_SIZE) { |
| writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); |
| if (NULL == writebuf) { |
| FTS_ERROR("apk proc write 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 >= FTS_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; |
| 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: |
| if (buflen < PROC_BUF_SIZE) { |
| 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; |
| |
| case PROC_READ_DATA_DIRECT: |
| writelen = buflen - 1; |
| if (writelen >= FTS_MAX_COMMMAND_LENGTH) { |
| FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); |
| goto proc_write_err; |
| } |
| memcpy(proc->cmd, writebuf + 1, writelen); |
| proc->cmd_len = writelen; |
| break; |
| |
| case PROC_WRITE_DATA_DIRECT: |
| writelen = buflen - 1; |
| ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); |
| if (ret < 0) { |
| FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); |
| goto proc_write_err; |
| } |
| break; |
| |
| case PROC_CONFIGURE: |
| ts_data->spi->mode = writebuf[1]; |
| ts_data->spi->bits_per_word = writebuf[2]; |
| ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); |
| FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, |
| ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); |
| ret = spi_setup(ts_data->spi); |
| if (ret) { |
| FTS_ERROR("spi setup fail"); |
| goto proc_write_err; |
| } |
| break; |
| |
| case PROC_CONFIGURE_INTR: |
| if (writebuf[1] == 0) |
| fts_irq_disable(); |
| else |
| fts_irq_enable(); |
| 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) { |
| FTS_ERROR("apk proc read count(%d) fail", buflen); |
| return -EINVAL; |
| } |
| |
| if (buflen > PROC_BUF_SIZE) { |
| readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); |
| if (NULL == readbuf) { |
| FTS_ERROR("apk proc 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(proc->cmd, proc->cmd_len, readbuf, num_read_chars); |
| if (ret < 0) { |
| FTS_ERROR("PROC_READ_DATA read error"); |
| goto proc_read_err; |
| } |
| break; |
| |
| case PROC_READ_DATA_DIRECT: |
| num_read_chars = buflen; |
| ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); |
| if (ret < 0) { |
| FTS_ERROR("PROC_READ_DATA_DIRECT read error"); |
| goto proc_read_err; |
| } |
| break; |
| |
| case PROC_WRITE_DATA: |
| break; |
| |
| default: |
| break; |
| } |
| |
| #if FTS_ESDCHECK_EN |
| fts_esdcheck_proc_busy(0); |
| #endif |
| |
| ret = num_read_chars; |
| proc_read_err: |
| if (copy_to_user(buff, readbuf, num_read_chars)) { |
| FTS_ERROR("copy to user error"); |
| ret = -EFAULT; |
| } |
| |
| 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; |
| |
| count = snprintf(buf, PAGE_SIZE, "irq_enable:%d\n", |
| !fts_data->irq_disabled); |
| |
| 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_tpfwver_show( |
| struct device *dev, struct device_attribute *attr, char *buf) |
| { |
| int ret = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| struct input_dev *input_dev = ts_data->input_dev; |
| ssize_t num_read_chars = 0; |
| u8 fw_major_ver = 0; |
| u8 fw_minor_ver = 0; |
| |
| mutex_lock(&input_dev->mutex); |
| |
| #if FTS_ESDCHECK_EN |
| fts_esdcheck_proc_busy(1); |
| #endif |
| ret = fts_read_reg(FTS_REG_FW_MAJOR_VER, &fw_major_ver); |
| if ((ret < 0) || (fw_major_ver == 0xFF) || (fw_major_ver == 0x00)) { |
| num_read_chars = snprintf(buf, PAGE_SIZE, |
| "get tp fw major version fail!\n"); |
| mutex_unlock(&input_dev->mutex); |
| return num_read_chars; |
| } |
| ret = fts_read_reg(FTS_REG_FW_MINOR_VER, &fw_minor_ver); |
| if (ret < 0) { |
| num_read_chars = snprintf(buf, PAGE_SIZE, |
| "get tp fw minor version fail!\n"); |
| mutex_unlock(&input_dev->mutex); |
| return num_read_chars; |
| } |
| #if FTS_ESDCHECK_EN |
| fts_esdcheck_proc_busy(0); |
| #endif |
| num_read_chars = snprintf(buf, PAGE_SIZE, "V%02x_D%02x\n", fw_major_ver, |
| fw_minor_ver); |
| |
| 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; |
| } |
| |
| /* 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!"); |
| |
| } 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_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; |
| } |
| } |
| } |
| |
| #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] = { 0 }; |
| struct input_dev *input_dev = fts_data->input_dev; |
| |
| 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, FILE_NAME_LENGTH, "%s", buf); |
| fwname[count - 1] = '\0'; |
| |
| FTS_INFO("upgrade with bin file through sysfs node"); |
| mutex_lock(&input_dev->mutex); |
| fts_upgrade_bin(fwname, 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 input_dev *input_dev = fts_data->input_dev; |
| |
| 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, FILE_NAME_LENGTH, "%s", buf); |
| fwname[count - 1] = '\0'; |
| |
| FTS_INFO("force upgrade through sysfs node"); |
| mutex_lock(&input_dev->mutex); |
| fts_upgrade_bin(fwname, 1); |
| 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); |
| |
| if (ts_data->bus_type == FTS_BUS_TYPE_I2C) { |
| count += snprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", |
| "I2C", ts_data->client->addr); |
| } else { |
| count += snprintf(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_MAJOR_VER, &val); |
| count += snprintf(buf + count, PAGE_SIZE, "FW Major Ver:0x%02x\n", val); |
| |
| fts_read_reg(FTS_REG_FW_MINOR_VER, &val); |
| count += snprintf(buf + count, PAGE_SIZE, "FW Minor 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_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; |
| } |
| |
| /* 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_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); |
| |
| /* 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_irq.attr, |
| &dev_attr_fts_boot_mode.attr, |
| &dev_attr_fts_touch_point.attr, |
| &dev_attr_fts_log_level.attr, |
| NULL |
| }; |
| |
| static struct attribute_group fts_attribute_group = { |
| .attrs = fts_attributes |
| }; |
| |
| static ssize_t proc_fw_update_write(struct file *filp, const char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| struct fts_ts_data *ts_data = pde_data(file_inode(filp)); |
| char fwname[FILE_NAME_LENGTH] = { 0 }; |
| int buflen = count; |
| |
| FTS_INFO("upgrade with bin file through proc node"); |
| if (!ts_data) { |
| FTS_ERROR("ts_data is null"); |
| return -EINVAL; |
| } |
| |
| if ((buflen <= 0) || (buflen >= FILE_NAME_LENGTH)) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(fwname, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| fwname[buflen - 1] = '\0'; |
| |
| mutex_lock(&ts_data->input_dev->mutex); |
| fts_upgrade_bin(fwname, 0); |
| mutex_unlock(&ts_data->input_dev->mutex); |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_fw_update_fops = { |
| .proc_write = proc_fw_update_write, |
| }; |
| #else |
| static const struct file_operations proc_fw_update_fops = { |
| .owner = THIS_MODULE, |
| .write = proc_fw_update_write, |
| }; |
| #endif |
| |
| /* scan modes */ |
| static ssize_t proc_scan_modes_read(struct file *filp, char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int cnt = 0; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "%s\n", |
| "scan_modes=0,1,2,3,4"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "%s\n", |
| "0:Auto mode"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "%s\n", |
| "1:Normal Active"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "%s\n", |
| "2:Normal Idle"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "%s\n", |
| "3:Low Power Active"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "%s\n", |
| "4:Low Power Idle"); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_scan_modes_fops = { |
| .proc_read = proc_scan_modes_read, |
| }; |
| #else |
| static const struct file_operations proc_scan_modes_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_scan_modes_read, |
| }; |
| #endif |
| |
| /* touch mode */ |
| static ssize_t proc_touch_mode_read(struct file *filp, char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| u8 gesture_mode = 0; |
| u8 power_mode = 0; |
| u8 monitor_ctrl = 0; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| ret = fts_read_reg(FTS_REG_GESTURE_EN, &gesture_mode); |
| if (ret < 0) { |
| FTS_ERROR("read reg0xD0 fails"); |
| return ret; |
| } |
| |
| ret = fts_read_reg(FTS_REG_POWER_MODE, &power_mode); |
| if (ret < 0) { |
| FTS_ERROR("read reg0xA5 fails"); |
| return ret; |
| } |
| |
| ret = fts_read_reg(FTS_REG_MONITOR_CTRL, &monitor_ctrl); |
| if (ret < 0) { |
| FTS_ERROR("read reg0x86 fails"); |
| return ret; |
| } |
| |
| if (gesture_mode) { |
| if (power_mode == 0) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "touch_mode:%d-%s\n", MODE_LOW_POWER_ACTIVE, "Low Power Active"); |
| else if (power_mode == 1) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "touch_mode:%d-%s\n", MODE_LOW_POWER_IDLE, "Low Power Idle"); |
| } else if (monitor_ctrl) { |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "touch_mode:%d-%s\n", MODE_AUTO, "Auto mode"); |
| } else { |
| if (power_mode == 0) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "touch_mode:%d-%s\n", MODE_NORMAL_ACTIVE, "Normal Active"); |
| else if (power_mode == 1) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "touch_mode:%d-%s\n", MODE_NORMAL_IDLE, "Normal Idle"); |
| } |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| static ssize_t proc_touch_mode_write(struct file *filp, const char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int touch_mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &touch_mode); |
| if ((ret != 1) || (touch_mode < 0) || (touch_mode >= MODE_CNT)) { |
| FTS_ERROR("get mode(%d) fails,ret=%d", touch_mode, ret); |
| return -EINVAL; |
| } |
| |
| FTS_INFO("switch touch_mode to %d", touch_mode); |
| switch (touch_mode) { |
| case MODE_AUTO: |
| ret = fts_write_reg(FTS_REG_MONITOR_CTRL, 1); |
| if (ret < 0) { |
| FTS_ERROR("write reg0x86 fails"); |
| return ret; |
| } |
| |
| ret = fts_write_reg(FTS_REG_GESTURE_EN, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xd0 fails"); |
| return ret; |
| } |
| break; |
| case MODE_NORMAL_ACTIVE: |
| ret = fts_write_reg(FTS_REG_MONITOR_CTRL, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg0x86 fails"); |
| return ret; |
| } |
| |
| ret = fts_write_reg(FTS_REG_POWER_MODE, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xA5 fails"); |
| return ret; |
| } |
| |
| ret = fts_write_reg(FTS_REG_GESTURE_EN, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xD0 fails"); |
| return ret; |
| } |
| break; |
| case MODE_NORMAL_IDLE: |
| ret = fts_write_reg(FTS_REG_MONITOR_CTRL, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg0x86 fails"); |
| return ret; |
| } |
| |
| ret = fts_write_reg(FTS_REG_POWER_MODE, 1); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xA5 fails"); |
| return ret; |
| } |
| |
| ret = fts_write_reg(FTS_REG_GESTURE_EN, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xD0 fails"); |
| return ret; |
| } |
| break; |
| case MODE_LOW_POWER_ACTIVE: |
| ret = fts_write_reg(FTS_REG_GESTURE_EN, 1); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xD0 fails"); |
| return ret; |
| } |
| |
| ret = fts_write_reg(FTS_REG_POWER_MODE, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xA5 fails"); |
| return ret; |
| } |
| break; |
| case MODE_LOW_POWER_IDLE: |
| ret = fts_write_reg(FTS_REG_GESTURE_EN, 1); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xD0 fails"); |
| return ret; |
| } |
| |
| ret = fts_write_reg(FTS_REG_POWER_MODE, 1); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xA5 fails"); |
| return ret; |
| } |
| break; |
| default: |
| FTS_ERROR("Input index of mode is out of range!"); |
| break; |
| } |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_touch_mode_fops = { |
| .proc_read = proc_touch_mode_read, |
| .proc_write = proc_touch_mode_write, |
| }; |
| #else |
| static const struct file_operations proc_touch_mode_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_touch_mode_read, |
| .write = proc_touch_mode_write, |
| }; |
| #endif |
| |
| /* lpwg */ |
| static ssize_t proc_lpwg_read(struct file *filp, char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| u8 gesture_mode = 0; |
| u8 gesture_function = 0; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| ret = fts_read_reg(FTS_REG_GESTURE_EN, &gesture_mode); |
| if (ret < 0) { |
| FTS_ERROR("read reg0xD0 fails"); |
| return ret; |
| } |
| |
| ret = fts_read_reg(FTS_REG_GESTURE_SWITCH, &gesture_function); |
| if (ret < 0) { |
| FTS_ERROR("read reg0xCF fails"); |
| return ret; |
| } |
| |
| if (gesture_function == 1) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "Gesture_mode: STTW\n"); |
| else if (gesture_function == 2) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "Gesture_mode: LPTW\n"); |
| else if (gesture_function == 3) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "Gesture_mode: STTW + LPTW\n"); |
| else if (gesture_function == 0) |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "Disable STTW and LPTW\n"); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| static ssize_t proc_lpwg_write(struct file *filp, const char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int gesture_mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &gesture_mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| |
| FTS_INFO("switch gesture mode to %d", gesture_mode); |
| |
| switch (gesture_mode) { |
| case 0: |
| ret = fts_write_reg(FTS_REG_GESTURE_SWITCH, 0); |
| if (ret < 0) { |
| FTS_ERROR("write reg 0xCF fails"); |
| return ret; |
| } |
| break; |
| |
| case 1: //Single tap |
| ret = fts_write_reg(FTS_REG_GESTURE_SWITCH, 1); |
| if (ret < 0) { |
| FTS_ERROR("write reg 0xCF fails"); |
| return ret; |
| } |
| FTS_INFO("switch gesture function to STTW"); |
| break; |
| |
| case 2: //Long press |
| ret = fts_write_reg(FTS_REG_GESTURE_SWITCH, 2); |
| if (ret < 0) { |
| FTS_ERROR("write reg 0xCF fails"); |
| return ret; |
| } |
| FTS_INFO("switch gesture function to LPTW"); |
| break; |
| |
| case 3: //Single tap + Long press |
| ret = fts_write_reg(FTS_REG_GESTURE_SWITCH, 3); |
| if (ret < 0) { |
| FTS_ERROR("write reg 0xCF fails"); |
| return ret; |
| } |
| FTS_INFO("switch gesture function to STTW + LPTW"); |
| break; |
| |
| default: |
| break; |
| } |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_lpwg_fops = { |
| .proc_read = proc_lpwg_read, |
| .proc_write = proc_lpwg_write, |
| }; |
| #else |
| static const struct file_operations proc_lpwg_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_lpwg_read, |
| .write = proc_lpwg_write, |
| }; |
| #endif |
| |
| /* high sensitivity */ |
| static ssize_t proc_hs_read(struct file *filp, char __user *buff, size_t count, |
| loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| u8 hs_mode = 0; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| ret = fts_read_reg(FTS_REG_GLOVE_MODE_EN, &hs_mode); |
| if (ret < 0) { |
| FTS_ERROR("read reg0xC0 fails"); |
| return ret; |
| } |
| |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "high_sensitivity mode:%s\n", hs_mode ? "Enable" : "Disable"); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| static ssize_t proc_hs_write(struct file *filp, const char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int hs_mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &hs_mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| |
| ret = fts_set_glove_mode(ts_data, !!hs_mode); |
| if (ret < 0) |
| return ret; |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_hs_fops = { |
| .proc_read = proc_hs_read, |
| .proc_write = proc_hs_write, |
| }; |
| #else |
| static const struct file_operations proc_hs_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_hs_read, |
| .write = proc_hs_write, |
| }; |
| #endif |
| |
| /* palm */ |
| static ssize_t proc_palm_read(struct file *filp, char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| FTS_DEBUG("fw_palm = %d", ts_data->enable_fw_palm); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "%u\n", ts_data->enable_fw_palm); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| /* Set palm rejection mode. |
| * 0 - Disable fw palm rejection. |
| * 1 - Enable fw palm rejection. |
| * 2 - Force disable fw palm rejection. |
| * 3 - Force enable fw palm rejection. |
| */ |
| static ssize_t proc_palm_write(struct file *filp, const char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int palm_mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &palm_mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| |
| if (palm_mode < 0 || palm_mode > 3) { |
| FTS_ERROR("get palm mode fails, fw_palm should be in [0,1,2,3]."); |
| return -EINVAL; |
| } |
| |
| ts_data->enable_fw_palm = palm_mode; |
| FTS_INFO("switch fw_aplm to %u\n", ts_data->enable_fw_palm); |
| |
| ret = fts_set_palm_mode(ts_data, palm_mode); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_palm_fops = { |
| .proc_read = proc_palm_read, |
| .proc_write = proc_palm_write, |
| }; |
| #else |
| static const struct file_operations proc_palm_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_palm_read, |
| .write = proc_palm_write, |
| }; |
| #endif |
| |
| /* grip */ |
| static ssize_t proc_grip_read(struct file *filp, char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| FTS_DEBUG("fw_grip = %u", ts_data->enable_fw_grip); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "%u\n", ts_data->enable_fw_grip); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| /* Set Grip suppression mode. |
| * 0 - Disable fw grip suppression. |
| * 1 - Enable fw grip suppression. |
| * 2 - Force disable fw grip suppression. |
| * 3 - Force enable fw grip suppression. |
| */ |
| static ssize_t proc_grip_write(struct file *filp, const char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int grip_mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &grip_mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| if (grip_mode < 0 || grip_mode > 3) { |
| FTS_ERROR("get mode fails, grip_mode should be in [0,1,2,3]."); |
| return -EINVAL; |
| } |
| |
| ts_data->enable_fw_grip = grip_mode; |
| FTS_INFO("switch fw_grip to %u\n", ts_data->enable_fw_grip); |
| |
| ret = fts_set_grip_mode(ts_data, grip_mode); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_grip_fops = { |
| .proc_read = proc_grip_read, |
| .proc_write = proc_grip_write, |
| }; |
| #else |
| static const struct file_operations proc_grip_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_grip_read, |
| .write = proc_grip_write, |
| }; |
| #endif |
| |
| /* sense on and off */ |
| static ssize_t proc_sense_onoff_read(struct file *filp, char __user *buff, |
| size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| u8 mode = 0; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| ret = fts_read_reg(FTS_REG_SENSE_ONOFF, &mode); |
| if (ret < 0) { |
| FTS_ERROR("read reg0xEA fails"); |
| return ret; |
| } |
| |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "Sensing mode:%s\n", |
| mode ? "Enable" : "Disable"); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| static ssize_t proc_sense_onoff_write(struct file *filp, |
| const char __user *buff, size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| |
| FTS_INFO("switch touch sense on/off to %d", mode); |
| ret = fts_write_reg(FTS_REG_SENSE_ONOFF, !!mode); |
| if (ret < 0) { |
| FTS_ERROR("write reg0xEA fails"); |
| return ret; |
| } |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_sense_onoff_fops = { |
| .proc_read = proc_sense_onoff_read, |
| .proc_write = proc_sense_onoff_write, |
| }; |
| #else |
| static const struct file_operations proc_sense_onoff_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_sense_onoff_read, |
| .write = proc_sense_onoff_write, |
| }; |
| #endif |
| |
| /* IRQ on and off */ |
| static ssize_t proc_irq_onoff_read(struct file *filp, |
| char __user *buff, size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| u8 mode = 0; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| ret = fts_read_reg(FTS_REG_IRQ_ONOFF, &mode); |
| if (ret < 0) { |
| FTS_ERROR("read reg_0xEB fails"); |
| return ret; |
| } |
| |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "touch IRQ:%s\n", |
| mode ? "Enable" : "Disable"); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| static ssize_t proc_irq_onoff_write(struct file *filp, |
| const char __user *buff, size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| |
| FTS_INFO("switch touch IRQ on/off to %d", mode); |
| ret = fts_write_reg(FTS_REG_IRQ_ONOFF, !!mode); |
| if (ret < 0) { |
| FTS_ERROR("write reg_0xEB fails"); |
| return ret; |
| } |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_irq_onoff_fops = { |
| .proc_read = proc_irq_onoff_read, |
| .proc_write = proc_irq_onoff_write, |
| }; |
| #else |
| static const struct file_operations proc_irq_onoff_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_irq_onoff_read, |
| .write = proc_irq_onoff_write, |
| }; |
| #endif |
| |
| /* heatmap on and off */ |
| static ssize_t proc_heatmap_onoff_read(struct file *filp, |
| char __user *buff, size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| u8 mode = 0; |
| u8 compressed = 0; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| ret = fts_read_reg(FTS_REG_HEATMAP_9E, &mode); |
| if (ret < 0) { |
| FTS_ERROR("read reg_0x%X fails", FTS_REG_HEATMAP_9E); |
| return ret; |
| } |
| |
| if (mode) { |
| fts_read_reg(FTS_REG_HEATMAP_ED, &compressed); |
| if (ret < 0) { |
| FTS_ERROR("read reg_0x%X fails", FTS_REG_HEATMAP_ED); |
| return ret; |
| } |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "%s ", |
| compressed ? "Compressed" : "Uncompressed"); |
| } |
| |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "heatmap is %s\n", |
| mode ? "Enable" : "Disable"); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| static ssize_t proc_heatmap_onoff_write(struct file *filp, |
| const char __user *buff, size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| |
| if (mode < FW_HEATMAP_MODE_DISABLE || mode > FW_HEATMAP_MODE_UNCOMPRESSED) { |
| FTS_ERROR("Please input the parameters in \n \ |
| 0: Disable firmware heatmap. \n \ |
| 1: Enable firmware compressed heatmap. \n \ |
| 2: Enable firmware uncompressed heatmap."); |
| return -EINVAL; |
| } |
| FTS_INFO("switch heatmap on/off to %d", mode); |
| fts_set_heatmap_mode(ts_data, mode); |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_heatmap_onoff_fops = { |
| .proc_read = proc_heatmap_onoff_read, |
| .proc_write = proc_heatmap_onoff_write, |
| }; |
| #else |
| static const struct file_operations proc_heatmap_onoff_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_heatmap_onoff_read, |
| .write = proc_heatmap_onoff_write, |
| }; |
| #endif |
| |
| static ssize_t proc_LPTW_setting_write( |
| struct file *filp, const char __user *buff, size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = {0}; |
| |
| int buflen = count; |
| int lptw_write_data[FTS_LPTW_BUF_LEN] = {0}; |
| u8 write_data[FTS_LPTW_BUF_LEN] = {0}; |
| |
| u8 cmd[2] = {0}; |
| u32 data_length = 0; |
| int i; |
| |
| cmd[0] = FTS_LPTW_REG_SET_E1; |
| cmd[1] = FTS_LPTW_REG_SET_E2; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%x%x%x%x%x%x%x%x%x%x%x%x%x", &lptw_write_data[0], |
| &lptw_write_data[1], &lptw_write_data[2], &lptw_write_data[3], |
| &lptw_write_data[4], &lptw_write_data[5], &lptw_write_data[6], |
| &lptw_write_data[7], &lptw_write_data[8], &lptw_write_data[9], |
| &lptw_write_data[10], &lptw_write_data[11], &lptw_write_data[12]); |
| |
| if(lptw_write_data[0] == FTS_LPTW_REG_SET_E1) |
| data_length = FTS_LPTW_E1_BUF_LEN; |
| else if (lptw_write_data[0] == FTS_LPTW_REG_SET_E2) |
| data_length = FTS_LPTW_E2_BUF_LEN; |
| else |
| data_length = 0; |
| |
| for (i = 0; i < data_length; i++) |
| write_data[i] = (char)lptw_write_data[i]; |
| |
| if (data_length != 0){ |
| ret=fts_write(write_data, data_length); |
| if (ret < 0) { |
| FTS_ERROR("write data to register E1/E2 fail"); |
| return ret; |
| } |
| } |
| |
| return count; |
| } |
| |
| /*LPTW setting read*/ |
| static ssize_t proc_LPTW_setting_read( |
| struct file *filp, char __user *buff, size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = {0}; |
| u8 cmd[2] = {0}; |
| int num_read_chars = 0; |
| loff_t pos = *ppos; |
| int buflen = count; |
| u8 *readbuf = NULL; |
| u8 read_tmpbuf[20] = {0}; |
| |
| if (pos) |
| return 0; |
| |
| if (buflen <= 0) { |
| FTS_ERROR("apk proc read count(%d) fail", buflen); |
| return -EINVAL; |
| } |
| |
| if (buflen > PROC_BUF_SIZE) { |
| readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); |
| if (NULL == readbuf) { |
| FTS_ERROR("apk proc buf zalloc fail"); |
| return -ENOMEM; |
| } |
| } else { |
| readbuf = read_tmpbuf; |
| } |
| |
| cmd[0] = FTS_LPTW_REG_SET_E1; |
| cmd[1] = FTS_LPTW_REG_SET_E2; |
| ret = fts_read(&cmd[0], 1, readbuf, FTS_LPTW_E1_BUF_LEN - 1); |
| if (ret < 0) { |
| FTS_ERROR("read reg_0xE1 fails"); |
| goto proc_read_err; |
| } |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "==LPTW Gesture setting(E1)==\n"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "min_x :%4d\n", |
| ((readbuf[0] & 0x0F) << 8) + (readbuf[1] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "min_y :%4d\n", |
| ((readbuf[2] & 0x0F) << 8) + (readbuf[3] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "max_x :%4d\n", |
| ((readbuf[4] & 0x0F) << 8) + (readbuf[5] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, "max_y :%4d\n", |
| ((readbuf[6] & 0x0F) << 8) + (readbuf[7] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "min_frame_count :%3d\n",(readbuf[8] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "jitter :%3d\n" ,(readbuf[9] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "max_touch_size :%3d\n\n", (readbuf[10] & 0xFF)); |
| |
| ret = fts_read(&cmd[1], 1, readbuf, FTS_LPTW_E2_BUF_LEN - 1); |
| if (ret < 0) { |
| FTS_ERROR("read reg_0xE2 fails"); |
| goto proc_read_err; |
| } |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "==LPTW Gesture setting(E2)==\n"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "marginal_min_x :%2d\n", (readbuf[0] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "marginal_max_x :%2d\n", (readbuf[1] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "marginal_min_y :%2d\n", (readbuf[2] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "marginal_max_y :%2d\n", (readbuf[3] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "monitor_channel_min_tx :%2d\n", (readbuf[4] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "monitor_channel_max_tx :%2d\n", (readbuf[5] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "monitor_channel_min_rx :%2d\n", (readbuf[6] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "monitor_channel_max_rx :%2d\n", (readbuf[7] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "min_node_count :%2d\n", (readbuf[8] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "motion_boundary :%4d\n\n",((readbuf[10] & 0x0F) << 8) + |
| (readbuf[11] & 0xFF)); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| |
| proc_read_err: |
| if (copy_to_user(buff, readbuf, num_read_chars)) { |
| FTS_ERROR("copy to user error"); |
| ret = -EFAULT; |
| } |
| |
| if ((buflen > PROC_BUF_SIZE) && readbuf) { |
| kfree(readbuf); |
| readbuf = NULL; |
| } |
| return ret; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops LPTW_setting_fops = { |
| .proc_read = proc_LPTW_setting_read, |
| .proc_write = proc_LPTW_setting_write, |
| }; |
| #else |
| static const struct file_operations LPTW_setting_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_LPTW_setting_read, |
| .write = proc_LPTW_setting_write, |
| }; |
| #endif |
| |
| static ssize_t proc_STTW_setting_write( |
| struct file *filp, const char __user *buff, size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = {0}; |
| int buflen = count; |
| int sttw_write_data[FTS_STTW_E3_BUF_LEN] = {0}; |
| u8 write_data[FTS_STTW_E3_BUF_LEN] = {0}; |
| |
| u8 cmd[2] = {0}; |
| u8 data_length = 0; |
| int i = 0; |
| cmd[0] = FTS_STTW_REG_SET_E3; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%x%x%x%x%x%x%x%x%x%x%x%x%x", &sttw_write_data[0], |
| &sttw_write_data[1], &sttw_write_data[2], &sttw_write_data[3], |
| &sttw_write_data[4], &sttw_write_data[5], &sttw_write_data[6], |
| &sttw_write_data[7], &sttw_write_data[8], &sttw_write_data[9], |
| &sttw_write_data[10], &sttw_write_data[11], &sttw_write_data[12]); |
| |
| if (sttw_write_data[0] == FTS_STTW_REG_SET_E3) { |
| data_length = FTS_STTW_E3_BUF_LEN; |
| for (i = 0; i < data_length; i++) |
| write_data[i] = (char)sttw_write_data[i]; |
| |
| ret = fts_write(write_data,data_length); |
| if (ret < 0) { |
| FTS_ERROR("write data to register E3 fail"); |
| return ret; |
| } |
| } |
| |
| return count; |
| } |
| |
| /*STTW setting read*/ |
| static ssize_t proc_STTW_setting_read( |
| struct file *filp, char __user *buff, size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| int ret = 0; |
| char tmpbuf[PROC_BUF_SIZE] = {0}; |
| u8 cmd[2] = {0}; |
| int num_read_chars = 0; |
| loff_t pos = *ppos; |
| int buflen = count; |
| u8 *readbuf = NULL; |
| u8 read_tmpbuf[20] = {0}; |
| |
| if (pos) |
| return 0; |
| |
| if (buflen <= 0) { |
| FTS_ERROR("apk proc read count(%d) fail", buflen); |
| return -EINVAL; |
| } |
| |
| if (buflen > PROC_BUF_SIZE) { |
| readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); |
| if (NULL == readbuf) { |
| FTS_ERROR("apk proc buf zalloc fail"); |
| return -ENOMEM; |
| } |
| } else { |
| readbuf = read_tmpbuf; |
| } |
| |
| cmd[0] = FTS_STTW_REG_SET_E3; |
| |
| ret = fts_read(&cmd[0], 1, readbuf, FTS_STTW_E3_BUF_LEN - 1); |
| if (ret < 0) { |
| FTS_ERROR("read reg_0xE3 fails"); |
| goto proc_read_err; |
| } |
| |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "==STTW Gesture setting(E3)==\n"); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "min_x :%4d\n",((readbuf[0] & 0x0F) << 8) +(readbuf[1] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "min_y :%4d\n",((readbuf[2] & 0x0F) << 8) +(readbuf[3] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "max_x :%4d\n",((readbuf[4] & 0x0F) << 8) +(readbuf[5] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "max_y :%4d\n",((readbuf[6] & 0x0F) << 8) +(readbuf[7] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "min_frame_count :%3d\n", (readbuf[8] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "max_frame_count :%3d\n", (readbuf[9] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "jitter :%3d\n",(readbuf[10] & 0xFF)); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "tap_max_touch_size :%3d\n", (readbuf[11] & 0xFF)); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| |
| proc_read_err: |
| if (copy_to_user(buff, readbuf, num_read_chars)) { |
| FTS_ERROR("copy to user error"); |
| ret = -EFAULT; |
| } |
| |
| if ((buflen > PROC_BUF_SIZE) && readbuf) { |
| kfree(readbuf); |
| readbuf = NULL; |
| } |
| return ret; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops STTW_setting_fops = { |
| .proc_read = proc_STTW_setting_read, |
| .proc_write = proc_STTW_setting_write, |
| }; |
| #else |
| static const struct file_operations STTW_setting_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_STTW_setting_read, |
| .write = proc_STTW_setting_write, |
| }; |
| #endif |
| |
| /* motion filter mode */ |
| static ssize_t proc_mf_mode_read(struct file *filp, |
| char __user *buff, size_t count, loff_t *ppos) |
| { |
| int cnt = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| loff_t pos = *ppos; |
| |
| if (pos) |
| return 0; |
| |
| FTS_DEBUG("mf_mode = %u", ts_data->mf_mode); |
| cnt += snprintf(tmpbuf + cnt, PROC_BUF_SIZE - cnt, |
| "%u\n", ts_data->mf_mode); |
| |
| if (copy_to_user(buff, tmpbuf, cnt)) { |
| FTS_ERROR("copy to user error"); |
| return -EFAULT; |
| } |
| |
| *ppos = pos + cnt; |
| return cnt; |
| } |
| |
| /** |
| * Attribute to set motion filter mode. |
| * 0 = Always unfilter. |
| * 1 = Dynamic change motion filter. |
| * 2 = Always filter by touch FW. |
| */ |
| static ssize_t proc_mf_mode_write(struct file *filp, |
| const char __user *buff, size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| int mf_mode = 0xFF; |
| int buflen = count; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| return -EINVAL; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| return -EFAULT; |
| } |
| |
| ret = sscanf(tmpbuf, "%d", &mf_mode); |
| if (ret != 1) { |
| FTS_ERROR("get mode fails,ret=%d", ret); |
| return -EINVAL; |
| } |
| if (mf_mode < 0 || mf_mode > 2) { |
| FTS_ERROR("get mode fails, mf_mode should be in [0,1,2]."); |
| return -EINVAL; |
| } |
| |
| ts_data->mf_mode = mf_mode; |
| FTS_INFO("switch fw_mode to %u\n", ts_data->mf_mode); |
| |
| return count; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_mf_mode_fops = { |
| .proc_read = proc_mf_mode_read, |
| .proc_write = proc_mf_mode_write, |
| }; |
| #else |
| static const struct file_operations proc_mf_mode_fops = { |
| .owner = THIS_MODULE, |
| .read = proc_mf_mode_read, |
| .write = proc_mf_mode_write, |
| }; |
| #endif |
| |
| /** |
| * proc_force_active_write() |
| * |
| * Attribute to set different scan mode. |
| * 0x10 - Set FTS_TS_BUS_REF_FORCE_ACTIVE bit 0. |
| * 0x11 - Set FTS_TS_BUS_REF_FORCE_ACTIVE bit 1. |
| * 0x20 - Set FTS_TS_BUS_REF_BUGREPORT bit 0. |
| * 0x21 - Set FTS_TS_BUS_REF_BUGREPORT bit 1. |
| * |
| * @return |
| * on success, return count; otherwise, return error code |
| */ |
| static ssize_t proc_force_active_write(struct file *filp, |
| const char __user *buff, size_t count, loff_t *ppos) |
| { |
| int ret = 0; |
| struct fts_ts_data *ts_data = fts_data; |
| char tmpbuf[PROC_BUF_SIZE] = { 0 }; |
| unsigned char input; |
| int buflen = count; |
| bool active; |
| u32 ref = 0; |
| |
| if (buflen >= PROC_BUF_SIZE) { |
| FTS_ERROR("proc write length(%d) fails", buflen); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (copy_from_user(tmpbuf, buff, buflen)) { |
| FTS_ERROR("copy from user error"); |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| ret = kstrtou8(tmpbuf, 16, &input); |
| if (ret != 0) { |
| FTS_ERROR("get mode fails, ret=%d", ret); |
| ret = -EINVAL; |
| goto exit; |
| } |
| switch (input) { |
| case 0x10: |
| ref = FTS_TS_BUS_REF_FORCE_ACTIVE; |
| active = false; |
| break; |
| case 0x11: |
| ref = FTS_TS_BUS_REF_FORCE_ACTIVE; |
| active = true; |
| break; |
| case 0x20: |
| ref = FTS_TS_BUS_REF_BUGREPORT; |
| active = false; |
| ts_data->bugreport_ktime_start = 0; |
| break; |
| case 0x21: |
| ref = FTS_TS_BUS_REF_BUGREPORT; |
| active = true; |
| ts_data->bugreport_ktime_start = ktime_get(); |
| break; |
| default: |
| FTS_ERROR("Invalid input %#x.\n", input); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| FTS_INFO("Set bus reference bit %#x %s.", ref, |
| active ? "enable" : "disable"); |
| |
| if (active) |
| pm_stay_awake(ts_data->dev); |
| else |
| pm_relax(ts_data->dev); |
| |
| ret = fts_ts_set_bus_ref(ts_data, ref, active); |
| if (ret < 0) { |
| FTS_ERROR("Set bus reference bit %#x %s failed.", ref, |
| active ? "enable" : "disable"); |
| goto exit; |
| } |
| |
| ret = count; |
| |
| exit: |
| return ret; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) |
| static const struct proc_ops proc_force_active_fops = { |
| .proc_write = proc_force_active_write, |
| }; |
| #else |
| static const struct file_operations proc_force_active_fops = { |
| .owner = THIS_MODULE, |
| .write = proc_force_active_write, |
| }; |
| #endif |
| |
| |
| struct proc_dir_entry *proc_fw_update; |
| struct proc_dir_entry *proc_scan_modes; |
| struct proc_dir_entry *proc_touch_mode; |
| struct proc_dir_entry *proc_lpwg; |
| struct proc_dir_entry *proc_high_sensitivity; |
| struct proc_dir_entry *proc_palm; |
| struct proc_dir_entry *proc_grip; |
| struct proc_dir_entry *proc_sense_onoff; |
| struct proc_dir_entry *proc_irq_onoff; |
| struct proc_dir_entry *proc_heatmap_onoff; |
| struct proc_dir_entry *proc_LPTW_setting; |
| struct proc_dir_entry *proc_STTW_setting; |
| struct proc_dir_entry *proc_mf_mode; |
| struct proc_dir_entry *proc_force_active; |
| |
| static int fts_create_ctrl_procs(struct fts_ts_data *ts_data) |
| { |
| int ret = 0; |
| |
| proc_fw_update = proc_create_data("fw_update", S_IWUSR, |
| ts_data->proc_touch_entry, &proc_fw_update_fops, ts_data); |
| if (!proc_fw_update) { |
| FTS_ERROR("create proc_fw_update entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_scan_modes = proc_create_data("scan_modes", S_IRUSR, |
| ts_data->proc_touch_entry, &proc_scan_modes_fops, ts_data); |
| if (!proc_scan_modes) { |
| FTS_ERROR("create proc_scan_modes entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_touch_mode = proc_create_data("touch_mode", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_touch_mode_fops, ts_data); |
| if (!proc_touch_mode) { |
| FTS_ERROR("create proc_touch_mode entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_lpwg = proc_create_data("lpwg", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_lpwg_fops, ts_data); |
| if (!proc_lpwg) { |
| FTS_ERROR("create proc_lpwg entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_high_sensitivity = proc_create_data("high_sensitivity", |
| S_IRUSR|S_IWUSR, ts_data->proc_touch_entry, &proc_hs_fops, ts_data); |
| if (!proc_lpwg) { |
| FTS_ERROR("create proc_high_sensitivity entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_palm = proc_create_data("fw_palm", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_palm_fops, ts_data); |
| if (!proc_palm) { |
| FTS_ERROR("create proc_palm entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_grip = proc_create_data("fw_grip", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_grip_fops, ts_data); |
| if (!proc_grip) { |
| FTS_ERROR("create proc_grip entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_sense_onoff = proc_create_data("sense_onoff", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_sense_onoff_fops, ts_data); |
| if (!proc_sense_onoff) { |
| FTS_ERROR("create proc_sense_onoff entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_irq_onoff = proc_create_data("irq_onoff", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_irq_onoff_fops, ts_data); |
| if (!proc_irq_onoff) { |
| FTS_ERROR("create proc_irq_onoff entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_heatmap_onoff = proc_create_data("heatmap_onoff", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_heatmap_onoff_fops, ts_data); |
| if (!proc_heatmap_onoff) { |
| FTS_ERROR("create proc_heatmap_onoff entry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_LPTW_setting = proc_create_data("LPTW_setting", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &LPTW_setting_fops, ts_data); |
| if (!proc_LPTW_setting) { |
| FTS_ERROR("create proc_LPTW_settingentry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_STTW_setting = proc_create_data("STTW_setting", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &STTW_setting_fops, ts_data); |
| if (!proc_STTW_setting) { |
| FTS_ERROR("create proc_STTW_settingentry fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_mf_mode = proc_create_data("mf_mode", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_mf_mode_fops, ts_data); |
| if (!proc_mf_mode) { |
| FTS_ERROR("create proc_mf_mode fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| proc_force_active = proc_create_data("force_active", S_IRUSR|S_IWUSR, |
| ts_data->proc_touch_entry, &proc_force_active_fops, ts_data); |
| if (!proc_force_active) { |
| FTS_ERROR("create proc_force_active fail"); |
| ret = -ENOMEM; |
| return ret; |
| } |
| |
| FTS_INFO("create control procs succeeds"); |
| return 0; |
| } |
| |
| static void fts_free_ctrl_procs(void) |
| { |
| if (proc_fw_update) |
| proc_remove(proc_fw_update); |
| |
| if (proc_scan_modes) |
| proc_remove(proc_scan_modes); |
| |
| if (proc_touch_mode) |
| proc_remove(proc_touch_mode); |
| |
| if (proc_lpwg) |
| proc_remove(proc_lpwg); |
| |
| if (proc_high_sensitivity) |
| proc_remove(proc_high_sensitivity); |
| |
| if (proc_palm) |
| proc_remove(proc_palm); |
| |
| if (proc_grip) |
| proc_remove(proc_grip); |
| |
| if (proc_sense_onoff) |
| proc_remove(proc_sense_onoff); |
| |
| if (proc_irq_onoff) |
| proc_remove(proc_irq_onoff); |
| |
| if (proc_heatmap_onoff) |
| proc_remove(proc_heatmap_onoff); |
| |
| if (proc_LPTW_setting) |
| proc_remove(proc_LPTW_setting); |
| |
| if (proc_STTW_setting) |
| proc_remove(proc_STTW_setting); |
| |
| if (proc_mf_mode) |
| proc_remove(proc_mf_mode); |
| |
| if (proc_force_active) |
| proc_remove(proc_force_active); |
| } |
| |
| 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!!"); |
| } |
| |
| ts_data->proc_touch_entry = proc_mkdir("focaltech_touch", NULL); |
| if (!ts_data->proc_touch_entry) { |
| FTS_ERROR("create proc/focaltech_touch fails"); |
| } |
| |
| ret = fts_create_ctrl_procs(ts_data); |
| if (ret) { |
| FTS_ERROR("Create ctrl procs fails"); |
| } |
| |
| return ret; |
| } |
| |
| int fts_remove_sysfs(struct fts_ts_data *ts_data) |
| { |
| sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); |
| fts_free_ctrl_procs(); |
| if (ts_data->proc_touch_entry) |
| proc_remove(fts_data->proc_touch_entry); |
| return 0; |
| } |