/* sysfs: /sys/class/touch/cmcs/cmcs_custom */
ssize_t ist30xx_cmcs_custom_show(struct device *dev, struct device_attribute *attr,
				 char *buf)
{
	int ret;
	int bin_size = 0;
	u8 *bin = NULL;
	const struct firmware *req_bin = NULL;

	ret = request_firmware(&req_bin, IST30XXB_CMCS_NAME, &ts_data->client->dev);
	if (ret)
		return sprintf(buf, "File not found, %s\n", IST30XXB_CMCS_NAME);

	bin = (u8 *)req_bin->data;
	bin_size = (u32)req_bin->size;

	ist30xx_get_cmcs_info(bin, bin_size);

	mutex_lock(&ist30xx_mutex);
	ret = ist30xx_cmcs_test(bin, bin_size);
	mutex_unlock(&ist30xx_mutex);

	release_firmware(req_bin);

	tsp_info("size: %d\n", sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n")));

	return sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n"));
}
/* sysfs: /sys/class/touch/cmcs/cm_key_slope_value */
ssize_t ist30xx_cm_key_slope_value_show(struct device *dev, struct device_attribute *attr,
					char *buf)
{
	int ret;
	CMCS_INFO *cmcs;

	if ((ts_cmcs_bin == NULL) || (ts_cmcs_bin_size == 0))
		return sprintf(buf, "%s", "FFFF");

	ist30xx_get_cmcs_info(ts_cmcs_bin, ts_cmcs_bin_size);

	mutex_lock(&ist30xx_mutex);
	ret = ist30xx_cmcs_test(ts_cmcs_bin, ts_cmcs_bin_size);
	mutex_unlock(&ist30xx_mutex);

	if (ret)
		return sprintf(buf, "%s", "FFFF");

	cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;

	if (cmcs_ready == CMCS_NOT_READY)
		return sprintf(buf, "%s", "FFFF");

	if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
		return sprintf(buf, "%s", "FFFF");

	return print_cm_key_slope_result(ts_cmcs_buf->cm, buf, false);
}
/* sysfs: /sys/class/touch/cmcs/cmcs_binary */
ssize_t ist30xx_cmcs_binary_show(struct device *dev, struct device_attribute *attr,
				 char *buf)
{
	int ret;
	struct ist30xx_data *data = dev_get_drvdata(dev);

	ret = ist30xx_load_cmcs_binary(data);
	if (unlikely(ret)) {
		return sprintf(buf, "Binary loaded failed(%d).\n", data->cmcs_bin_size);
	}

	ist30xx_get_cmcs_info(data, data->cmcs_bin, data->cmcs_bin_size);

	mutex_lock(&data->ist30xx_mutex);
	ret = ist30xx_cmcs_test(data, data->cmcs_bin, data->cmcs_bin_size);
	mutex_unlock(&data->ist30xx_mutex);

	if (likely(data->cmcs_bin != NULL)) {
		kfree(data->cmcs_bin);
		data->cmcs_bin = NULL;
		data->cmcs_bin_size = 0;
	}

	return sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n"));
}
/* sysfs: /sys/class/touch/cmcs/cmcs_binary */
ssize_t ist30xx_cmcs_binary_show(struct device *dev, struct device_attribute *attr,
				 char *buf)
{
	int ret;

	if ((ts_cmcs_bin == NULL) || (ts_cmcs_bin_size == 0))
		return sprintf(buf, "Binary is not correct(%d)\n", ts_cmcs_bin_size);

	ist30xx_get_cmcs_info(ts_cmcs_bin, ts_cmcs_bin_size);

	mutex_lock(&ist30xx_mutex);
	ret = ist30xx_cmcs_test(ts_cmcs_bin, ts_cmcs_bin_size);
	mutex_unlock(&ist30xx_mutex);

	return sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n"));
}
int ist30xx_init_cmcs_sysfs(void)
{
	/* /sys/class/touch/cmcs */
	ist30xx_cmcs_dev = device_create(ist30xx_class, NULL, 0, NULL, "cmcs");

	/* /sys/class/touch/cmcs/... */
	if (unlikely(sysfs_create_group(&ist30xx_cmcs_dev->kobj,
					&cmcs_attr_group)))
		tsp_err("Failed to create sysfs group(%s)!\n", "cmcs");

#if IST30XX_INTERNAL_CMCS_BIN
	ts_cmcs_bin = (u8 *)ist30xxb_cmcs;
	ts_cmcs_bin_size = sizeof(ist30xxb_cmcs);

	ist30xx_get_cmcs_info(ts_cmcs_bin, ts_cmcs_bin_size);
#endif

	return 0;
}
/* sysfs: /sys/class/touch/cmcs/cmcs_sdcard */
ssize_t ist30xx_cmcs_sdcard_show(struct device *dev, struct device_attribute *attr,
				 char *buf)
{
	int ret = 0;
	int bin_size = 0;
	u8 *bin = NULL;
	const u8 *buff = 0;
	mm_segment_t old_fs = { 0 };
	struct file *fp = NULL;
	long fsize = 0, nread = 0;
	char fw_path[MAX_FILE_PATH];

	old_fs = get_fs();
	set_fs(get_ds());

	snprintf(fw_path, MAX_FILE_PATH, "/sdcard/touch/firmware/%s", IST30XXB_CMCS_NAME);
	fp = filp_open(fw_path, O_RDONLY, 0);
	if (IS_ERR(fp)) {
		tsp_info("file %s open error:%d\n", fw_path, (s32)fp);
		ret = -ENOENT;
		goto err_file_open;
	}

	fsize = fp->f_path.dentry->d_inode->i_size;

	buff = kzalloc((size_t)fsize, GFP_KERNEL);
	if (!buff) {
		tsp_info("fail to alloc buffer\n");
		ret = -ENOMEM;
		goto err_alloc;
	}

	nread = vfs_read(fp, (char __user *)buff, fsize, &fp->f_pos);
	if (nread != fsize) {
		tsp_info("mismatch fw size\n");

		goto err_fw_size;
	}

	bin = (u8 *)buff;
	bin_size = (u32)fsize;

	filp_close(fp, current->files);
	set_fs(old_fs);
	tsp_info("firmware is loaded!!\n");

	ist30xx_get_cmcs_info(bin, bin_size);

	mutex_lock(&ist30xx_mutex);
	ret = ist30xx_cmcs_test(bin, bin_size);
	mutex_unlock(&ist30xx_mutex);

	kfree(buff);

	tsp_info("size: %d\n", sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n")));

	return sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n"));

err_fw_size:
	if (buff)
		kfree(buff);
err_alloc:
	if (fp)
		filp_close(fp, NULL);
err_file_open:
	set_fs(old_fs);

	return 0;
}
ssize_t ist30xx_cmcs_test_all_show(struct device *dev, struct device_attribute *attr,
				char *buf)
{
	int ret;
	char* msg = NULL;

	struct ist30xx_data *data = dev_get_drvdata(dev);
	CMCS_INFO *cmcs = (CMCS_INFO *)&data->cmcs->cmcs;

	msg = kzalloc(sizeof(char) * 4096, GFP_KERNEL);
	if (!msg) {
		tsp_err("Memory allocation failed\n");
		return 0;
	}

	/* CMCS Binary */
	ret = ist30xx_load_cmcs_binary(data);
	if (unlikely(ret)) {
		kfree(msg);
		return sprintf(buf, "Binary loaded failed(%d).\n", data->cmcs_bin_size);
	}

	ist30xx_get_cmcs_info(data, data->cmcs_bin, data->cmcs_bin_size);

	mutex_lock(&data->ist30xx_mutex);
	ret = ist30xx_cmcs_test(data, data->cmcs_bin, data->cmcs_bin_size);
	mutex_unlock(&data->ist30xx_mutex);

	if (likely(data->cmcs_bin != NULL)) {
		kfree(data->cmcs_bin);
		data->cmcs_bin = NULL;
		data->cmcs_bin_size = 0;
	}

	if (ret == 0) {
		tsp_debug("CMCS Binary test passed!\n");
	} else {
		kfree(msg);
		return sprintf(buf, "CMCS Binary test failed!\n");
	}

	if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM) && !(cmcs->cmd.mode & FLAG_ENABLE_CS)) {
		kfree(msg);
		return sprintf(buf, "CMCS not enabled!\n");
	}

	/* CM Result */
	memset(msg, 0, sizeof(msg));
	ret = print_cm_result(data, msg);
	if (strncmp(msg, "OK\n", strlen("OK\n")) == 0) {
		tsp_debug("CM result test passed!\n");
	} else {
		goto out;
	}

	/* CM Slope 0 Result */
	memset(msg, 0, sizeof(msg));
	ret = print_cm_slope_result(data, CMCS_FLAG_CM_SLOPE0,
				data->cmcs_buf->slope0, msg);
	if (strncmp(msg, "OK\n", strlen("OK\n")) == 0) {
		tsp_debug("CM Slope 0 test passed!\n");
	} else {
		goto out;
	}

	/* CM Slope 1 Result */
	memset(msg, 0, sizeof(msg));
	ret = print_cm_slope_result(data, CMCS_FLAG_CM_SLOPE1,
				data->cmcs_buf->slope1, msg);
	if (strncmp(msg, "OK\n", strlen("OK\n")) == 0) {
		tsp_debug("CM Slope 1 test passed!\n");
	} else {
		goto out;
	}

	/* CS 0 Result */
	memset(msg, 0, sizeof(msg));
	ret = print_cs_result(data, data->cmcs_buf->cs0, msg, 0);
	if (strncmp(msg, "OK\n", strlen("OK\n")) == 0) {
		tsp_debug("CS 0 test passed!\n");
	} else {
		goto out;
	}

	/* CS 1 Result */
	memset(msg, 0, sizeof(msg));
	ret = print_cs_result(data, data->cmcs_buf->cs1, msg, 1);
	if (strncmp(msg, "OK\n", strlen("OK\n")) == 0) {
		tsp_debug("CS 1 test passed!\n");
	} else {
		goto out;
	}

out:
	ret = sprintf(buf, "%s", msg);
	kfree(msg);
	return ret;
}