// SPDX-License-Identifier: GPL-2.0-or-later #define LOG_TAG "Sysfs" #include "cts_config.h" #include "cts_platform.h" #include "cts_core.h" #include "cts_tcs.h" #include "cts_test.h" #include "cts_sfctrl.h" #include "cts_spi_flash.h" #include "cts_firmware.h" #include "cts_strerror.h" #include "cts_sysfs.h" #ifdef CONFIG_CTS_SYSFS #define SPLIT_LINE_STR \ "-----------------------------------------------"\ "------------------------------------------------\n" #define ROW_NUM_FORMAT_STR "%2d | " #define COL_NUM_FORMAT_STR "%4u " #define DATA_FORMAT_STR "%5d" #define RAWDATA_BUFFER_SIZE(cts_dev) \ (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) #define MAX_ARG_NUM (100) #define MAX_ARG_LENGTH (1024) #define HW_STUB_ADDR (0XF000) static char cmdline_param[MAX_ARG_LENGTH + 1]; int cts_argc; char *cts_argv[MAX_ARG_NUM]; int cts_parse_arg(const char *buf, size_t count) { char *p; size_t size; size = min((size_t)MAX_ARG_LENGTH, count); memcpy(cmdline_param, buf, size); cmdline_param[size] = '\0'; cts_argc = 0; p = strim(cmdline_param); if (p == NULL || p[0] == '\0') return 0; while (p && p[0] != '\0' && cts_argc < MAX_ARG_NUM) cts_argv[cts_argc++] = strsep(&p, " ,"); return cts_argc; } #ifdef CFG_CTS_FW_UPDATE_SYS static ssize_t cts_panel_supplier_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *data = dev_get_drvdata(dev); if (data->pdata && data->pdata->panel_supplier) { return scnprintf(buf, PAGE_SIZE, "%s\n", data->pdata->panel_supplier); } return 0; } static ssize_t buildid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "0000-%04x\n", cts_data->cts_dev.fwdata.version); } static ssize_t forcereflash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); unsigned int input; if (kstrtouint(buf, 10, &input) != 0) return -EINVAL; cts_data->force_reflash = (input == 0) ? false : true; cts_info("%s force_reflash=%d, count=%zu", __func__, (cts_data->force_reflash ? 1 : 0), count); return count; } static ssize_t flashprog_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", cts_is_firmware_updating(&cts_data->cts_dev) ? 1 : 0); } static bool is_reflash_filename_valid(const struct chipone_ts_data *cts_data, const char *filename) { char prefix[CFG_CTS_FW_FILE_NAME_MAX_LEN]; if (cts_data->pdata->panel_supplier != NULL) { snprintf(prefix, sizeof(prefix), "%s-%s-%s-", CFG_CTS_FW_FILE_NAME_VENDOR, cts_data->pdata->panel_supplier, cts_data->cts_dev.hwdata->name); } else { /* panel supplier not set, just check vendor. */ snprintf(prefix, sizeof(prefix), "%s", CFG_CTS_FW_FILE_NAME_VENDOR); } cts_info("%s: prefix=%s", __func__, prefix); if (strncmp(filename, prefix, strlen(prefix))) { cts_err("%s: invalid FW file.", __func__); return false; } return true; } static ssize_t doreflash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); int ret; cts_info("doreflash FW filename: %s len: %zu", buf, count); if (count > CFG_CTS_FW_FILE_NAME_MAX_LEN) { cts_err("doreflash FW filename is too long %zu > %d", count, CFG_CTS_FW_FILE_NAME_MAX_LEN); return -EINVAL; } if (cts_is_device_suspended(&cts_data->cts_dev)) { cts_err("In suspend state, try again later"); return -EAGAIN; } if (cts_is_firmware_updating(&cts_data->cts_dev)) { cts_err("In FW flashing state, try again later"); return -EAGAIN; } if (!cts_data->force_reflash) { /* Check filename if force_reflash is false */ if (!is_reflash_filename_valid(cts_data, buf)) { cts_err("Invalid firmware filename '%*.s'", (int)count, buf); return -EINVAL; } } strncpy(cts_data->cts_dev.config_fw_name, buf, count); /* If use echo xxx > doreflash, 0x0A will append to the string, * if use echo -n xxx > doreflash, nothing will append. */ if (cts_data->cts_dev.config_fw_name[count - 1] == '\n') cts_data->cts_dev.config_fw_name[count - 1] = '\0'; else cts_data->cts_dev.config_fw_name[count] = '\0'; cts_stop_device(&cts_data->cts_dev); cts_lock_device(&cts_data->cts_dev); ret = cts_update_firmware_from_file(&cts_data->cts_dev, cts_data->cts_dev.config_fw_name); cts_unlock_device(&cts_data->cts_dev); if (ret) cts_err("Update firmware from file '%s' failed %d", cts_data->cts_dev.config_fw_name, ret); cts_start_device(&cts_data->cts_dev); cts_data->force_reflash = false; cts_info("%s: end", __func__); return ret ? ret : count; } static ssize_t cts_poweron_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); bool val; /* TBD: check if cts is power to ready for flash. * set "1" if power on ready. */ val = cts_is_device_suspended(&cts_data->cts_dev); return scnprintf(buf, PAGE_SIZE, "%d\n", val == false); } static ssize_t cts_productinfo_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); /* set chip IC type to productinfo */ return scnprintf(buf, PAGE_SIZE, "%s\n", cts_data->cts_dev.hwdata->name); } /* add sys entries for FW update */ /* static DEVICE_ATTR(drv_irq, S_IRUGO | S_IWUSR, drv_irq_show, drv_irq_store); */ /* static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, reset_store); */ static DEVICE_ATTR(panel_supplier, 0444, cts_panel_supplier_show, NULL); static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); static DEVICE_ATTR(forcereflash, S_IWUSR | S_IWGRP, NULL, forcereflash_store); static DEVICE_ATTR(flashprog, S_IRUGO, flashprog_show, NULL); static DEVICE_ATTR(doreflash, S_IWUSR | S_IWGRP, NULL, doreflash_store); static DEVICE_ATTR(poweron, S_IRUGO, cts_poweron_show, NULL); static DEVICE_ATTR(productinfo, S_IRUGO, cts_productinfo_show, NULL); static struct attribute *cts_dev_fw_up_atts[] = { /** * &dev_attr_drv_irq.attr, * &dev_attr_reset.attr, */ &dev_attr_buildid.attr, &dev_attr_forcereflash.attr, &dev_attr_flashprog.attr, &dev_attr_doreflash.attr, &dev_attr_poweron.attr, &dev_attr_productinfo.attr, &dev_attr_panel_supplier.attr, NULL }; static const struct attribute_group cts_dev_fw_up_attr_group = { .attrs = cts_dev_fw_up_atts, }; #endif static ssize_t write_tcs_register_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u16 addr; int i, ret; u8 *data = NULL; cts_parse_arg(buf, count); cts_info("Write firmware register '%.*s'", (int)count, buf); if (cts_argc < 2) { cts_err("Too few args %d", cts_argc); return -EFAULT; } ret = kstrtou16(cts_argv[0], 0, &addr); if (ret) { cts_err("Invalid address %s", cts_argv[0]); return -EINVAL; } data = (u8 *) kmalloc(cts_argc - 1, GFP_KERNEL); if (data == NULL) { cts_err("Allocate buffer for write data failed\n"); return -ENOMEM; } for (i = 1; i < cts_argc; i++) { ret = kstrtou8(cts_argv[i], 0, data + i - 1); if (ret) { cts_err("Invalid value %s", cts_argv[i]); goto free_data; } } ret = cts_tcs_write(cts_dev, addr, data, cts_argc - 1); if (ret) { cts_err("Write tcs register addr: 0x%04x size: %d failed", addr, cts_argc - 1); goto free_data; } free_data: kfree(data); return (ret < 0 ? ret : count); } static DEVICE_ATTR(write_tcs_reg, S_IWUSR, NULL, write_tcs_register_store); static ssize_t read_tcs_register_show(struct device *dev, struct device_attribute *attr, char *buf) { #define PRINT_ROW_SIZE (16) struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u16 addr, size, i, remaining; u8 *data = NULL; ssize_t count = 0; int ret; cts_info("Read tcs register "); if (cts_argc != 2) { return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n" " 1. echo (0x)addr size > read_reg\n" " 2. cat read_reg\n", cts_argc); } ret = kstrtou16(cts_argv[0], 0, &addr); if (ret) { return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", cts_argv[0]); } ret = kstrtou16(cts_argv[1], 0, &size); if (ret) return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", cts_argv[1]); data = (u8 *) kmalloc(size, GFP_KERNEL); if (data == NULL) { return snprintf(buf, PAGE_SIZE, "Allocate buffer for read data failed\n"); } cts_info("Read tcs register from 0x%04x size %u", addr, size); cts_lock_device(cts_dev); ret = cts_tcs_read(cts_dev, addr, data, (size_t)size); cts_unlock_device(cts_dev); if (ret) { count = snprintf(buf, PAGE_SIZE, "Read tcs register from 0x%04x size %u failed %d\n", addr, size, ret); goto err_free_data; } remaining = size; for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); remaining -= PRINT_ROW_SIZE; count += snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); /* Lower version kernel return void */ hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, buf + count, PAGE_SIZE - count, true); count += strlen(buf + count); if (count < PAGE_SIZE) { buf[count++] = '\n'; addr += PRINT_ROW_SIZE; } else { break; } } err_free_data: kfree(data); return count; #undef PRINT_ROW_SIZE } static ssize_t read_tcs_register_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return (cts_argc == 0 ? 0 : count); } static DEVICE_ATTR(read_tcs_reg, S_IWUSR | S_IRUSR, read_tcs_register_show, read_tcs_register_store); static ssize_t read_hw_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { #define PRINT_ROW_SIZE (16) struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u32 addr, size, i, remaining; u8 *data = NULL; ssize_t count = 0; int ret; cts_info("Read hw register"); if (cts_argc != 2) { return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n" " 1. echo addr size > read_hw_reg\n" " 2. cat read_hw_reg\n", cts_argc); } ret = kstrtou32(cts_argv[0], 0, &addr); if (ret) { return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", cts_argv[0]); } ret = kstrtou32(cts_argv[1], 0, &size); if (ret) return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", cts_argv[1]); data = (u8 *) kmalloc(size, GFP_KERNEL); if (data == NULL) return snprintf(buf, PAGE_SIZE, "Allocate buffer for read data failed\n"); cts_info("Read hw register from 0x%04x size %u", addr, size); cts_lock_device(cts_dev); if (cts_dev->rtdata.program_mode) { for (i = 0; i < size; i++) { ret = cts_dev_readb(cts_dev, addr + i, data + i, 3, 10); if (ret) { count = snprintf(buf, PAGE_SIZE, "Write hw register error\n"); goto err_free_data; } } } else { ret = cts_tcs_read_hw_reg(cts_dev, addr, data, size); if (ret < 0) { count = snprintf(buf, PAGE_SIZE, "Read hw register error\n"); goto err_free_data; } } remaining = size; for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); remaining -= PRINT_ROW_SIZE; count += snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); /* Lower version kernel return void */ hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, buf + count, PAGE_SIZE - count, true); count += strlen(buf + count); if (count < PAGE_SIZE) { buf[count++] = '\n'; addr += PRINT_ROW_SIZE; } else { break; } } err_free_data: cts_unlock_device(cts_dev); kfree(data); return count; #undef PRINT_ROW_SIZE } /* echo addr size > read_reg */ static ssize_t read_hw_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return (cts_argc == 0 ? 0 : count); } static DEVICE_ATTR(read_hw_reg, S_IRUSR | S_IWUSR, read_hw_reg_show, read_hw_reg_store); static ssize_t write_hw_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u32 addr; int i, ret; u8 *data = NULL; cts_parse_arg(buf, count); cts_info("Write hw register"); if (cts_argc < 2) { cts_err("Too few args %d", cts_argc); return -EFAULT; } ret = kstrtou32(cts_argv[0], 0, &addr); if (ret) { cts_err("Invalid address %s", cts_argv[0]); return -EINVAL; } data = (u8 *) kmalloc(cts_argc - 1, GFP_KERNEL); if (data == NULL) { cts_err("Allocate buffer for write data failed\n"); return -ENOMEM; } for (i = 1; i < cts_argc; i++) { ret = kstrtou8(cts_argv[i], 0, data + i - 1); if (ret) { cts_err("Invalid value %s", cts_argv[i]); goto free_data; } } cts_lock_device(cts_dev); if (cts_dev->rtdata.program_mode) { for (i = 0; i < cts_argc - 1; i++) { ret = cts_dev_writeb(cts_dev, addr + i, data[i], 3, 10); if (ret) { cts_err("Write hw register error"); break; } } } else { ret = cts_tcs_write_hw_reg(cts_dev, addr, data, cts_argc - 1); if (ret < 0) cts_err("Write hw register error"); } cts_unlock_device(cts_dev); free_data: kfree(data); return (ret < 0 ? ret : count); } static DEVICE_ATTR(write_hw_reg, S_IWUSR, NULL, write_hw_reg_store); static ssize_t curr_firmware_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Current firmware version: %04x\n", cts_data->cts_dev.fwdata.version); } static DEVICE_ATTR(curr_version, S_IRUGO, curr_firmware_version_show, NULL); static ssize_t curr_ddi_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Current ddi version: %02x\n", cts_data->cts_dev.fwdata.ddi_version); } static DEVICE_ATTR(curr_ddi_version, S_IRUGO, curr_ddi_version_show, NULL); static ssize_t rows_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Num rows: %u\n", cts_data->cts_dev.fwdata.rows); } static DEVICE_ATTR(rows, S_IRUGO, rows_show, NULL); static ssize_t cols_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Num cols: %u\n", cts_data->cts_dev.fwdata.cols); } static DEVICE_ATTR(cols, S_IRUGO, cols_show, NULL); static ssize_t res_x_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "X Resolution: %u\n", cts_data->cts_dev.fwdata.res_x); } static DEVICE_ATTR(res_x, S_IRUGO, res_x_show, NULL); static ssize_t res_y_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Y Resolution: %u\n", cts_data->cts_dev.fwdata.res_y); } static DEVICE_ATTR(res_y, S_IRUGO, res_y_show, NULL); static ssize_t esd_protection_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int ret; u8 esd_protection; cts_lock_device(cts_dev); ret = cts_tcs_get_esd_protection(cts_dev, &esd_protection); cts_unlock_device(cts_dev); if (ret) return snprintf(buf, PAGE_SIZE, "Read firmware ESD protection register failed %d\n", ret); return snprintf(buf, PAGE_SIZE, "ESD protection: %u\n", esd_protection); } static DEVICE_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); static ssize_t monitor_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int ret; u8 value; cts_lock_device(cts_dev); ret = cts_tcs_is_mnt_enabled(cts_dev, &value); cts_unlock_device(cts_dev); if (ret) return snprintf(buf, PAGE_SIZE, "Read firmware monitor enable register failed %d\n", ret); return snprintf(buf, PAGE_SIZE, "Monitor mode: %s\n", value & BIT(0) ? "Enable" : "Disable"); } static ssize_t monitor_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int ret; u8 value, enable = 0; if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') enable = 1; cts_info("Write firmware monitor mode to '%c', %s", buf[0], enable ? "Enable" : "Disable"); cts_lock_device(cts_dev); ret = cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 344, &value); cts_unlock_device(cts_dev); if (ret) { cts_err("Write firmware monitor enable register failed %d", ret); return -EIO; } if ((value & BIT(0)) && enable) cts_info("Monitor mode already enabled"); else if ((value & BIT(0)) == 0 && enable == 0) cts_info("Monitor mode already disabled"); else { if (enable) value |= BIT(0); else value &= ~BIT(0); cts_lock_device(cts_dev); ret = cts_fw_reg_writeb(&cts_data->cts_dev, 0x8000 + 344, value); cts_unlock_device(cts_dev); if (ret) { cts_err("Write firmware monitor enable register failed %d", ret); return -EIO; } } return count; } static DEVICE_ATTR(monitor_mode, S_IRUGO, monitor_mode_show, monitor_mode_store); static ssize_t auto_compensate_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int ret; u8 value; cts_lock_device(cts_dev); ret = cts_tcs_is_cneg_enabled(&cts_data->cts_dev, &value); cts_unlock_device(cts_dev); if (ret) return snprintf(buf, PAGE_SIZE, "Read auto compensate enable register failed %d\n", ret); return snprintf(buf, PAGE_SIZE, "Auto compensate: %s\n", value ? "Enable" : "Disable"); } static DEVICE_ATTR(auto_compensate, S_IRUGO, auto_compensate_show, NULL); #ifdef CFG_CTS_FIRMWARE_IN_FS /* echo filepath [flash/sram] > update_firmware_from_file */ static ssize_t update_firmware_from_file_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; bool to_flash = true; int ret; cts_parse_arg(buf, count); if (cts_argc > 2) { cts_err("Invalid num args %d\n" " echo filepath [flash/sram] > update_from_file\n", cts_argc); return -EFAULT; } else if (cts_argc > 1) { if (strncasecmp(cts_argv[1], "flash", 5) == 0) to_flash = true; else if (strncasecmp(cts_argv[1], "sram", 4) == 0) to_flash = false; else { cts_err("Invalid location '%s', must be 'flash' or 'sram'", cts_argv[1]); return -EINVAL; } } cts_info("Update firmware from file '%s'", cts_argv[0]); ret = cts_request_newer_firmware_from_fs(cts_dev, cts_argv[0], 0); if (ret) { cts_err("Request firmware from file '%s' failed", cts_argv[0]); return -ENOENT; } cts_reset_device(cts_dev); ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto err_release_firmware; } cts_lock_device(cts_dev); ret = cts_update_firmware(cts_dev); cts_unlock_device(cts_dev); if (ret) { cts_err("Update firmware failed %d", ret); goto err_release_firmware; } ret = cts_start_device(cts_dev); if (ret) { cts_err("Start device failed %d", ret); goto err_release_firmware; } ret = count; err_release_firmware: return ret; } static DEVICE_ATTR(update_from_file, S_IWUSR, NULL, update_firmware_from_file_store); #endif /* CFG_CTS_FIRMWARE_IN_FS */ static ssize_t updating_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Updating: %s\n", cts_data->cts_dev.rtdata.updating ? "Y" : "N"); } static DEVICE_ATTR(updating, S_IRUGO, updating_show, NULL); static struct attribute *cts_dev_firmware_atts[] = { &dev_attr_curr_version.attr, &dev_attr_curr_ddi_version.attr, &dev_attr_rows.attr, &dev_attr_cols.attr, &dev_attr_res_x.attr, &dev_attr_res_y.attr, &dev_attr_esd_protection.attr, &dev_attr_monitor_mode.attr, &dev_attr_auto_compensate.attr, #ifdef CFG_CTS_FIRMWARE_IN_FS &dev_attr_update_from_file.attr, #endif /* CFG_CTS_FIRMWARE_IN_FS */ &dev_attr_updating.attr, NULL }; static const struct attribute_group cts_dev_firmware_attr_group = { .name = "cts_firmware", .attrs = cts_dev_firmware_atts, }; static ssize_t flash_info_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; const struct cts_flash *flash; if (cts_dev->flash == NULL) { bool program_mode; bool enabled; int ret; program_mode = cts_is_device_program_mode(cts_dev); enabled = cts_is_device_enabled(cts_dev); ret = cts_prepare_flash_operation(cts_dev); if (ret) return snprintf(buf, PAGE_SIZE, "Prepare flash operation failed %d", ret); cts_post_flash_operation(cts_dev); if (!program_mode) { ret = cts_enter_normal_mode(cts_dev); if (ret) return snprintf(buf, PAGE_SIZE, "Enter normal mode failed %d", ret); } if (enabled) { ret = cts_start_device(cts_dev); if (ret) return snprintf(buf, PAGE_SIZE, "Start device failed %d", ret); } if (cts_dev->flash == NULL) return snprintf(buf, PAGE_SIZE, "Flash not found\n"); } flash = cts_dev->flash; return snprintf(buf, PAGE_SIZE, "%s:\n" " JEDEC ID : %06X\n" " Page size : 0x%zx\n" " Sector size: 0x%zx\n" " Block size : 0x%zx\n" " Total size : 0x%zx\n", flash->name, flash->jedec_id, flash->page_size, flash->sector_size, flash->block_size, flash->total_size); } static DEVICE_ATTR(info, S_IRUGO, flash_info_show, NULL); static ssize_t read_flash_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u32 flash_addr, size, i, remaining; u8 *data = NULL; ssize_t count = 0; int ret; bool program_mode; bool enabled; #ifndef CFG_CTS_FOR_GKI loff_t pos = 0; #endif if (cts_argc != 2 && cts_argc != 3) return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", cts_argc); ret = kstrtou32(cts_argv[0], 0, &flash_addr); if (ret) return snprintf(buf, PAGE_SIZE, "Invalid flash addr: %s\n", cts_argv[0]); ret = kstrtou32(cts_argv[1], 0, &size); if (ret) return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", cts_argv[1]); data = (u8 *) kmalloc(size, GFP_KERNEL); if (data == NULL) return snprintf(buf, PAGE_SIZE, "Allocate buffer for read data failed\n"); cts_info("Read flash from 0x%06x size %u%s%s", flash_addr, size, cts_argc == 3 ? " to file " : "", cts_argc == 3 ? cts_argv[2] : ""); cts_lock_device(cts_dev); program_mode = cts_is_device_program_mode(cts_dev); enabled = cts_is_device_enabled(cts_dev); ret = cts_prepare_flash_operation(cts_dev); if (ret) { count += snprintf(buf, PAGE_SIZE, "Prepare flash operation failed %d", ret); goto err_free_data; } ret = cts_read_flash(cts_dev, flash_addr, data, size); if (ret) { count = snprintf(buf, PAGE_SIZE, "Read flash data failed %d\n", ret); goto err_post_flash_operation; } if (cts_argc == 3) { #ifdef CFG_CTS_FOR_GKI cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); #else struct file *file; cts_info("Write flash data to file '%s'", cts_argv[2]); file = filp_open(cts_argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); if (IS_ERR(file)) { count += snprintf(buf, PAGE_SIZE, "Open file '%s' failed %ld", cts_argv[2], PTR_ERR(file)); goto err_post_flash_operation; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) ret = kernel_write(file, data, size, &pos); #else ret = kernel_write(file, data, size, pos); #endif if (ret != size) count += snprintf(buf, PAGE_SIZE, "Write flash data to file '%s' failed %d", cts_argv[2], ret); ret = filp_close(file, NULL); if (ret) count += snprintf(buf, PAGE_SIZE, "Close file '%s' failed %d", cts_argv[2], ret); #endif } else { #define PRINT_ROW_SIZE (16) remaining = size; for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); remaining -= PRINT_ROW_SIZE; count += snprintf(buf + count, PAGE_SIZE - count - 1, "%04x-%04x: ", flash_addr >> 16, flash_addr & 0xFFFF); /* Lower version kernel return void */ hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, buf + count, PAGE_SIZE - count - 1, true); count += strlen(buf + count); buf[count++] = '\n'; flash_addr += linelen; #undef PRINT_ROW_SIZE } } err_post_flash_operation: cts_post_flash_operation(cts_dev); if (!program_mode) { int r = cts_enter_normal_mode(cts_dev); if (r) count += snprintf(buf, PAGE_SIZE, "Enter normal mode failed %d", r); } if (enabled) { int r = cts_start_device(cts_dev); if (r) { cts_unlock_device(cts_dev); return snprintf(buf, PAGE_SIZE, "Start device failed %d", r); } } err_free_data: cts_unlock_device(cts_dev); kfree(data); return (ret < 0 ? ret : count); } /* echo start_addr size [filepath] > read */ static ssize_t read_flash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(read, S_IWUSR | S_IRUGO, read_flash_show, read_flash_store); /* echo addr size > erase */ static ssize_t erase_flash_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u32 flash_addr, size; int ret; bool program_mode; bool enabled; cts_parse_arg(buf, count); if (cts_argc != 2) { cts_err("Invalid num args %d", cts_argc); return -EFAULT; } ret = kstrtou32(cts_argv[0], 0, &flash_addr); if (ret) { cts_err("Invalid flash addr: %s", cts_argv[0]); return -EINVAL; } ret = kstrtou32(cts_argv[1], 0, &size); if (ret) { cts_err("Invalid size: %s", cts_argv[1]); return -EINVAL; } cts_info("Erase flash from 0x%06x size %u", flash_addr, size); cts_lock_device(cts_dev); program_mode = cts_is_device_program_mode(cts_dev); enabled = cts_is_device_enabled(cts_dev); ret = cts_prepare_flash_operation(cts_dev); if (ret) { cts_err("Prepare flash operation failed %d", ret); cts_unlock_device(cts_dev); return ret; } ret = cts_erase_flash(cts_dev, flash_addr, size); if (ret) { cts_err("Erase flash from 0x%06x size %u failed %d", flash_addr, size, ret); goto err_post_flash_operation; } err_post_flash_operation: cts_post_flash_operation(cts_dev); if (!program_mode) { int r = cts_enter_normal_mode(cts_dev); if (r) cts_err("Enter normal mode failed %d", r); } if (enabled) { int r = cts_start_device(cts_dev); if (r) cts_err("Start device failed %d", r); } cts_unlock_device(cts_dev); return (ret < 0 ? ret : count); } static DEVICE_ATTR(erase, S_IWUSR, NULL, erase_flash_store); static struct attribute *cts_dev_flash_attrs[] = { &dev_attr_info.attr, &dev_attr_read.attr, &dev_attr_erase.attr, NULL }; static const struct attribute_group cts_dev_flash_attr_group = { .name = "flash", .attrs = cts_dev_flash_attrs, }; static ssize_t open_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_test_param test_param = { .test_item = CTS_TEST_OPEN, .flags = CTS_TEST_FLAG_VALIDATE_DATA | CTS_TEST_FLAG_VALIDATE_MIN | CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, .test_data_filepath = "/sdcard/open-test-data.txt", .num_invalid_node = 0, .invalid_nodes = NULL, }; int min = 0; int ret; ktime_t start_time, end_time, delta_time; if (cts_argc != 1) { return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", cts_argc); } ret = kstrtoint(cts_argv[0], 0, &min); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", cts_argv[0]); } cts_info("Open test, threshold = %u", min); test_param.min = &min; start_time = ktime_get(); ret = cts_test_open(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { return scnprintf(buf, PAGE_SIZE, "Open test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", ret, min, ktime_to_ms(delta_time)); } else if (ret < 0) { return scnprintf(buf, PAGE_SIZE, "Open test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "Open test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } /* echo threshod > open_test */ static ssize_t open_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(open_test, S_IWUSR | S_IRUGO, open_test_show, open_test_store); static ssize_t short_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_test_param test_param = { .test_item = CTS_TEST_SHORT, .flags = CTS_TEST_FLAG_VALIDATE_DATA | CTS_TEST_FLAG_VALIDATE_MIN | CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, .test_data_filepath = "/sdcard/short-test-data.txt", .num_invalid_node = 0, .invalid_nodes = NULL, }; int min = 0; int ret; ktime_t start_time, end_time, delta_time; if (cts_argc != 1) { return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", cts_argc); } ret = kstrtoint(cts_argv[0], 0, &min); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", cts_argv[0]); } cts_info("Short test, threshold = %u", min); test_param.min = &min; start_time = ktime_get(); ret = cts_test_short(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { return scnprintf(buf, PAGE_SIZE, "Short test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", ret, min, ktime_to_ms(delta_time)); } else if (ret < 0) { return scnprintf(buf, PAGE_SIZE, "Short test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "Short test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } /* echo threshod > short_test */ static ssize_t short_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(short_test, S_IWUSR | S_IRUGO, short_test_show, short_test_store); static ssize_t testing_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Testting: %s\n", cts_data->cts_dev.rtdata.testing ? "Y" : "N"); } static DEVICE_ATTR(testing, S_IRUGO, testing_show, NULL); #ifdef CFG_CTS_HAS_RESET_PIN static ssize_t reset_pin_test_show(struct device *dev, struct device_attribute *attr, char *buf) { #ifdef CFG_CTS_HAS_RESET_PIN struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_test_param test_param = { .test_item = CTS_TEST_RESET_PIN, .flags = 0, }; int ret; ktime_t start_time, end_time, delta_time; start_time = ktime_get(); ret = cts_test_reset_pin(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret) { return scnprintf(buf, PAGE_SIZE, "Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "Reset-Pin test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } #else /* CFG_CTS_HAS_RESET_PIN */ return scnprintf(buf, PAGE_SIZE, "Reset-Pin test NOT supported(CFG_CTS_HAS_RESET_PIN not defined)\n"); #endif } static DEVICE_ATTR(reset_pin_test, S_IRUGO, reset_pin_test_show, NULL); #endif static ssize_t int_pin_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_test_param test_param = { .test_item = CTS_TEST_INT_PIN, .flags = 0, }; int ret; ktime_t start_time, end_time, delta_time; start_time = ktime_get(); ret = cts_test_int_pin(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret) { return scnprintf(buf, PAGE_SIZE, "Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "Int-Pin test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } static DEVICE_ATTR(int_pin_test, S_IRUGO, int_pin_test_show, NULL); static ssize_t compensate_cap_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_test_param test_param = { .test_item = CTS_TEST_COMPENSATE_CAP, .flags = CTS_TEST_FLAG_VALIDATE_DATA | CTS_TEST_FLAG_VALIDATE_MIN | CTS_TEST_FLAG_VALIDATE_MAX | CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, .test_data_filepath = "/sdcard/comp-cap-test-data.txt", .num_invalid_node = 0, .invalid_nodes = NULL, }; int min = 0, max = 0; int ret; ktime_t start_time, end_time, delta_time; if (cts_argc != 2) { return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" "USAGE:\n" " 1. echo min max > compensate_cap_test\n" " 2. cat compensate_cap_test\n"); } ret = kstrtoint(cts_argv[0], 0, &min); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", cts_argv[0]); } ret = kstrtoint(cts_argv[1], 0, &max); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid max thres: %s\n", cts_argv[1]); } cts_info("Compensate cap test, min: %u, max: %u", min, max); test_param.min = &min; test_param.max = &max; start_time = ktime_get(); ret = cts_test_compensate_cap(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { return scnprintf(buf, PAGE_SIZE, "Compensate cap test has %d nodes FAIL, " "threshold[%u, %u], ELAPSED TIME: %lldms\n", ret, min, max, ktime_to_ms(delta_time)); } else if (ret < 0) { return scnprintf(buf, PAGE_SIZE, "Compensate cap test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "Compensate cap test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } /* echo threshod > short_test */ static ssize_t compensate_cap_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(compensate_cap_test, S_IWUSR | S_IRUGO, compensate_cap_test_show, compensate_cap_test_store); static ssize_t rawdata_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_rawdata_test_priv_param priv_param = { .frames = 16, //.work_mode = 0, }; struct cts_test_param test_param = { .test_item = CTS_TEST_RAWDATA, .flags = CTS_TEST_FLAG_VALIDATE_DATA | CTS_TEST_FLAG_VALIDATE_MIN | CTS_TEST_FLAG_VALIDATE_MAX | CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, .test_data_filepath = "/sdcard/rawdata-test-data.txt", .num_invalid_node = 0, .invalid_nodes = NULL, .priv_param = &priv_param, .priv_param_size = sizeof(priv_param), }; int min, max; int ret; ktime_t start_time, end_time, delta_time; if (cts_argc < 2 || cts_argc > 3) { return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" "USAGE:\n" " 1. echo min max [frames] > rawdata_test\n" " 2. cat rawdata_test\n"); } ret = kstrtoint(cts_argv[0], 0, &min); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", cts_argv[0]); } ret = kstrtoint(cts_argv[1], 0, &max); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid max thres: %s\n", cts_argv[1]); } if (cts_argc > 2) { ret = kstrtou32(cts_argv[2], 0, &priv_param.frames); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid frames: %s\n", cts_argv[2]); } } cts_info("Rawdata test, frames: %u min: %d, max: %d", priv_param.frames, min, max); test_param.min = &min; test_param.max = &max; start_time = ktime_get(); ret = cts_test_rawdata(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { return scnprintf(buf, PAGE_SIZE, "Rawdata test has %d nodes FAIL, threshold[%u, %u], " "ELAPSED TIME: %lldms\n", ret, min, max, ktime_to_ms(delta_time)); } else if (ret < 0) { return scnprintf(buf, PAGE_SIZE, "Rawdata test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "Rawdata test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } /* echo threshod > short_test */ static ssize_t rawdata_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(rawdata_test, S_IWUSR | S_IRUGO, rawdata_test_show, rawdata_test_store); static ssize_t noise_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_noise_test_priv_param priv_param = { .frames = 50, //.work_mode = 0, }; struct cts_test_param test_param = { .test_item = CTS_TEST_NOISE, .flags = CTS_TEST_FLAG_VALIDATE_DATA | CTS_TEST_FLAG_VALIDATE_MAX | CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, .test_data_filepath = "/sdcard/noise-test-data.txt", .num_invalid_node = 0, .invalid_nodes = NULL, .priv_param = &priv_param, .priv_param_size = sizeof(priv_param), }; int max; int ret; ktime_t start_time, end_time, delta_time; if (cts_argc < 1 || cts_argc > 2) { return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" "USAGE:\n" " 1. echo threshold [frames] > noise_test\n" " 2. cat noise_test\n"); } ret = kstrtoint(cts_argv[0], 0, &max); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid max thres: %s\n", cts_argv[0]); } if (cts_argc > 1) { ret = kstrtou32(cts_argv[1], 0, &priv_param.frames); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid frames: %s\n", cts_argv[1]); } } cts_info("Noise test, frames: %u threshold: %d", priv_param.frames, max); test_param.max = &max; start_time = ktime_get(); ret = cts_test_noise(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { return scnprintf(buf, PAGE_SIZE, "Noise test has %d nodes FAIL, max: %u, ELAPSED TIME: %lldms\n", ret, max, ktime_to_ms(delta_time)); } else if (ret < 0) { return scnprintf(buf, PAGE_SIZE, "Noise test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "Noise test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } static ssize_t noise_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(noise_test, S_IWUSR | S_IRUGO, noise_test_show, noise_test_store); static ssize_t stylus_rawdata_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_rawdata_test_priv_param priv_param = { .frames = 16, //.work_mode = 0, }; struct cts_test_param test_param = { .test_item = CTS_TEST_RAWDATA, .flags = CTS_TEST_FLAG_VALIDATE_DATA | CTS_TEST_FLAG_VALIDATE_MIN | CTS_TEST_FLAG_VALIDATE_MAX | CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, .test_data_filepath = "/sdcard/stylus-rawdata-test-data.txt", .num_invalid_node = 0, .invalid_nodes = NULL, .priv_param = &priv_param, .priv_param_size = sizeof(priv_param), }; int min, max; int ret; ktime_t start_time, end_time, delta_time; if (cts_argc < 2 || cts_argc > 3) { return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" "USAGE:\n" " 1. echo min max [frames] > stylus_rawdata_test\n" " 2. cat stylus_rawdata_test\n"); } ret = kstrtoint(cts_argv[0], 0, &min); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", cts_argv[0]); } ret = kstrtoint(cts_argv[1], 0, &max); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid max thres: %s\n", cts_argv[1]); } if (cts_argc > 2) { ret = kstrtou32(cts_argv[2], 0, &priv_param.frames); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid frames: %s\n", cts_argv[2]); } } cts_info("stylus rawdata test, frames: %u min: %d, max: %d", priv_param.frames, min, max); test_param.min = &min; test_param.max = &max; start_time = ktime_get(); ret = cts_test_stylus_rawdata(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { return scnprintf(buf, PAGE_SIZE, "stylus rawdata test has %d nodes FAIL, threshold[%u, %u], " "ELAPSED TIME: %lldms\n", ret, min, max, ktime_to_ms(delta_time)); } else if (ret < 0) { return scnprintf(buf, PAGE_SIZE, "stylus rawdata test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "stylus rawdata test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } /* echo threshod > short_test */ static ssize_t stylus_rawdata_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(stylus_rawdata_test, S_IWUSR | S_IRUGO, stylus_rawdata_test_show, stylus_rawdata_test_store); static ssize_t stylus_noise_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_noise_test_priv_param priv_param = { .frames = 50, //.work_mode = 0, }; struct cts_test_param test_param = { .test_item = CTS_TEST_NOISE, .flags = CTS_TEST_FLAG_VALIDATE_DATA | CTS_TEST_FLAG_VALIDATE_MAX | CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, .test_data_filepath = "/sdcard/stylus-noise-test-data.txt", .num_invalid_node = 0, .invalid_nodes = NULL, .priv_param = &priv_param, .priv_param_size = sizeof(priv_param), }; int max; int ret; ktime_t start_time, end_time, delta_time; if (cts_argc < 1 || cts_argc > 2) { return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" "USAGE:\n" " 1. echo threshold [frames] > stylus_noise_test\n" " 2. cat stylus_noise_test\n"); } ret = kstrtoint(cts_argv[0], 0, &max); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid max thres: %s\n", cts_argv[0]); } if (cts_argc > 1) { ret = kstrtou32(cts_argv[1], 0, &priv_param.frames); if (ret) { return scnprintf(buf, PAGE_SIZE, "Invalid frames: %s\n", cts_argv[1]); } } cts_info("stylus noise test, frames: %u threshold: %d", priv_param.frames, max); test_param.max = &max; start_time = ktime_get(); ret = cts_test_stylus_noise(cts_dev, &test_param); end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { return scnprintf(buf, PAGE_SIZE, "stylus noise test has %d nodes FAIL, max: %u, ELAPSED TIME: %lldms\n", ret, max, ktime_to_ms(delta_time)); } else if (ret < 0) { return scnprintf(buf, PAGE_SIZE, "stylus noise test FAIL %d(%s), ELAPSED TIME: %lldms\n", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { return scnprintf(buf, PAGE_SIZE, "stylus noise test PASS, ELAPSED TIME: %lldms\n", ktime_to_ms(delta_time)); } } static ssize_t stylus_noise_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { cts_parse_arg(buf, count); return count; } static DEVICE_ATTR(stylus_noise_test, S_IWUSR | S_IRUGO, stylus_noise_test_show, stylus_noise_test_store); static struct attribute *cts_dev_test_atts[] = { &dev_attr_open_test.attr, &dev_attr_short_test.attr, &dev_attr_testing.attr, #ifdef CFG_CTS_HAS_RESET_PIN &dev_attr_reset_pin_test.attr, #endif &dev_attr_int_pin_test.attr, &dev_attr_compensate_cap_test.attr, &dev_attr_rawdata_test.attr, &dev_attr_noise_test.attr, &dev_attr_stylus_rawdata_test.attr, &dev_attr_stylus_noise_test.attr, NULL }; static const struct attribute_group cts_dev_test_attr_group = { .name = "test", .attrs = cts_dev_test_atts, }; static ssize_t ic_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "IC Type : %s\n", cts_data->cts_dev.hwdata->name); } static DEVICE_ATTR(ic_type, S_IRUGO, ic_type_show, NULL); static ssize_t program_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "Program mode: %s\n", cts_data->cts_dev.rtdata.program_mode ? "Y" : "N"); } static ssize_t program_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); int ret; cts_parse_arg(buf, count); if (cts_argc != 1) { cts_err("Invalid num args %d", cts_argc); return -EFAULT; } if (*cts_argv[0] == '1' || tolower(*cts_argv[0]) == 'y') { ret = cts_enter_program_mode(&cts_data->cts_dev); if (ret) { cts_err("Enter program mode failed %d", ret); return ret; } } else if (*cts_argv[0] == '0' || tolower(*cts_argv[0]) == 'n') { ret = cts_enter_normal_mode(&cts_data->cts_dev); if (ret) { cts_err("Exit program mode failed %d", ret); return ret; } } else cts_err("Invalid args"); return count; } static DEVICE_ATTR(program_mode, S_IWUSR | S_IRUGO, program_mode_show, program_mode_store); #ifdef CFG_CTS_HAS_RESET_PIN #ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED static ssize_t reset_pin_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); cts_info("Read RESET-PIN"); return snprintf(buf, PAGE_SIZE, "Reset pin: %d, status: %d\n", cts_data->pdata->rst_gpio, gpio_get_value(cts_data->pdata->rst_gpio)); } #endif static ssize_t reset_pin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; cts_info("Write RESET-PIN"); cts_info("Chip staus maybe changed"); cts_plat_set_reset(cts_dev->pdata, (buf[0] == '1') ? 1 : 0); return count; } #ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED static DEVICE_ATTR(reset_pin, S_IRUSR | S_IWUSR, reset_pin_show, reset_pin_store); #else static DEVICE_ATTR(reset_pin, S_IWUSR, NULL, reset_pin_store); #endif #endif #ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED static ssize_t irq_pin_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); cts_info("Read IRQ-PIN"); return snprintf(buf, PAGE_SIZE, "IRQ pin: %d, status: %d\n", cts_data->pdata->int_gpio, gpio_get_value(cts_data->pdata->int_gpio)); } static DEVICE_ATTR(irq_pin, S_IRUGO, irq_pin_show, NULL); #endif static ssize_t irq_info_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct irq_desc *desc; cts_info("Read IRQ-INFO"); desc = irq_to_desc(cts_data->pdata->irq); if (desc == NULL) { return snprintf(buf, PAGE_SIZE, "IRQ: %d descriptor not found\n", cts_data->pdata->irq); } return scnprintf(buf, PAGE_SIZE, "IRQ num: %d, depth: %u, " "count: %u, unhandled: %u, last unhandled eslape: %lu, irq flags: 0x%x, int_mode: %s\n", cts_data->pdata->irq, desc->depth, desc->irq_count, desc->irqs_unhandled, desc->last_unhandled, desc->action->flags, (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_RISING ? "IRQF_TRIGGER_RISING" : (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_FALLING ? "IRQF_TRIGGER_FALLING" : (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_HIGH ? "IRQF_TRIGGER_HIGH" : (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_LOW ? "IRQF_TRIGGER_LOW " : "IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING"); } static DEVICE_ATTR(irq_info, S_IRUGO, irq_info_show, NULL); #ifndef CONFIG_CTS_I2C_HOST static ssize_t debug_spi_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; return snprintf(buf, PAGE_SIZE, "spi_speed=%d\n", cts_dev->pdata->spi_speed); } static ssize_t debug_spi_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u16 s = 0; int ret = 0; struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; cts_parse_arg(buf, count); if (cts_argc != 1) { cts_err("Invalid num args %d", cts_argc); return -EFAULT; } ret = kstrtou16(cts_argv[0], 0, &s); if (ret) { cts_err("Invalid spi speed: %s", cts_argv[0]); return -EINVAL; } cts_dev->pdata->spi_speed = s; return count; } static DEVICE_ATTR(debug_spi, S_IRUSR | S_IWUSR, debug_spi_show, debug_spi_store); #endif #ifdef CFG_CTS_GESTURE static ssize_t gesture_en_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; return sprintf(buf, "Gesture wakup is %s\n", cts_is_gesture_wakeup_enabled(cts_dev) ? "enable" : "disable"); } static ssize_t gesture_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u8 enable = 0; if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') enable = 1; if (enable) cts_enable_gesture_wakeup(cts_dev); else cts_disable_gesture_wakeup(cts_dev); return count; } static DEVICE_ATTR(gesture_en, S_IRUSR | S_IWUSR, gesture_en_show, gesture_en_store); #endif static ssize_t int_data_types_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%#04x\n", cts_data->cts_dev.fwdata.int_data_types); } static ssize_t int_data_types_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); u16 type = 0; int ret = 0; cts_parse_arg(buf, count); if (cts_argc != 1) { cts_err("Invalid num args %d", cts_argc); return -EFAULT; } ret = kstrtou16(cts_argv[0], 0, &type); if (ret) { cts_err("Invalid int data types: %s", cts_argv[0]); return -EINVAL; } cts_lock_device(&cts_data->cts_dev); ret = cts_set_int_data_types(&cts_data->cts_dev, type); cts_unlock_device(&cts_data->cts_dev); if (ret) return -EIO; return count; } static DEVICE_ATTR(int_data_types, S_IWUSR | S_IRUGO, int_data_types_show, int_data_types_store); static ssize_t int_data_method_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", cts_data->cts_dev.fwdata.int_data_method); } static ssize_t int_data_method_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); u8 method = 0; int ret = 0; cts_parse_arg(buf, count); if (cts_argc != 1) { cts_err("Invalid num args %d", cts_argc); return -EFAULT; } ret = kstrtou8(cts_argv[0], 0, &method); if (ret) { cts_err("Invalid int data method: %s", cts_argv[0]); return -EINVAL; } else if (method >= INT_DATA_METHOD_CNT) { cts_err("Invalid int data method: %s", cts_argv[0]); return -EINVAL; } cts_lock_device(&cts_data->cts_dev); ret = cts_set_int_data_method(&cts_data->cts_dev, method); cts_unlock_device(&cts_data->cts_dev); if (ret) return -EIO; return count; } static DEVICE_ATTR(int_data_method, S_IWUSR | S_IRUGO, int_data_method_show, int_data_method_store); static ssize_t cts_charger_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u8 val; int ret; cts_info("Read sysfs '/%s'", attr->attr.name); cts_lock_device(cts_dev); ret = cts_tcs_get_charger_plug(cts_dev, &val); cts_unlock_device(cts_dev); if (ret) { cts_err("Get charger state failed %d(%s)", ret, cts_strerror(ret)); return -1; } switch (val) { case 0: return scnprintf(buf, PAGE_SIZE, "Detached\n"); case 1: return scnprintf(buf, PAGE_SIZE, "Attached\n"); default: return scnprintf(buf, PAGE_SIZE, "Read error\n"); } } static ssize_t cts_charger_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int state; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); switch (buf[0]) { case '0': state = 0; break; case '1': state = 1; break; default: cts_err("Invalid arg for state"); return -EINVAL; } cts_info("state = %d", state); cts_lock_device(cts_dev); ret = cts_tcs_set_charger_plug(cts_dev, state); cts_unlock_device(cts_dev); if (ret) { cts_err("Set charger state failed %d(%s)", ret, cts_strerror(ret)); return -EIO; } return count; } static DEVICE_ATTR(charger_state, S_IRUGO | S_IWUSR, cts_charger_state_show, cts_charger_state_store); static ssize_t cts_earjack_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u8 val; int ret; cts_info("Read sysfs '/%s'", attr->attr.name); cts_lock_device(cts_dev); ret = cts_tcs_get_earjack_plug(cts_dev, &val); cts_unlock_device(cts_dev); if (ret) { cts_err("Get earjack state failed %d(%s)", ret, cts_strerror(ret)); return -1; } switch (val) { case 0: return scnprintf(buf, PAGE_SIZE, "Detached\n"); case 1: return scnprintf(buf, PAGE_SIZE, "Attached\n"); default: return scnprintf(buf, PAGE_SIZE, "Read error\n"); } } static ssize_t cts_earjack_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int state; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); switch (buf[0]) { case '0': state = 0; break; case '1': state = 1; break; default: cts_err("Invalid arg for state"); return -EINVAL; } cts_info("state = %d", state); cts_lock_device(cts_dev); ret = cts_tcs_set_earjack_plug(cts_dev, state); cts_unlock_device(cts_dev); if (ret) { cts_err("Set earjack state failed %d(%s)", ret, cts_strerror(ret)); return -EIO; } return count; } static DEVICE_ATTR(earjack_state, S_IRUGO | S_IWUSR, cts_earjack_state_show, cts_earjack_state_store); static ssize_t cts_edge_restain_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u8 direction; int ret; cts_info("Read sysfs '/%s'", attr->attr.name); cts_lock_device(cts_dev); ret = cts_tcs_get_panel_direction(cts_dev, &direction); cts_unlock_device(cts_dev); if (ret) { cts_err("get panel direction failed!"); return scnprintf(buf, PAGE_SIZE, "Read error\n"); } return scnprintf(buf, PAGE_SIZE, "direction: 0x%02x\n", direction); } static ssize_t cts_edge_restain_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u8 direction; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); switch (buf[0]) { case '0': direction = 0; break;//normal case '1': direction = 1; break;//notch left case '2': direction = 2; break;//notch right default: cts_err("Invalid arg for mode"); return -EINVAL; } cts_info("direction = %d", direction); cts_lock_device(cts_dev); ret = cts_tcs_set_panel_direction(cts_dev, direction); cts_unlock_device(cts_dev); if (ret) { cts_err("Set edge restain failed"); return -EIO; } return count; } static DEVICE_ATTR(edge_restain, S_IWUSR | S_IRUGO, cts_edge_restain_show, cts_edge_restain_store); static ssize_t cts_game_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u8 enabled; int ret; cts_info("Read sysfs '/%s'", attr->attr.name); cts_lock_device(cts_dev); ret = cts_tcs_get_game_mode(cts_dev, &enabled); cts_unlock_device(cts_dev); if (ret) { cts_err("get game mode failed!"); return scnprintf(buf, PAGE_SIZE, "Read error\n"); } return scnprintf(buf, PAGE_SIZE, "game mode: 0x%02x\n", enabled); } static ssize_t cts_game_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int enable = -1; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); if (count >= 1) { if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { enable = 1; } else if (buf[0] == 'N' || buf[0] == 'n' || buf[0] == '0') { enable = 0; } } if (enable == -1) { cts_err("Invalid arg for game mode enable"); return -EINVAL; } cts_lock_device(cts_dev); ret = cts_tcs_set_game_mode(cts_dev, enable); cts_unlock_device(cts_dev); if (ret) { cts_err("Set game mode failed"); return -EIO; } return count; } static DEVICE_ATTR(game_mode, S_IWUSR | S_IRUGO, cts_game_mode_show, cts_game_mode_store); #ifdef CONFIG_CTS_TP_PROXIMITY static ssize_t cts_proximity_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; struct cts_firmware_status *status = (struct cts_firmware_status *) &cts_dev->rtdata.firmware_status; cts_info("Read sysfs '/%s'", attr->attr.name); return scnprintf(buf, PAGE_SIZE, "proximity mode: %d\n", status->proximity); } static ssize_t cts_proximity_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int enable = -1; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); if (count >= 1) { if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { enable = 1; } else if (buf[0] == 'N' || buf[0] == 'n' || buf[0] == '0') { enable = 0; } } if (enable == -1) { cts_err("Invalid arg for proximity mode enable"); return -EINVAL; } cts_lock_device(cts_dev); ret = cts_tcs_set_proximity_mode(cts_dev, enable); cts_unlock_device(cts_dev); if (ret) { cts_err("Set proximity mode failed"); return -EIO; } return count; } static DEVICE_ATTR(proximity_mode, S_IWUSR | S_IRUGO, cts_proximity_mode_show, cts_proximity_mode_store); #endif static ssize_t cts_tcs_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; return snprintf(buf, PAGE_SIZE, "chipone_cmds.bin size:%zu\n", cts_dev->rtdata.tcscmd_len * sizeof(u16)); } static ssize_t cts_tcs_cmd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; const struct firmware *tcscmds; char *update = "update"; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); ret = memcmp(update, buf, count - 1); if (ret) { cts_err("Not update tcs cmd buf, %s", buf); return count; } ret = request_firmware(&tcscmds, "chipone_cmds.bin", dev); if (ret) { cts_err("Could not load firmware from chipone_cmds.bin: %d", ret); return ret; } if (tcscmds->size > PAGE_SIZE) { cts_err("tcscmds length is over one PAGE_SIZE"); return count; } memcpy((u8 *)cts_dev->rtdata.tcscmd, tcscmds->data, tcscmds->size); cts_dev->rtdata.tcscmd_len = tcscmds->size / sizeof(u16); release_firmware(tcscmds); cts_info("update tcscmds successfully"); return count; } static DEVICE_ATTR(tcs_cmd, S_IWUSR | S_IRUSR, cts_tcs_cmd_show, cts_tcs_cmd_store); static u16 cts_find_tcs_cmd(struct cts_device *cts_dev, u8 class_id, u8 cmd_id) { struct cts_tcs_cmd *tcmd; u8 cmd_buf[2]; int i; for (i = 0; i < cts_dev->rtdata.tcscmd_len; i++) { put_unaligned_le16(cts_dev->rtdata.tcscmd[i], cmd_buf); tcmd = (struct cts_tcs_cmd *)cmd_buf; if (class_id == tcmd->class_id && cmd_id == tcmd->cmd_id) { cts_info("Found tcs cmd:0x%04x: { %d, %d, %d, %d, %d}", cts_dev->rtdata.tcscmd[i], tcmd->base_flag, tcmd->class_id, tcmd->cmd_id, tcmd->is_read, tcmd->is_write); return cts_dev->rtdata.tcscmd[i]; } } return 0; } static ssize_t cts_read_tcs_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; int count = 0; u8 *read_buf; int i; int ret; read_buf = kzalloc(cts_dev->rtdata.curr_len, GFP_KERNEL); if (read_buf == NULL) { return snprintf(buf, PAGE_SIZE, "kzalloc read_buf failed\n"); } if (!(cts_dev->rtdata.curr_cmd & BIT(14))) { kfree(read_buf); return snprintf(buf, PAGE_SIZE, "This cmd %04x is not readable!\n", cts_dev->rtdata.curr_cmd); } cts_lock_device(cts_dev); ret = cts_tcs_read(cts_dev, cts_dev->rtdata.curr_cmd, read_buf, cts_dev->rtdata.curr_len); cts_unlock_device(cts_dev); if (ret) { cts_err("read tcscmd: 0x%04x, failed", cts_dev->rtdata.curr_cmd); kfree(read_buf); return ret; } count += snprintf(buf + count, PAGE_SIZE - count, "read_tcs(0x%02x): ", cts_dev->rtdata.curr_cmd); for (i = 0; i < cts_dev->rtdata.curr_len; i++) { count += snprintf(buf + count, PAGE_SIZE - count, "%02x ", read_buf[i]); } count += snprintf(buf + count, PAGE_SIZE - count, "\n"); kfree(read_buf); return count; } static ssize_t cts_read_tcs_cmd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u16 cmd; u8 class_id, cmd_id, read_len; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); cts_parse_arg(buf, count); if (cts_argc != 3) { cts_err("usage: echo class_id cmd_id read_len > read_tcs"); return count; } ret = kstrtou8(cts_argv[0], 0, &class_id); if (ret) { cts_err("kstrtou8 class_id %d failed", class_id); return ret; } ret = kstrtou8(cts_argv[1], 0, &cmd_id); if (ret) { cts_err("kstrtou8 cmd_id %d failed", cmd_id); return ret; } ret = kstrtou8(cts_argv[2], 0, &read_len); if (ret) { cts_err("kstrtou8 read_len %d failed", read_len); return ret; } cmd = cts_find_tcs_cmd(cts_dev, class_id, cmd_id); if (cmd == 0) { cts_err("Not found classid %d, cmdid %d", class_id, cmd_id); return count; } cts_dev->rtdata.curr_cmd = cmd; cts_dev->rtdata.curr_len = read_len; cts_info("tcs cmd:%02x", cmd); return count; } static DEVICE_ATTR(read_tcs_cmd, S_IWUSR | S_IRUSR, cts_read_tcs_cmd_show, cts_read_tcs_cmd_store); static ssize_t cts_write_tcs_cmd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); struct cts_device *cts_dev = &cts_data->cts_dev; u8 class_id, cmd_id; u16 cmd; u8 wbuf[32]; int wlen; int i; int ret; cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); cts_parse_arg(buf, count); if (cts_argc < 3) { cts_err("usage: echo class_id cmd_id x1 x2 .. > write_tcs"); return count; } ret = kstrtou8(cts_argv[0], 0, &class_id); if (ret) { cts_err("kstrtou8 class_id %d failed", ret); return ret; } ret = kstrtou8(cts_argv[1], 0, &cmd_id); if (ret) { cts_err("kstrtou8 cmd_id %d failed", ret); return ret; } wlen = cts_argc - 2; if (wlen > sizeof(wbuf)) { cts_warn("write size is limit %zu bytes.", sizeof(wbuf)); wlen = sizeof(wbuf); } for (i = 0; i < wlen; i++) { ret = kstrtou8(cts_argv[i + 2], 0, wbuf + i); if (ret) { cts_err("kstrtou8 wbuf %d failed", i); return ret; } } cmd = cts_find_tcs_cmd(cts_dev, class_id, cmd_id); if (cmd == 0) { cts_err("Not found classid %d, cmdid %d", class_id, cmd_id); return count; } cts_lock_device(cts_dev); ret = cts_tcs_write(cts_dev, cmd, wbuf, wlen); cts_unlock_device(cts_dev); if (ret) { cts_err("write tcscmd: 0x%04x, failed", cmd); return ret; } return count; } static DEVICE_ATTR(write_tcs_cmd, S_IWUSR, NULL, cts_write_tcs_cmd_store); static struct attribute *cts_dev_misc_atts[] = { &dev_attr_ic_type.attr, &dev_attr_program_mode.attr, #ifdef CFG_CTS_HAS_RESET_PIN &dev_attr_reset_pin.attr, #endif #ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED &dev_attr_irq_pin.attr, #endif &dev_attr_irq_info.attr, &dev_attr_read_tcs_reg.attr, &dev_attr_write_tcs_reg.attr, &dev_attr_read_hw_reg.attr, &dev_attr_write_hw_reg.attr, #ifndef CONFIG_CTS_I2C_HOST &dev_attr_debug_spi.attr, #endif #ifdef CFG_CTS_GESTURE &dev_attr_gesture_en.attr, #endif /* CFG_CTS_GESTURE */ &dev_attr_int_data_types.attr, &dev_attr_int_data_method.attr, &dev_attr_charger_state.attr, &dev_attr_earjack_state.attr, &dev_attr_edge_restain.attr, &dev_attr_game_mode.attr, #ifdef CONFIG_CTS_TP_PROXIMITY &dev_attr_proximity_mode.attr, #endif &dev_attr_tcs_cmd.attr, &dev_attr_read_tcs_cmd.attr, &dev_attr_write_tcs_cmd.attr, NULL }; static const struct attribute_group cts_dev_misc_attr_group = { .name = "misc", .attrs = cts_dev_misc_atts, }; static const struct attribute_group *cts_dev_attr_groups[] = { &cts_dev_firmware_attr_group, #ifdef CFG_CTS_FW_UPDATE_SYS &cts_dev_fw_up_attr_group, #endif &cts_dev_flash_attr_group, &cts_dev_test_attr_group, &cts_dev_misc_attr_group, NULL }; #ifdef CFG_CTS_PLATFORM_SPRD_SUPPORTED static ssize_t cts_suspend_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cts_device *cts_dev = &g_cts_data->cts_dev; return sprintf(buf, "%s\n", cts_dev->rtdata.suspended ? "true" : "false"); } static ssize_t cts_suspend_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { if (count >= 1) { if (buf[0] == '1') { cts_info("ts_suspend_store suspend."); cts_suspend(g_cts_data); } else if (buf[0] == '0') { cts_info("ts_suspend_store cts_resume."); cts_resume(g_cts_data); } else { cts_err("usage: echo 0/1 > ts_suspend"); } } return count; } static DEVICE_ATTR(ts_suspend, S_IWUSR | S_IRUGO, cts_suspend_show, cts_suspend_store); static struct attribute *cts_dev_ts_suspend_atts[] = { &dev_attr_ts_suspend.attr, NULL }; static struct attribute_group ts_suspend_attr_group = { .attrs = cts_dev_ts_suspend_atts, }; static int cts_suspend_sysfs_init(struct device *dev) { int ret = 0; cts_info("sprocomm ts_suspend_store suspend. ret=%d",ret); ret = sysfs_create_group(&dev->kobj, &ts_suspend_attr_group); if (ret) { cts_info("Create sysfs ts_suspend group failed!"); return -ENOMEM; } else { cts_info("Create sysfs ts_suspend group succesfully!"); } ret = sysfs_create_link(NULL, &dev->kobj, "touchscreen"); if (ret < 0) { dev_err(dev, "Failed to create touchscreen link!"); return -ENOMEM; } return ret; } static void cts_suspend_sysfs_deinit(struct device *dev) { sysfs_remove_link(NULL, "touchscreen"); sysfs_remove_group(&dev->kobj, &ts_suspend_attr_group); } #endif #include #include #ifndef CFG_CTS_FOR_GKI /* Attribute: path (RO) */ static ssize_t path_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *data = dev_get_drvdata(dev); ssize_t blen; const char *path; if (!data) { cts_err("Read 'path' with chipone_ts_data NULL"); return (ssize_t) 0; } #ifdef CONFIG_CTS_I2C_HOST path = kobject_get_path(&data->i2c_client->dev.kobj, GFP_KERNEL); #else path = kobject_get_path(&data->spi_client->dev.kobj, GFP_KERNEL); #endif blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); kfree(path); return blen; } #endif /* Attribute: vendor (RO) */ static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { return scnprintf(buf, PAGE_SIZE, "chipone"); } /* Attribute: vendor (RO) */ static ssize_t ic_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { struct chipone_ts_data *ts = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", "Product ID: ", ts->cts_dev.hwdata->name, "Build ID: ", ts->cts_dev.fwdata.version ? ts->cts_dev.fwdata.version : ts->pdata->build_id, "Config ID: ", ts->cts_dev.fwdata.ddi_version ? ts->cts_dev.fwdata.ddi_version : ts->pdata->config_id); } static struct device_attribute touchscreen_attributes[] = { #ifndef CFG_CTS_FOR_GKI __ATTR_RO(path), #endif __ATTR_RO(vendor), __ATTR_RO(ic_ver), __ATTR_NULL }; #define TSDEV_MINOR_BASE 128 #define TSDEV_MINOR_MAX 32 /******************************************************* *Description: * Chipone touchscreen FW function class. file node * initial function. * * return: * Executive outcomes. 0---succeed. -1---failed. *******************************************************/ static int cts_fw_class_init(void *_data, bool create) { struct chipone_ts_data *data = _data; struct device_attribute *attrs = touchscreen_attributes; int i, error = 0; static struct class *touchscreen_class; static struct device *ts_class_dev; dev_t devno; cts_info("%s touchscreen class files", create ? "Add" : "Remove"); if (create) { if (data->cts_dev.hwdata->name != NULL) error = alloc_chrdev_region(&devno, 0, 1, data->cts_dev.hwdata->name); else error = alloc_chrdev_region(&devno, 0, 1, CFG_CTS_CHIP_NAME); if (error) { cts_info("Alloc input devno failed %d", error); return error; } cts_info("Create class 'touchscreen'"); touchscreen_class = class_create(THIS_MODULE, "touchscreen"); if (IS_ERR(touchscreen_class)) { cts_err("Create class 'touchscreen' failed %ld", PTR_ERR(touchscreen_class)); error = PTR_ERR(touchscreen_class); touchscreen_class = NULL; return error; } if (data->cts_dev.hwdata->name != NULL) { ts_class_dev = device_create(touchscreen_class, NULL, devno, data, "%s", data->cts_dev.hwdata->name); cts_info("Create device for IC: %s", data->cts_dev.hwdata->name); } else { ts_class_dev = device_create(touchscreen_class, NULL, devno, data, "%s", CFG_CTS_CHIP_NAME); cts_info("Create device '" CFG_CTS_CHIP_NAME "'"); } if (IS_ERR(ts_class_dev)) { cts_err("Create device '" CFG_CTS_CHIP_NAME "'failed %ld", PTR_ERR(ts_class_dev)); error = PTR_ERR(ts_class_dev); ts_class_dev = NULL; return error; } cts_info("Create attr files"); for (i = 0; attrs[i].attr.name != NULL; ++i) { cts_info(" Create attr file '%s'", attrs[i].attr.name); error = device_create_file(ts_class_dev, &attrs[i]); if (error) { cts_err("Create attr file '%s' failed %d", attrs[i].attr.name, error); break; } } if (error) goto device_destroy; else cts_info("Create /sys/class/touchscreen/ Succeeded"); } else { if (!touchscreen_class || !ts_class_dev) return -ENODEV; for (i = 0; attrs[i].attr.name != NULL; ++i) { cts_info("Remove device file '%s'", attrs[i].attr.name); device_remove_file(ts_class_dev, &attrs[i]); } device_unregister(ts_class_dev); class_unregister(touchscreen_class); } return 0; device_destroy: for (--i; i >= 0; --i) device_remove_file(ts_class_dev, &attrs[i]); device_destroy(touchscreen_class, devno); ts_class_dev = NULL; class_unregister(touchscreen_class); cts_err("Creating touchscreen class failed %d", error); return -ENODEV; } int cts_sysfs_add_device(struct device *dev) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); int ret = 0, i; cts_info("Add device attr groups"); /*Low version kernel NOT support sysfs_create_groups() */ for (i = 0; cts_dev_attr_groups[i]; i++) { ret = sysfs_create_group(&dev->kobj, cts_dev_attr_groups[i]); if (ret) { while (--i >= 0) sysfs_remove_group(&dev->kobj, cts_dev_attr_groups[i]); break; } } if (ret) { cts_err("Add device attr failed %d", ret); return ret; } ret = sysfs_create_link(NULL, &dev->kobj, "chipone-tddi"); if (ret) cts_err("Create sysfs link error:%d", ret); ret = cts_fw_class_init(cts_data, true); if (ret) { cts_err("Create touchscreen class failed. ret=%d", ret); return ret; } #ifdef CFG_CTS_PLATFORM_SPRD_SUPPORTED ret = cts_suspend_sysfs_init(dev); if (ret) { cts_info("create suspend sysfs node failed"); return ret; } #endif return 0; } void cts_sysfs_remove_device(struct device *dev) { struct chipone_ts_data *cts_data = dev_get_drvdata(dev); int i; cts_info("Remove device attr groups"); sysfs_remove_link(NULL, "chipone-tddi"); /*Low version kernel NOT support sysfs_remove_groups() */ for (i = 0; cts_dev_attr_groups[i]; i++) sysfs_remove_group(&dev->kobj, cts_dev_attr_groups[i]); cts_fw_class_init(cts_data, false); #ifdef CFG_CTS_PLATFORM_SPRD_SUPPORTED cts_suspend_sysfs_deinit(dev); #endif } #undef SPLIT_LINE_STR #undef ROW_NUM_FORMAT_STR #undef COL_NUM_FORMAT_STR #undef DATA_FORMAT_STR #endif /* CONFIG_CTS_SYSFS */