1233 lines
35 KiB
C
1233 lines
35 KiB
C
|
/*
|
||
|
* sec_cmd.c - samsung input command driver
|
||
|
*
|
||
|
* Copyright (C) 2014 Samsung Electronics
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "sec_cmd.h"
|
||
|
#include "sec_input.h"
|
||
|
#include "sec_tsp_log.h"
|
||
|
|
||
|
struct class *tsp_sec_class;
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_SEC_KUNIT)
|
||
|
__visible_for_testing struct sec_cmd_data *kunit_sec;
|
||
|
EXPORT_SYMBOL(kunit_sec);
|
||
|
#else
|
||
|
#define __visible_for_testing static
|
||
|
#endif
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
|
||
|
static struct sec_cmd_data *main_sec;
|
||
|
static struct sec_cmd_data *sub_sec;
|
||
|
#endif
|
||
|
|
||
|
#ifdef USE_SEC_CMD_QUEUE
|
||
|
static void sec_cmd_store_function(struct sec_cmd_data *data);
|
||
|
void sec_cmd_execution(struct sec_cmd_data *data, bool lock)
|
||
|
{
|
||
|
if (lock)
|
||
|
mutex_lock(&data->fs_lock);
|
||
|
|
||
|
/* check lock */
|
||
|
mutex_lock(&data->cmd_lock);
|
||
|
data->cmd_is_running = true;
|
||
|
mutex_unlock(&data->cmd_lock);
|
||
|
|
||
|
data->cmd_state = SEC_CMD_STATUS_RUNNING;
|
||
|
sec_cmd_store_function(data);
|
||
|
|
||
|
if (lock)
|
||
|
mutex_unlock(&data->fs_lock);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void sec_cmd_set_cmd_exit(struct sec_cmd_data *data)
|
||
|
{
|
||
|
#ifdef USE_SEC_CMD_QUEUE
|
||
|
mutex_lock(&data->fifo_lock);
|
||
|
if (kfifo_len(&data->cmd_queue)) {
|
||
|
pr_info("%s: %s %s: do next cmd, left cmd[%d]\n", dev_name(data->fac_dev), SECLOG, __func__,
|
||
|
(int)(kfifo_len(&data->cmd_queue) / sizeof(struct command)));
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
|
||
|
sec_cmd_execution(data, false);
|
||
|
#else
|
||
|
schedule_work(&data->cmd_work.work);
|
||
|
#endif
|
||
|
} else {
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
|
||
|
mutex_lock(&data->cmd_lock);
|
||
|
data->cmd_is_running = false;
|
||
|
mutex_unlock(&data->cmd_lock);
|
||
|
}
|
||
|
if (data->wait_cmd_result_done)
|
||
|
complete_all(&data->cmd_result_done);
|
||
|
#else
|
||
|
mutex_lock(&data->cmd_lock);
|
||
|
data->cmd_is_running = false;
|
||
|
mutex_unlock(&data->cmd_lock);
|
||
|
#endif
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_set_cmd_exit);
|
||
|
|
||
|
#ifdef USE_SEC_CMD_QUEUE
|
||
|
static void cmd_exit_work(struct work_struct *work)
|
||
|
{
|
||
|
struct sec_cmd_data *data = container_of(work, struct sec_cmd_data, cmd_work.work);
|
||
|
|
||
|
sec_cmd_execution(data, true);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void sec_cmd_set_default_result(struct sec_cmd_data *data)
|
||
|
{
|
||
|
char *delim = ":";
|
||
|
memset(data->cmd_result, 0x00, SEC_CMD_RESULT_STR_LEN_EXPAND);
|
||
|
memcpy(data->cmd_result, data->cmd, SEC_CMD_STR_LEN);
|
||
|
strlcat(data->cmd_result, delim, SEC_CMD_RESULT_STR_LEN_EXPAND);
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_set_default_result);
|
||
|
|
||
|
void sec_cmd_set_cmd_result_all(struct sec_cmd_data *data, char *buff, int len, char *item)
|
||
|
{
|
||
|
char *delim1 = " ";
|
||
|
char *delim2 = ":";
|
||
|
int cmd_result_len;
|
||
|
|
||
|
cmd_result_len = (int)strlen(data->cmd_result_all) + len + 2 + (int)strlen(item);
|
||
|
|
||
|
if (cmd_result_len >= SEC_CMD_RESULT_STR_LEN) {
|
||
|
pr_err("%s: %s %s: cmd length is over (%d)!!", dev_name(data->fac_dev), SECLOG, __func__, cmd_result_len);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data->item_count++;
|
||
|
strlcat(data->cmd_result_all, delim1, sizeof(data->cmd_result_all));
|
||
|
strlcat(data->cmd_result_all, item, sizeof(data->cmd_result_all));
|
||
|
strlcat(data->cmd_result_all, delim2, sizeof(data->cmd_result_all));
|
||
|
strlcat(data->cmd_result_all, buff, sizeof(data->cmd_result_all));
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_set_cmd_result_all);
|
||
|
|
||
|
void sec_cmd_set_cmd_result(struct sec_cmd_data *data, char *buff, int len)
|
||
|
{
|
||
|
if (strlen(buff) >= (unsigned int)SEC_CMD_RESULT_STR_LEN_EXPAND) {
|
||
|
pr_err("%s %s: cmd length is over (%d)!!", SECLOG, __func__, (int)strlen(buff));
|
||
|
strlcat(data->cmd_result, "NG", SEC_CMD_RESULT_STR_LEN_EXPAND);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data->cmd_result_expand = (int)strlen(buff) / SEC_CMD_RESULT_STR_LEN;
|
||
|
data->cmd_result_expand_count = 0;
|
||
|
|
||
|
strlcat(data->cmd_result, buff, SEC_CMD_RESULT_STR_LEN_EXPAND);
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_set_cmd_result);
|
||
|
|
||
|
#ifndef USE_SEC_CMD_QUEUE
|
||
|
__visible_for_testing ssize_t sec_cmd_store(struct device *dev,
|
||
|
struct device_attribute *devattr, const char *buf, size_t count)
|
||
|
{
|
||
|
struct sec_cmd_data *data = dev_get_drvdata(dev);
|
||
|
char *cur, *start, *end;
|
||
|
char buff[SEC_CMD_STR_LEN] = { 0 };
|
||
|
int len, i;
|
||
|
struct sec_cmd *sec_cmd_ptr = NULL;
|
||
|
char delim = ',';
|
||
|
bool cmd_found = false;
|
||
|
int param_cnt = 0;
|
||
|
|
||
|
if (!data) {
|
||
|
pr_err("%s %s: No platform data found\n", SECLOG, __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (strnlen(buf, SEC_CMD_STR_LEN) >= SEC_CMD_STR_LEN) {
|
||
|
pr_err("%s: %s %s: cmd length(strlen(buf)) is over (%d,%s)!!\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__, (int)strlen(buf), buf);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (count >= (unsigned int)SEC_CMD_STR_LEN) {
|
||
|
pr_err("%s: %s %s: cmd length(count) is over (%d,%s)!!\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__, (unsigned int)count, buf);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (data->cmd_is_running == true) {
|
||
|
pr_err("%s: %s %s: other cmd is running.\n", dev_name(data->fac_dev), SECLOG, __func__);
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
/* check lock */
|
||
|
mutex_lock(&data->cmd_lock);
|
||
|
data->cmd_is_running = true;
|
||
|
mutex_unlock(&data->cmd_lock);
|
||
|
|
||
|
data->cmd_state = SEC_CMD_STATUS_RUNNING;
|
||
|
for (i = 0; i < ARRAY_SIZE(data->cmd_param); i++)
|
||
|
data->cmd_param[i] = 0;
|
||
|
|
||
|
len = (int)count;
|
||
|
if (*(buf + len - 1) == '\n')
|
||
|
len--;
|
||
|
|
||
|
memset(data->cmd, 0x00, ARRAY_SIZE(data->cmd));
|
||
|
memcpy(data->cmd, buf, len);
|
||
|
|
||
|
cur = strchr(buf, (int)delim);
|
||
|
if (cur)
|
||
|
memcpy(buff, buf, cur - buf);
|
||
|
else
|
||
|
memcpy(buff, buf, len);
|
||
|
|
||
|
pr_debug("%s: %s %s: COMMAND = %s\n", dev_name(data->fac_dev), SECLOG, __func__, buff);
|
||
|
|
||
|
/* find command */
|
||
|
list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) {
|
||
|
if (!strncmp(buff, sec_cmd_ptr->cmd_name, SEC_CMD_STR_LEN)) {
|
||
|
cmd_found = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
check_not_support_cmd:
|
||
|
/* set not_support_cmd */
|
||
|
if (!cmd_found) {
|
||
|
list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) {
|
||
|
if (!strncmp("not_support_cmd", sec_cmd_ptr->cmd_name,
|
||
|
SEC_CMD_STR_LEN))
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* parsing parameters */
|
||
|
if (cur && cmd_found) {
|
||
|
cur++;
|
||
|
start = cur;
|
||
|
memset(buff, 0x00, ARRAY_SIZE(buff));
|
||
|
|
||
|
do {
|
||
|
if (*cur == delim || cur - buf == len) {
|
||
|
end = cur;
|
||
|
memcpy(buff, start, end - start);
|
||
|
*(buff + strnlen(buff, ARRAY_SIZE(buff))) = '\0';
|
||
|
if (kstrtoint(buff, 10, data->cmd_param + param_cnt) < 0) {
|
||
|
pr_err("%s: %s %s: error to parse parameter\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__);
|
||
|
cmd_found = false;
|
||
|
goto check_not_support_cmd;
|
||
|
}
|
||
|
start = cur + 1;
|
||
|
memset(buff, 0x00, ARRAY_SIZE(buff));
|
||
|
param_cnt++;
|
||
|
}
|
||
|
cur++;
|
||
|
} while ((cur - buf <= len) && (param_cnt < SEC_CMD_PARAM_NUM));
|
||
|
}
|
||
|
|
||
|
if (cmd_found) {
|
||
|
pr_info("%s: %s %s: cmd = %s", dev_name(data->fac_dev), SECLOG, __func__, sec_cmd_ptr->cmd_name);
|
||
|
for (i = 0; i < param_cnt; i++) {
|
||
|
if (i == 0)
|
||
|
pr_cont(" param =");
|
||
|
pr_cont(" %d", data->cmd_param[i]);
|
||
|
}
|
||
|
pr_cont("\n");
|
||
|
} else {
|
||
|
pr_info("%s: %s %s: cmd = %s(%s)\n", dev_name(data->fac_dev), SECLOG, __func__, buff, sec_cmd_ptr->cmd_name);
|
||
|
}
|
||
|
|
||
|
sec_cmd_ptr->cmd_func(data);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
#if IS_ENABLED(CONFIG_SEC_KUNIT)
|
||
|
EXPORT_SYMBOL_KUNIT(sec_cmd_store);
|
||
|
#endif
|
||
|
|
||
|
#else /* defined USE_SEC_CMD_QUEUE */
|
||
|
static void sec_cmd_store_function(struct sec_cmd_data *data)
|
||
|
{
|
||
|
char *cur, *start, *end;
|
||
|
char buff[SEC_CMD_STR_LEN] = { 0 };
|
||
|
int len, i;
|
||
|
struct sec_cmd *sec_cmd_ptr = NULL;
|
||
|
char delim = ',';
|
||
|
bool cmd_found = false;
|
||
|
int param_cnt = 0;
|
||
|
int ret;
|
||
|
const char *buf;
|
||
|
size_t count;
|
||
|
struct command cmd = {{0}};
|
||
|
|
||
|
if (!data) {
|
||
|
pr_err("%s %s: No platform data found\n", SECLOG, __func__);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&data->fifo_lock);
|
||
|
if (kfifo_len(&data->cmd_queue)) {
|
||
|
ret = kfifo_out(&data->cmd_queue, &cmd, sizeof(struct command));
|
||
|
if (!ret) {
|
||
|
pr_err("%s: %s %s: kfifo_out failed, it seems empty, ret=%d\n", dev_name(data->fac_dev), SECLOG, __func__, ret);
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
pr_err("%s: %s %s: left cmd is nothing\n", dev_name(data->fac_dev), SECLOG, __func__);
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
mutex_lock(&data->cmd_lock);
|
||
|
data->cmd_is_running = false;
|
||
|
mutex_unlock(&data->cmd_lock);
|
||
|
return;
|
||
|
}
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
|
||
|
buf = cmd.cmd;
|
||
|
count = strlen(buf);
|
||
|
|
||
|
for (i = 0; i < (int)ARRAY_SIZE(data->cmd_param); i++)
|
||
|
data->cmd_param[i] = 0;
|
||
|
|
||
|
len = (int)count;
|
||
|
if (*(buf + len - 1) == '\n')
|
||
|
len--;
|
||
|
|
||
|
memset(data->cmd, 0x00, ARRAY_SIZE(data->cmd));
|
||
|
memcpy(data->cmd, buf, len);
|
||
|
|
||
|
cur = strchr(buf, (int)delim);
|
||
|
if (cur)
|
||
|
memcpy(buff, buf, cur - buf);
|
||
|
else
|
||
|
memcpy(buff, buf, len);
|
||
|
|
||
|
pr_debug("%s: %s %s: COMMAND : %s\n", dev_name(data->fac_dev), SECLOG, __func__, buff);
|
||
|
|
||
|
/* find command */
|
||
|
list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) {
|
||
|
if (!strncmp(buff, sec_cmd_ptr->cmd_name, SEC_CMD_STR_LEN)) {
|
||
|
cmd_found = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
check_not_support_cmd:
|
||
|
/* set not_support_cmd */
|
||
|
if (!cmd_found) {
|
||
|
list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) {
|
||
|
if (!strncmp("not_support_cmd", sec_cmd_ptr->cmd_name,
|
||
|
SEC_CMD_STR_LEN))
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* parsing parameters */
|
||
|
if (cur && cmd_found) {
|
||
|
cur++;
|
||
|
start = cur;
|
||
|
memset(buff, 0x00, ARRAY_SIZE(buff));
|
||
|
|
||
|
do {
|
||
|
if (*cur == delim || cur - buf == len) {
|
||
|
end = cur;
|
||
|
memcpy(buff, start, end - start);
|
||
|
*(buff + strnlen(buff, ARRAY_SIZE(buff))) = '\0';
|
||
|
if (kstrtoint(buff, 10, data->cmd_param + param_cnt) < 0) {
|
||
|
pr_err("%s: %s %s: error to parse parameter\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__);
|
||
|
cmd_found = false;
|
||
|
goto check_not_support_cmd;
|
||
|
}
|
||
|
start = cur + 1;
|
||
|
memset(buff, 0x00, ARRAY_SIZE(buff));
|
||
|
param_cnt++;
|
||
|
}
|
||
|
cur++;
|
||
|
} while ((cur - buf <= len) && (param_cnt < SEC_CMD_PARAM_NUM));
|
||
|
}
|
||
|
|
||
|
if (cmd_found) {
|
||
|
pr_info("%s: %s %s: cmd = %s", dev_name(data->fac_dev), SECLOG, __func__, sec_cmd_ptr->cmd_name);
|
||
|
for (i = 0; i < param_cnt; i++) {
|
||
|
if (i == 0)
|
||
|
pr_cont(" param =");
|
||
|
pr_cont(" %d", data->cmd_param[i]);
|
||
|
}
|
||
|
pr_cont("\n");
|
||
|
} else {
|
||
|
pr_info("%s: %s %s: cmd = %s(%s)\n", dev_name(data->fac_dev), SECLOG, __func__, buff, sec_cmd_ptr->cmd_name);
|
||
|
}
|
||
|
|
||
|
sec_cmd_ptr->cmd_func(data);
|
||
|
|
||
|
if (cmd_found && sec_cmd_ptr->cmd_log) {
|
||
|
char tbuf[32];
|
||
|
unsigned long long t;
|
||
|
unsigned long nanosec_rem;
|
||
|
|
||
|
memset(tbuf, 0x00, sizeof(tbuf));
|
||
|
t = local_clock();
|
||
|
nanosec_rem = do_div(t, 1000000000);
|
||
|
snprintf(tbuf, sizeof(tbuf), "[r:%lu.%06lu]",
|
||
|
(unsigned long)t,
|
||
|
nanosec_rem / 1000);
|
||
|
#if IS_ENABLED(CONFIG_SEC_DEBUG_TSP_LOG)
|
||
|
sec_debug_tsp_command_history(tbuf);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
__visible_for_testing ssize_t sec_cmd_store(struct device *dev, struct device_attribute *devattr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct sec_cmd_data *data = dev_get_drvdata(dev);
|
||
|
struct command cmd = {{0}};
|
||
|
struct sec_cmd *sec_cmd_ptr = NULL;
|
||
|
int queue_size;
|
||
|
|
||
|
if (!data) {
|
||
|
pr_err("%s %s: No platform data found\n", SECLOG, __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (strnlen(buf, SEC_CMD_STR_LEN) >= SEC_CMD_STR_LEN) {
|
||
|
pr_err("%s: %s %s: cmd length(strlen(buf)) is over (%d,%s)!!\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__, (int)strlen(buf), buf);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (count >= (unsigned int)SEC_CMD_STR_LEN) {
|
||
|
pr_err("%s: %s %s: cmd length(count) is over (%d,%s)!!\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__, (unsigned int)count, buf);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (strnlen(buf, SEC_CMD_STR_LEN) == 0) {
|
||
|
pr_err("%s: %s %s: cmd length is zero (%d,%s) count(%ld)!!\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__, (int)strlen(buf), buf, count);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
strncpy(cmd.cmd, buf, count);
|
||
|
|
||
|
if (data->wait_cmd_result_done) {
|
||
|
int ret;
|
||
|
|
||
|
mutex_lock(&data->wait_lock);
|
||
|
if (!data->cmd_result_done.done)
|
||
|
pr_info("%s: %s %s: %s - waiting prev cmd...\n", dev_name(data->fac_dev), SECLOG, __func__, cmd.cmd);
|
||
|
ret = wait_for_completion_interruptible_timeout(&data->cmd_result_done, msecs_to_jiffies(2000));
|
||
|
if (ret <= 0)
|
||
|
pr_err("%s: %s %s: completion %d\n", dev_name(data->fac_dev), SECLOG, __func__, ret);
|
||
|
|
||
|
reinit_completion(&data->cmd_result_done);
|
||
|
mutex_unlock(&data->wait_lock);
|
||
|
}
|
||
|
|
||
|
list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) {
|
||
|
if (!strncmp(cmd.cmd, sec_cmd_ptr->cmd_name, strlen(sec_cmd_ptr->cmd_name))) {
|
||
|
if (sec_cmd_ptr->cmd_log) {
|
||
|
char task_info[40];
|
||
|
char tbuf[32];
|
||
|
unsigned long long t;
|
||
|
unsigned long nanosec_rem;
|
||
|
|
||
|
memset(tbuf, 0x00, sizeof(tbuf));
|
||
|
t = local_clock();
|
||
|
nanosec_rem = do_div(t, 1000000000);
|
||
|
snprintf(tbuf, sizeof(tbuf), "[q:%lu.%06lu]",
|
||
|
(unsigned long)t,
|
||
|
nanosec_rem / 1000);
|
||
|
|
||
|
snprintf(task_info, 40, "\n[%d:%s:%s]", current->pid, current->comm, dev_name(data->fac_dev));
|
||
|
#if IS_ENABLED(CONFIG_SEC_DEBUG_TSP_LOG)
|
||
|
sec_debug_tsp_command_history(task_info);
|
||
|
sec_debug_tsp_command_history(cmd.cmd);
|
||
|
sec_debug_tsp_command_history(tbuf);
|
||
|
#endif
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mutex_lock(&data->fifo_lock);
|
||
|
queue_size = (kfifo_len(&data->cmd_queue) / sizeof(struct command));
|
||
|
|
||
|
if (kfifo_avail(&data->cmd_queue) && (queue_size < SEC_CMD_MAX_QUEUE)) {
|
||
|
kfifo_in(&data->cmd_queue, &cmd, sizeof(struct command));
|
||
|
pr_info("%s: %s %s: push cmd: %s\n", dev_name(data->fac_dev), SECLOG, __func__, cmd.cmd);
|
||
|
} else {
|
||
|
pr_err("%s: %s %s: cmd_queue is full!!\n", dev_name(data->fac_dev), SECLOG, __func__);
|
||
|
|
||
|
kfifo_reset(&data->cmd_queue);
|
||
|
pr_err("%s: %s %s: cmd_queue is reset!!\n", dev_name(data->fac_dev), SECLOG, __func__);
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
|
||
|
mutex_lock(&data->cmd_lock);
|
||
|
data->cmd_is_running = false;
|
||
|
mutex_unlock(&data->cmd_lock);
|
||
|
|
||
|
if (data->wait_cmd_result_done)
|
||
|
complete_all(&data->cmd_result_done);
|
||
|
|
||
|
return -ENOSPC;
|
||
|
}
|
||
|
|
||
|
if (data->cmd_is_running == true) {
|
||
|
pr_err("%s: %s %s: other cmd is running. Wait until previous cmd is done[%d]\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__, (int)(kfifo_len(&data->cmd_queue) / sizeof(struct command)));
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
return count;
|
||
|
}
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
|
||
|
sec_cmd_execution(data, true);
|
||
|
return count;
|
||
|
}
|
||
|
#if IS_ENABLED(CONFIG_SEC_KUNIT)
|
||
|
EXPORT_SYMBOL_KUNIT(sec_cmd_store);
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
__visible_for_testing ssize_t sec_cmd_show_status(struct device *dev,
|
||
|
struct device_attribute *devattr, char *buf)
|
||
|
{
|
||
|
struct sec_cmd_data *data = dev_get_drvdata(dev);
|
||
|
char buff[16] = { 0 };
|
||
|
|
||
|
if (!data) {
|
||
|
pr_err("%s %s: No platform data found\n", SECLOG, __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (data->cmd_state == SEC_CMD_STATUS_WAITING)
|
||
|
snprintf(buff, sizeof(buff), "WAITING");
|
||
|
|
||
|
else if (data->cmd_state == SEC_CMD_STATUS_RUNNING)
|
||
|
snprintf(buff, sizeof(buff), "RUNNING");
|
||
|
|
||
|
else if (data->cmd_state == SEC_CMD_STATUS_OK)
|
||
|
snprintf(buff, sizeof(buff), "OK");
|
||
|
|
||
|
else if (data->cmd_state == SEC_CMD_STATUS_FAIL)
|
||
|
snprintf(buff, sizeof(buff), "FAIL");
|
||
|
|
||
|
else if (data->cmd_state == SEC_CMD_STATUS_EXPAND)
|
||
|
snprintf(buff, sizeof(buff), "EXPAND");
|
||
|
|
||
|
else if (data->cmd_state == SEC_CMD_STATUS_NOT_APPLICABLE)
|
||
|
snprintf(buff, sizeof(buff), "NOT_APPLICABLE");
|
||
|
|
||
|
pr_debug("%s: %s %s: %d, %s\n", dev_name(data->fac_dev), SECLOG, __func__, data->cmd_state, buff);
|
||
|
|
||
|
return snprintf(buf, sizeof(buff), "%s\n", buff);
|
||
|
}
|
||
|
#if IS_ENABLED(CONFIG_SEC_KUNIT)
|
||
|
EXPORT_SYMBOL_KUNIT(sec_cmd_show_status);
|
||
|
#endif
|
||
|
|
||
|
static ssize_t sec_cmd_show_status_all(struct device *dev,
|
||
|
struct device_attribute *devattr, char *buf)
|
||
|
{
|
||
|
struct sec_cmd_data *data = dev_get_drvdata(dev);
|
||
|
char buff[16] = { 0 };
|
||
|
|
||
|
if (!data) {
|
||
|
pr_err("%s %s: No platform data found\n", SECLOG, __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (data->cmd_all_factory_state == SEC_CMD_STATUS_WAITING)
|
||
|
snprintf(buff, sizeof(buff), "WAITING");
|
||
|
|
||
|
else if (data->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING)
|
||
|
snprintf(buff, sizeof(buff), "RUNNING");
|
||
|
|
||
|
else if (data->cmd_all_factory_state == SEC_CMD_STATUS_OK)
|
||
|
snprintf(buff, sizeof(buff), "OK");
|
||
|
|
||
|
else if (data->cmd_all_factory_state == SEC_CMD_STATUS_FAIL)
|
||
|
snprintf(buff, sizeof(buff), "FAIL");
|
||
|
|
||
|
else if (data->cmd_state == SEC_CMD_STATUS_EXPAND)
|
||
|
snprintf(buff, sizeof(buff), "EXPAND");
|
||
|
|
||
|
else if (data->cmd_all_factory_state == SEC_CMD_STATUS_NOT_APPLICABLE)
|
||
|
snprintf(buff, sizeof(buff), "NOT_APPLICABLE");
|
||
|
|
||
|
pr_debug("%s: %s %s: %d, %s\n", dev_name(data->fac_dev), SECLOG, __func__, data->cmd_all_factory_state, buff);
|
||
|
|
||
|
return snprintf(buf, sizeof(buff), "%s\n", buff);
|
||
|
}
|
||
|
|
||
|
__visible_for_testing ssize_t sec_cmd_show_result(struct device *dev,
|
||
|
struct device_attribute *devattr, char *buf)
|
||
|
{
|
||
|
struct sec_cmd_data *data = dev_get_drvdata(dev);
|
||
|
int size;
|
||
|
|
||
|
if (!data) {
|
||
|
pr_err("%s %s: No platform data found\n", SECLOG, __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
size = snprintf(buf, SEC_CMD_RESULT_STR_LEN, "%s\n",
|
||
|
data->cmd_result + (SEC_CMD_RESULT_STR_LEN - 1) * data->cmd_result_expand_count);
|
||
|
|
||
|
if (data->cmd_result_expand_count != data->cmd_result_expand) {
|
||
|
data->cmd_state = SEC_CMD_STATUS_EXPAND;
|
||
|
data->cmd_result_expand_count++;
|
||
|
} else {
|
||
|
data->cmd_state = SEC_CMD_STATUS_WAITING;
|
||
|
}
|
||
|
|
||
|
pr_info("%s: %s %s: %s\n", dev_name(data->fac_dev), SECLOG, __func__, buf);
|
||
|
|
||
|
sec_cmd_set_cmd_exit(data);
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
#if IS_ENABLED(CONFIG_SEC_KUNIT)
|
||
|
EXPORT_SYMBOL_KUNIT(sec_cmd_show_result);
|
||
|
#endif
|
||
|
|
||
|
static ssize_t sec_cmd_show_result_all(struct device *dev,
|
||
|
struct device_attribute *devattr, char *buf)
|
||
|
{
|
||
|
struct sec_cmd_data *data = dev_get_drvdata(dev);
|
||
|
int size;
|
||
|
|
||
|
if (!data) {
|
||
|
pr_err("%s %s: No platform data found\n", SECLOG, __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
data->cmd_state = SEC_CMD_STATUS_WAITING;
|
||
|
pr_info("%s: %s %s: %d, %s\n", dev_name(data->fac_dev), SECLOG, __func__, data->item_count, data->cmd_result_all);
|
||
|
size = snprintf(buf, SEC_CMD_RESULT_STR_LEN, "%d%s\n", data->item_count, data->cmd_result_all);
|
||
|
|
||
|
sec_cmd_set_cmd_exit(data);
|
||
|
|
||
|
data->item_count = 0;
|
||
|
memset(data->cmd_result_all, 0x00, SEC_CMD_RESULT_STR_LEN);
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
static ssize_t sec_cmd_list_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct sec_cmd_data *data = dev_get_drvdata(dev);
|
||
|
struct sec_cmd *sec_cmd_ptr = NULL;
|
||
|
char *buffer;
|
||
|
char buffer_name[SEC_CMD_STR_LEN];
|
||
|
int ret = 0;
|
||
|
|
||
|
buffer = kzalloc(data->cmd_buffer_size + 30, GFP_KERNEL);
|
||
|
if (!buffer)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
snprintf(buffer, 30, "++factory command list++\n");
|
||
|
|
||
|
list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) {
|
||
|
if (strncmp(sec_cmd_ptr->cmd_name, "not_support_cmd", 15)) {
|
||
|
snprintf(buffer_name, SEC_CMD_STR_LEN, "%s\n", sec_cmd_ptr->cmd_name);
|
||
|
strlcat(buffer, buffer_name, data->cmd_buffer_size + 30);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ret = snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buffer);
|
||
|
kfree(buffer);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static DEVICE_ATTR(cmd, 0220, NULL, sec_cmd_store);
|
||
|
static DEVICE_ATTR(cmd_status, 0444, sec_cmd_show_status, NULL);
|
||
|
static DEVICE_ATTR(cmd_status_all, 0444, sec_cmd_show_status_all, NULL);
|
||
|
static DEVICE_ATTR(cmd_result, 0444, sec_cmd_show_result, NULL);
|
||
|
static DEVICE_ATTR(cmd_result_all, 0444, sec_cmd_show_result_all, NULL);
|
||
|
static DEVICE_ATTR(cmd_list, 0444, sec_cmd_list_show, NULL);
|
||
|
|
||
|
static struct attribute *sec_fac_attrs[] = {
|
||
|
&dev_attr_cmd.attr,
|
||
|
&dev_attr_cmd_status.attr,
|
||
|
&dev_attr_cmd_status_all.attr,
|
||
|
&dev_attr_cmd_result.attr,
|
||
|
&dev_attr_cmd_result_all.attr,
|
||
|
&dev_attr_cmd_list.attr,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
static struct attribute_group sec_fac_attr_group = {
|
||
|
.attrs = sec_fac_attrs,
|
||
|
};
|
||
|
|
||
|
|
||
|
int sec_cmd_init(struct sec_cmd_data *data, struct sec_cmd *cmds,
|
||
|
int len, int devt)
|
||
|
{
|
||
|
const char *dev_name;
|
||
|
int ret, i;
|
||
|
|
||
|
INIT_LIST_HEAD(&data->cmd_list_head);
|
||
|
|
||
|
data->cmd_buffer_size = 0;
|
||
|
for (i = 0; i < len; i++) {
|
||
|
list_add_tail(&cmds[i].list, &data->cmd_list_head);
|
||
|
if (cmds[i].cmd_name)
|
||
|
data->cmd_buffer_size += strlen(cmds[i].cmd_name) + 1;
|
||
|
}
|
||
|
|
||
|
mutex_init(&data->cmd_lock);
|
||
|
mutex_init(&data->fs_lock);
|
||
|
|
||
|
mutex_lock(&data->cmd_lock);
|
||
|
data->cmd_is_running = false;
|
||
|
mutex_unlock(&data->cmd_lock);
|
||
|
|
||
|
data->cmd_result = kzalloc(SEC_CMD_RESULT_STR_LEN_EXPAND, GFP_KERNEL);
|
||
|
if (!data->cmd_result)
|
||
|
goto err_alloc_cmd_result;
|
||
|
|
||
|
#ifdef USE_SEC_CMD_QUEUE
|
||
|
if (kfifo_alloc(&data->cmd_queue,
|
||
|
SEC_CMD_MAX_QUEUE * sizeof(struct command), GFP_KERNEL)) {
|
||
|
pr_err("%s %s: failed to alloc queue for cmd\n", SECLOG, __func__);
|
||
|
goto err_alloc_queue;
|
||
|
}
|
||
|
mutex_init(&data->fifo_lock);
|
||
|
mutex_init(&data->wait_lock);
|
||
|
init_completion(&data->cmd_result_done);
|
||
|
complete_all(&data->cmd_result_done);
|
||
|
|
||
|
INIT_DELAYED_WORK(&data->cmd_work, cmd_exit_work);
|
||
|
#endif
|
||
|
|
||
|
switch (devt) {
|
||
|
case SEC_CLASS_DEVT_TSP:
|
||
|
dev_name = SEC_CLASS_DEV_NAME_TSP;
|
||
|
break;
|
||
|
#if IS_ENABLED(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
|
||
|
case SEC_CLASS_DEVT_TSP1:
|
||
|
dev_name = SEC_CLASS_DEV_NAME_TSP1;
|
||
|
break;
|
||
|
case SEC_CLASS_DEVT_TSP2:
|
||
|
dev_name = SEC_CLASS_DEV_NAME_TSP2;
|
||
|
break;
|
||
|
#endif
|
||
|
case SEC_CLASS_DEVT_TKEY:
|
||
|
dev_name = SEC_CLASS_DEV_NAME_TKEY;
|
||
|
break;
|
||
|
case SEC_CLASS_DEVT_WACOM:
|
||
|
dev_name = SEC_CLASS_DEV_NAME_WACOM;
|
||
|
break;
|
||
|
case SEC_CLASS_DEVT_SIDEKEY:
|
||
|
dev_name = SEC_CLASS_DEV_NAME_SIDEKEY;
|
||
|
break;
|
||
|
default:
|
||
|
pr_err("%s %s: not defined devt=%d\n", SECLOG, __func__, devt);
|
||
|
goto err_get_dev_name;
|
||
|
}
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_DRV_SAMSUNG)
|
||
|
data->fac_dev = sec_device_create(data, dev_name);
|
||
|
#else
|
||
|
tsp_sec_class = class_create(THIS_MODULE, "tsp_sec");
|
||
|
if (unlikely(IS_ERR(tsp_sec_class))) {
|
||
|
pr_err("%s %s: Failed to create class(sec) %ld\n", SECLOG, __func__, PTR_ERR(tsp_sec_class));
|
||
|
return PTR_ERR(tsp_sec_class);
|
||
|
}
|
||
|
data->fac_dev = device_create(tsp_sec_class, NULL, devt, data, "%s", dev_name);
|
||
|
#endif
|
||
|
|
||
|
if (IS_ERR(data->fac_dev)) {
|
||
|
pr_err("%s %s: failed to create device for the sysfs\n", SECLOG, __func__);
|
||
|
goto err_sysfs_device;
|
||
|
}
|
||
|
|
||
|
dev_set_drvdata(data->fac_dev, data);
|
||
|
|
||
|
ret = sysfs_create_group(&data->fac_dev->kobj, &sec_fac_attr_group);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s %s: failed to create sysfs group\n", SECLOG, __func__);
|
||
|
goto err_sysfs_group;
|
||
|
}
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
|
||
|
switch (devt) {
|
||
|
case SEC_CLASS_DEVT_TSP1:
|
||
|
case SEC_CLASS_DEVT_TSP2:
|
||
|
sec_cmd_virtual_tsp_register(data);
|
||
|
break;
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
pr_info("%s: %s %s: done\n", dev_name, SECLOG, __func__);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_sysfs_group:
|
||
|
#if IS_ENABLED(CONFIG_DRV_SAMSUNG)
|
||
|
sec_device_destroy(data->fac_dev->devt);
|
||
|
#else
|
||
|
device_destroy(tsp_sec_class, devt);
|
||
|
#endif
|
||
|
err_sysfs_device:
|
||
|
err_get_dev_name:
|
||
|
#ifdef USE_SEC_CMD_QUEUE
|
||
|
mutex_destroy(&data->fifo_lock);
|
||
|
kfifo_free(&data->cmd_queue);
|
||
|
mutex_destroy(&data->wait_lock);
|
||
|
err_alloc_queue:
|
||
|
#endif
|
||
|
kfree(data->cmd_result);
|
||
|
err_alloc_cmd_result:
|
||
|
mutex_destroy(&data->cmd_lock);
|
||
|
list_del(&data->cmd_list_head);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_init);
|
||
|
|
||
|
void sec_cmd_exit(struct sec_cmd_data *data, int devt)
|
||
|
{
|
||
|
#ifdef USE_SEC_CMD_QUEUE
|
||
|
struct command cmd = {{0}};
|
||
|
int ret;
|
||
|
#endif
|
||
|
|
||
|
pr_info("%s: %s %s\n", dev_name(data->fac_dev), SECLOG, __func__);
|
||
|
sysfs_remove_group(&data->fac_dev->kobj, &sec_fac_attr_group);
|
||
|
dev_set_drvdata(data->fac_dev, NULL);
|
||
|
#if IS_ENABLED(CONFIG_DRV_SAMSUNG)
|
||
|
sec_device_destroy(data->fac_dev->devt);
|
||
|
#else
|
||
|
device_destroy(tsp_sec_class, devt);
|
||
|
#endif
|
||
|
#ifdef USE_SEC_CMD_QUEUE
|
||
|
mutex_lock(&data->fifo_lock);
|
||
|
while (kfifo_len(&data->cmd_queue)) {
|
||
|
ret = kfifo_out(&data->cmd_queue, &cmd, sizeof(struct command));
|
||
|
if (!ret) {
|
||
|
pr_err("%s %s: kfifo_out failed, it seems empty, ret=%d\n", SECLOG, __func__, ret);
|
||
|
}
|
||
|
pr_info("%s %s: remove pending commands: %s", SECLOG, __func__, cmd.cmd);
|
||
|
}
|
||
|
mutex_unlock(&data->fifo_lock);
|
||
|
mutex_destroy(&data->fifo_lock);
|
||
|
kfifo_free(&data->cmd_queue);
|
||
|
mutex_destroy(&data->wait_lock);
|
||
|
|
||
|
cancel_delayed_work_sync(&data->cmd_work);
|
||
|
flush_delayed_work(&data->cmd_work);
|
||
|
#endif
|
||
|
data->fac_dev = NULL;
|
||
|
kfree(data->cmd_result);
|
||
|
mutex_destroy(&data->cmd_lock);
|
||
|
list_del(&data->cmd_list_head);
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
|
||
|
if (devt == SEC_CLASS_DEVT_TSP1)
|
||
|
main_sec = NULL;
|
||
|
if (devt == SEC_CLASS_DEVT_TSP2)
|
||
|
sub_sec = NULL;
|
||
|
#endif
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_exit);
|
||
|
|
||
|
void sec_cmd_send_event_to_user(struct sec_cmd_data *data, char *test, char *result)
|
||
|
{
|
||
|
char *event[5];
|
||
|
char timestamp[32];
|
||
|
char feature[32];
|
||
|
char stest[32];
|
||
|
char sresult[64];
|
||
|
ktime_t calltime;
|
||
|
u64 realtime;
|
||
|
int curr_time;
|
||
|
char *eol = "\0";
|
||
|
|
||
|
if (!data || !data->fac_dev)
|
||
|
return;
|
||
|
|
||
|
calltime = ktime_get();
|
||
|
realtime = ktime_to_ns(calltime);
|
||
|
do_div(realtime, NSEC_PER_USEC);
|
||
|
curr_time = realtime / USEC_PER_MSEC;
|
||
|
|
||
|
snprintf(timestamp, 32, "TIMESTAMP=%d", curr_time);
|
||
|
strncat(timestamp, eol, 1);
|
||
|
snprintf(feature, 32, "FEATURE=TSP");
|
||
|
strncat(feature, eol, 1);
|
||
|
if (!test) {
|
||
|
snprintf(stest, 32, "TEST=NULL");
|
||
|
} else {
|
||
|
snprintf(stest, 32, "%s", test);
|
||
|
}
|
||
|
strncat(stest, eol, 1);
|
||
|
|
||
|
if (!result) {
|
||
|
snprintf(sresult, 64, "RESULT=NULL");
|
||
|
} else {
|
||
|
snprintf(sresult, 64, "%s", result);
|
||
|
}
|
||
|
strncat(sresult, eol, 1);
|
||
|
|
||
|
pr_info("%s: %s %s: time:%s, feature:%s, test:%s, result:%s\n",
|
||
|
dev_name(data->fac_dev), SECLOG, __func__, timestamp, feature, stest, sresult);
|
||
|
|
||
|
event[0] = timestamp;
|
||
|
event[1] = feature;
|
||
|
event[2] = stest;
|
||
|
event[3] = sresult;
|
||
|
event[4] = NULL;
|
||
|
|
||
|
kobject_uevent_env(&data->fac_dev->kobj, KOBJ_CHANGE, event);
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_send_event_to_user);
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
|
||
|
void sec_cmd_virtual_tsp_register(struct sec_cmd_data *sec)
|
||
|
{
|
||
|
if (strcmp(dev_name(sec->fac_dev), SEC_CLASS_DEV_NAME_TSP1) == 0) {
|
||
|
main_sec = sec;
|
||
|
input_info(true, sec->fac_dev, "%s: main\n", __func__);
|
||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 10, 0))
|
||
|
main_sec->sysfs_functions = devm_kzalloc(main_sec->fac_dev, sizeof(struct sec_ts_virtual_sysfs_function), GFP_KERNEL);
|
||
|
if (!main_sec->sysfs_functions) {
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
} else if (strcmp(dev_name(sec->fac_dev), SEC_CLASS_DEV_NAME_TSP2) == 0) {
|
||
|
sub_sec = sec;
|
||
|
input_info(true, sec->fac_dev, "%s: sub\n", __func__);
|
||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 10, 0))
|
||
|
sub_sec->sysfs_functions = devm_kzalloc(sub_sec->fac_dev, sizeof(struct sec_ts_virtual_sysfs_function), GFP_KERNEL);
|
||
|
if (!sub_sec->sysfs_functions) {
|
||
|
return ;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 10, 0))
|
||
|
int sec_cmd_virtual_tsp_read_sysfs(struct sec_cmd_data *sec, const char *path, char *buf, int len)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (main_sec) {
|
||
|
if (strcmp(path, PATH_MAIN_SEC_CMD_STATUS) == 0)
|
||
|
sec_cmd_show_status(main_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_MAIN_SEC_CMD_RESULT) == 0)
|
||
|
sec_cmd_show_result(main_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_MAIN_SEC_CMD_STATUS_ALL) == 0)
|
||
|
sec_cmd_show_status_all(main_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_MAIN_SEC_CMD_RESULT_ALL) == 0)
|
||
|
sec_cmd_show_result_all(main_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_MAIN_SEC_SYSFS_SUPPORT_FEATURE) == 0) {
|
||
|
if (main_sec->sysfs_functions->sec_tsp_support_feature_show != NULL)
|
||
|
main_sec->sysfs_functions->sec_tsp_support_feature_show(main_sec->fac_dev, NULL, buf);
|
||
|
} else if (strcmp(path, PATH_MAIN_SEC_SYSFS_PROX_POWER_OFF) == 0) {
|
||
|
if (main_sec->sysfs_functions->sec_tsp_prox_power_off_show != NULL)
|
||
|
main_sec->sysfs_functions->sec_tsp_prox_power_off_show(main_sec->fac_dev, NULL, buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sub_sec) {
|
||
|
if (strcmp(path, PATH_SUB_SEC_CMD_STATUS) == 0)
|
||
|
sec_cmd_show_status(sub_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_SUB_SEC_CMD_RESULT) == 0)
|
||
|
sec_cmd_show_result(sub_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_SUB_SEC_CMD_STATUS_ALL) == 0)
|
||
|
sec_cmd_show_status_all(sub_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_SUB_SEC_CMD_RESULT_ALL) == 0)
|
||
|
sec_cmd_show_result_all(sub_sec->fac_dev, NULL, buf);
|
||
|
else if (strcmp(path, PATH_SUB_SEC_SYSFS_PROX_POWER_OFF) == 0) {
|
||
|
if (sub_sec->sysfs_functions->sec_tsp_prox_power_off_show != NULL)
|
||
|
sub_sec->sysfs_functions->sec_tsp_prox_power_off_show(sub_sec->fac_dev, NULL, buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ret < 0) {
|
||
|
input_err(true, sec->fac_dev, "%s: failed to read, len:%d, ret:%d\n", __func__, len, ret);
|
||
|
ret = -EIO;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#else
|
||
|
int sec_cmd_virtual_tsp_read_sysfs(struct sec_cmd_data *sec, const char *path, char *buf, int len)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
mm_segment_t old_fs;
|
||
|
struct file *sysfs;
|
||
|
|
||
|
old_fs = get_fs();
|
||
|
set_fs(KERNEL_DS);
|
||
|
|
||
|
sysfs = filp_open(path, O_RDONLY, 0444);
|
||
|
if (IS_ERR(sysfs)) {
|
||
|
ret = PTR_ERR(sysfs);
|
||
|
input_err(true, sec->fac_dev, "%s: %s open fail, %d\n", __func__, path, ret);
|
||
|
set_fs(old_fs);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = sysfs->f_op->read(sysfs, buf, len, &sysfs->f_pos);
|
||
|
if (ret < 0) {
|
||
|
input_err(true, sec->fac_dev, "%s: failed to read, len:%d, ret:%d\n", __func__, len, ret);
|
||
|
ret = -EIO;
|
||
|
}
|
||
|
|
||
|
filp_close(sysfs, current->files);
|
||
|
set_fs(old_fs);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
EXPORT_SYMBOL(sec_cmd_virtual_tsp_read_sysfs);
|
||
|
|
||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 10, 0))
|
||
|
int sec_cmd_virtual_tsp_write_sysfs(struct sec_cmd_data *sec, const char *path, const char *cmd)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int len;
|
||
|
|
||
|
len = strlen(cmd);
|
||
|
if (strncmp(path, PATH_MAIN_SEC_CMD, 23) == 0) {
|
||
|
if (main_sec)
|
||
|
ret = sec_cmd_store(main_sec->fac_dev, NULL, cmd, len);
|
||
|
} else if (strncmp(path, PATH_SUB_SEC_CMD, 23) == 0) {
|
||
|
if (sub_sec)
|
||
|
ret = sec_cmd_store(sub_sec->fac_dev, NULL, cmd, len);
|
||
|
} else if (strncmp(path, PATH_MAIN_SEC_SYSFS_DUALSCREEN_POLICY, 38) == 0) {
|
||
|
if (main_sec) {
|
||
|
if (main_sec->sysfs_functions->dualscreen_policy_store != NULL)
|
||
|
ret = main_sec->sysfs_functions->dualscreen_policy_store(main_sec->fac_dev, NULL, cmd, len);
|
||
|
}
|
||
|
} else if (strncmp(path, PATH_SUB_SEC_SYSFS_DUALSCREEN_POLICY, 38) == 0) {
|
||
|
if (sub_sec) {
|
||
|
if (sub_sec->sysfs_functions->dualscreen_policy_store != NULL)
|
||
|
ret = sub_sec->sysfs_functions->dualscreen_policy_store(sub_sec->fac_dev, NULL, cmd, len);
|
||
|
}
|
||
|
} else if (strncmp(path, PATH_MAIN_SEC_SYSFS_PROX_POWER_OFF, 34) == 0) {
|
||
|
if (main_sec) {
|
||
|
if (main_sec->sysfs_functions->sec_tsp_prox_power_off_store != NULL)
|
||
|
ret = main_sec->sysfs_functions->sec_tsp_prox_power_off_store(main_sec->fac_dev, NULL, cmd, len);
|
||
|
}
|
||
|
} else if (strncmp(path, PATH_SUB_SEC_SYSFS_PROX_POWER_OFF, 34) == 0) {
|
||
|
if (sub_sec) {
|
||
|
if (sub_sec->sysfs_functions->sec_tsp_prox_power_off_store != NULL)
|
||
|
ret = sub_sec->sysfs_functions->sec_tsp_prox_power_off_store(sub_sec->fac_dev, NULL, cmd, len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ret != len) {
|
||
|
input_err(true, sec->fac_dev, "%s: failed to write, len:%d, ret:%d\n", __func__, len, ret);
|
||
|
ret = -EIO;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#else
|
||
|
int sec_cmd_virtual_tsp_write_sysfs(struct sec_cmd_data *sec, const char *path, const char *cmd)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
mm_segment_t old_fs;
|
||
|
struct file *sysfs;
|
||
|
int len;
|
||
|
|
||
|
if (strncmp(path, PATH_SUB_SEC_SYSFS_DUALSCREEN_POLICY, 38) == 0)
|
||
|
return ret;
|
||
|
|
||
|
len = strlen(cmd);
|
||
|
old_fs = get_fs();
|
||
|
set_fs(KERNEL_DS);
|
||
|
|
||
|
sysfs = filp_open(path, O_WRONLY, 0220);
|
||
|
if (IS_ERR(sysfs)) {
|
||
|
ret = PTR_ERR(sysfs);
|
||
|
input_err(true, sec->fac_dev, "%s: %s open fail, %d\n", __func__, path, ret);
|
||
|
set_fs(old_fs);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = sysfs->f_op->write(sysfs, cmd, len, &sysfs->f_pos);
|
||
|
if (ret != len) {
|
||
|
input_err(true, sec->fac_dev, "%s: failed to write, len:%d, ret:%d\n", __func__, len, ret);
|
||
|
ret = -EIO;
|
||
|
}
|
||
|
|
||
|
filp_close(sysfs, current->files);
|
||
|
set_fs(old_fs);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
EXPORT_SYMBOL(sec_cmd_virtual_tsp_write_sysfs);
|
||
|
|
||
|
static int sec_cmd_virtual_tsp_get_cmd_status(struct sec_cmd_data *sec, char *path)
|
||
|
{
|
||
|
u8 buff[16];
|
||
|
int ret;
|
||
|
|
||
|
memset(buff, 0x00, sizeof(buff));
|
||
|
|
||
|
ret = sec_cmd_virtual_tsp_read_sysfs(sec, path, buff, sizeof(buff));
|
||
|
if (ret < 0)
|
||
|
return SEC_CMD_STATUS_FAIL;
|
||
|
|
||
|
if (strncmp(buff, "WAITING", 7) == 0)
|
||
|
return SEC_CMD_STATUS_WAITING;
|
||
|
else if (strncmp(buff, "OK", 2) == 0)
|
||
|
return SEC_CMD_STATUS_OK;
|
||
|
else if (strncmp(buff, "FAIL", 4) == 0)
|
||
|
return SEC_CMD_STATUS_FAIL;
|
||
|
else if (strncmp(buff, "RUNNING", 7) == 0)
|
||
|
return SEC_CMD_STATUS_RUNNING;
|
||
|
else if (strncmp(buff, "EXPAND", 6) == 0)
|
||
|
return SEC_CMD_STATUS_EXPAND;
|
||
|
else
|
||
|
return SEC_CMD_STATUS_NOT_APPLICABLE;
|
||
|
}
|
||
|
|
||
|
int sec_cmd_virtual_tsp_write_cmd(struct sec_cmd_data *sec, bool main, bool sub)
|
||
|
{
|
||
|
u8 buff[16];
|
||
|
int ret_sub = 0;
|
||
|
int ret_main = 0;
|
||
|
bool exit = false;
|
||
|
|
||
|
sec_cmd_set_default_result(sec);
|
||
|
|
||
|
if (!main && !sub) {
|
||
|
snprintf(buff, sizeof(buff), "%s", "NA");
|
||
|
sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE;
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if (sub && sub_sec) {
|
||
|
input_dbg(true, sec->fac_dev, "%s: send to sub\n", sec->cmd);
|
||
|
ret_sub = sec_cmd_virtual_tsp_write_sysfs(sec, PATH_SUB_SEC_CMD, sec->cmd);
|
||
|
if (ret_sub < 0) {
|
||
|
snprintf(buff, sizeof(buff), "%s", "NG");
|
||
|
sec->cmd_state = SEC_CMD_STATUS_FAIL;
|
||
|
goto main;
|
||
|
}
|
||
|
sec->cmd_state = sec_cmd_virtual_tsp_get_cmd_status(sec, PATH_SUB_SEC_CMD_STATUS);
|
||
|
input_dbg(true, sec->fac_dev, "%s: sub_sec OK\n", sec->cmd);
|
||
|
if (!sub_sec->cmd_is_running)
|
||
|
exit = true;
|
||
|
sec_cmd_virtual_tsp_read_sysfs(sec, PATH_SUB_SEC_CMD_RESULT, sec->cmd_result, SEC_CMD_RESULT_STR_LEN);
|
||
|
memset(sec->cmd_result, 0x00, SEC_CMD_RESULT_STR_LEN_EXPAND);
|
||
|
sec_cmd_set_cmd_result(sec, sub_sec->cmd_result, strlen(sub_sec->cmd_result));
|
||
|
}
|
||
|
main:
|
||
|
if (main && main_sec) {
|
||
|
input_dbg(true, sec->fac_dev, "%s: send to main\n", sec->cmd);
|
||
|
ret_main = sec_cmd_virtual_tsp_write_sysfs(sec, PATH_MAIN_SEC_CMD, sec->cmd);
|
||
|
if (ret_main < 0) {
|
||
|
snprintf(buff, sizeof(buff), "%s", "NG");
|
||
|
sec->cmd_state = SEC_CMD_STATUS_FAIL;
|
||
|
goto err;
|
||
|
}
|
||
|
sec->cmd_state = sec_cmd_virtual_tsp_get_cmd_status(sec, PATH_MAIN_SEC_CMD_STATUS);
|
||
|
input_dbg(true, sec->fac_dev, "%s: main_sec OK\n", sec->cmd);
|
||
|
if (!main_sec->cmd_is_running)
|
||
|
exit = true;
|
||
|
sec_cmd_virtual_tsp_read_sysfs(sec, PATH_MAIN_SEC_CMD_RESULT, sec->cmd_result, SEC_CMD_RESULT_STR_LEN);
|
||
|
memset(sec->cmd_result, 0x00, SEC_CMD_RESULT_STR_LEN_EXPAND);
|
||
|
sec_cmd_set_cmd_result(sec, main_sec->cmd_result, strlen(main_sec->cmd_result));
|
||
|
}
|
||
|
if (exit) {
|
||
|
input_dbg(true, sec->fac_dev, "%s: set_cmd_exit\n", sec->cmd);
|
||
|
sec_cmd_set_cmd_exit(sec);
|
||
|
} else if ((main && !main_sec) || (sub && !sub_sec)) {
|
||
|
input_err(true, sec->fac_dev, "%s: some device is not registered in virtual tsp.\n", sec->cmd);
|
||
|
sec_cmd_set_cmd_exit(sec);
|
||
|
}
|
||
|
|
||
|
return (ret_sub < 0 || ret_main < 0) ? -1 : 0;
|
||
|
|
||
|
err:
|
||
|
sec_cmd_set_cmd_result(sec, buff, SEC_CMD_RESULT_STR_LEN);
|
||
|
sec_cmd_set_cmd_exit(sec);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_virtual_tsp_write_cmd);
|
||
|
|
||
|
void sec_cmd_virtual_tsp_write_cmd_factory_all(struct sec_cmd_data *sec, bool main, bool sub)
|
||
|
{
|
||
|
u8 buff[16];
|
||
|
int ret = 0;
|
||
|
|
||
|
sec_cmd_set_default_result(sec);
|
||
|
|
||
|
if (!main && !sub) {
|
||
|
snprintf(buff, sizeof(buff), "%s", "NA");
|
||
|
sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE;
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if (sub && sub_sec) {
|
||
|
input_dbg(true, sec->fac_dev, "%s: sub\n", sec->cmd);
|
||
|
ret = sec_cmd_virtual_tsp_write_sysfs(sec, PATH_SUB_SEC_CMD, sec->cmd);
|
||
|
if (ret < 0) {
|
||
|
snprintf(buff, sizeof(buff), "%s", "NG");
|
||
|
sec->cmd_all_factory_state = SEC_CMD_STATUS_FAIL;
|
||
|
goto main;
|
||
|
}
|
||
|
sec->cmd_all_factory_state = sec_cmd_virtual_tsp_get_cmd_status(sec, PATH_SUB_SEC_CMD_STATUS_ALL);
|
||
|
sec_cmd_virtual_tsp_read_sysfs(sec, PATH_SUB_SEC_CMD_RESULT_ALL, sec->cmd_result_all, SEC_CMD_RESULT_STR_LEN);
|
||
|
}
|
||
|
main:
|
||
|
if (main && main_sec) {
|
||
|
input_dbg(true, sec->fac_dev, "%s: main\n", sec->cmd);
|
||
|
ret = sec_cmd_virtual_tsp_write_sysfs(sec, PATH_MAIN_SEC_CMD, sec->cmd);
|
||
|
if (ret < 0) {
|
||
|
snprintf(buff, sizeof(buff), "%s", "NG");
|
||
|
sec->cmd_all_factory_state = SEC_CMD_STATUS_FAIL;
|
||
|
goto err;
|
||
|
}
|
||
|
sec->cmd_all_factory_state = sec_cmd_virtual_tsp_get_cmd_status(sec, PATH_MAIN_SEC_CMD_STATUS_ALL);
|
||
|
sec_cmd_virtual_tsp_read_sysfs(sec, PATH_MAIN_SEC_CMD_RESULT_ALL, sec->cmd_result_all, SEC_CMD_RESULT_STR_LEN);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
|
||
|
err:
|
||
|
sec_cmd_set_cmd_result_all(sec, buff, SEC_CMD_RESULT_STR_LEN, "NONE");
|
||
|
}
|
||
|
EXPORT_SYMBOL(sec_cmd_virtual_tsp_write_cmd_factory_all);
|
||
|
#endif
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_SEC_KUNIT) && !IS_ENABLED(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
|
||
|
|
||
|
static int __init sec_cmd_m_init(void)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void __exit sec_cmd_m_exit(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
module_init(sec_cmd_m_init);
|
||
|
module_exit(sec_cmd_m_exit);
|
||
|
#endif
|
||
|
|
||
|
MODULE_DESCRIPTION("Samsung input command");
|
||
|
MODULE_LICENSE("GPL");
|
||
|
|