You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/drivers/samsung/quest/sec_quest_qdaf.c

391 lines
11 KiB

/*
* drivers/debug/sec_quest_qdaf.c
*
* COPYRIGHT(C) 2006-2017 Samsung Electronics Co., Ltd. All Right Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/sec_class.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/fcntl.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/umh.h>
#include <linux/sec_quest.h>
#include <linux/sec_quest_qdaf.h>
extern struct device *sec_nad;
static int qdaf_cur_cmd_mode;
//////////////////////////////////////////////////
/////// helper functions /////////////////////////////
//////////////////////////////////////////////////
static int call_user_prg( char **argv, int wait )
{
int ret_userapp;
char *envp[5] = {
"HOME=/",
"PATH=/system/bin/quest:/system/bin:/system/xbin",
"ANDROID_DATA=/data",
"ANDROID_ROOT=/system",
NULL };
ret_userapp = call_usermodehelper(argv[0], argv, envp, wait);
if (!ret_userapp) {
QUEST_PRINT("%s is executed. ret_userapp = %d\n", argv[0], ret_userapp);
return 0;
} else {
QUEST_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0], ret_userapp);
return ret_userapp;
}
}
int get_qdaf_failed_cnt()
{
int ret_userapp, wait_val=0;
char *argv[4] = { NULL, NULL, NULL, NULL };
char qdaf_cmd[BUFF_SZ];
argv[0] = QDAF_PROG;
snprintf(qdaf_cmd, BUFF_SZ, "%d", QUEST_QDAF_ACTION_RESULT_GET);
argv[1] = qdaf_cmd;
wait_val = UMH_WAIT_PROC;
ret_userapp = call_user_prg(argv, wait_val);
// evaulate return value after ret_userapp>>8
// qdaf.sh exit with failed_cnt if persist.qdaf.failed_cnt>0
// so let's use return value from call_usermodehelper
return (ret_userapp>=0)? (ret_userapp>>8) : 0;
}
int call_qdaf_from_quest_driver(int cmd_mode, int wait_val)
{
int ret_userapp;
char *argv[4] = { NULL, NULL, NULL, NULL };
char qdaf_cmd[BUFF_SZ];
QUEST_PRINT("%s : cmd_mode=%d, wait_val=%d\n", __func__, cmd_mode, wait_val);
// let's just return if receiving the same command as before one
if ( qdaf_cur_cmd_mode==cmd_mode ) {
if( cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC ||
cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC) {
QUEST_PRINT("%s : return because qdaf.sh already has been running.\n", __func__);
}else if( cmd_mode==QUEST_QDAF_ACTION_CONTROL_STOP ||
cmd_mode==QUEST_QDAF_ACTION_CONTROL_STOP_WATING) {
QUEST_PRINT("%s : return because qdaf.sh has not been running yet.\n", __func__);
}
return cmd_mode;
}
argv[0] = QDAF_PROG;
snprintf(qdaf_cmd, BUFF_SZ, "%d", cmd_mode);
argv[1] = qdaf_cmd;
ret_userapp = call_user_prg(argv, wait_val);
if (!ret_userapp) qdaf_cur_cmd_mode = cmd_mode;
else qdaf_cur_cmd_mode = QUEST_QDAF_ACTION_EMPTY;
return ret_userapp;
}
static void qdaf_save_logs(void)
{
int fd, idx=0;
char temp[1]={'\0'}, buf[BUFF_SZ]={'\0',};
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
fd = sys_open(QDAF_QMESA_LOG, O_RDONLY, 0);
#else
fd = ksys_open(QDAF_QMESA_LOG, O_RDONLY, 0);
#endif
if (fd >= 0) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
while (sys_read(fd, temp, 1) == 1) {
#else
while (ksys_read(fd, temp, 1) == 1) {
#endif
buf[idx++] = temp[0];
if( temp[0]=='\n' ) {
buf[idx] = '\0';
QUEST_PRINT("%s", buf);
idx = 0;
}
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
sys_close(fd);
#else
ksys_close(fd);
#endif
}
set_fs(old_fs);
}
//////////////////////////////////////////////////
//////////////////////////////////////////////////
/////// NAD_QDAF_CONTROL ////////////////////////
//////////////////////////////////////////////////
static ssize_t show_quest_qdaf_control(struct device *dev,
struct device_attribute *attr, char *buf)
{
if( qdaf_cur_cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC ||
qdaf_cur_cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC )
return snprintf(buf, BUFF_SZ, "RUNNING\n");
return snprintf(buf, BUFF_SZ, "READY\n");
}
static ssize_t store_quest_qdaf_control(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int idx = 0, cmd_mode=0, wait_val=0;
char temp[QUEST_BUFF_SIZE * 3];
char quest_cmd[QUEST_CMD_LIST][QUEST_BUFF_SIZE];
char *quest_ptr, *string;
int ret_userapp;
char *argv[4] = { NULL, NULL, NULL, NULL };
QUEST_PRINT("%s : is called\n",__func__);
if ((int)count < QUEST_BUFF_SIZE) {
QUEST_PRINT("%s : return error (count=%d)\n", __func__, (int)count);
return -EINVAL;;
}
if (strncmp(buf, "nad_qdaf_control", 16)) {
QUEST_PRINT("%s : return error (buf=%s)\n", __func__, buf);
return -EINVAL;;
}
strlcpy(temp, buf, QUEST_BUFF_SIZE * 3);
string = temp;
while (idx < QUEST_CMD_LIST) {
quest_ptr = strsep(&string, ",");
strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE);
}
sscanf(quest_cmd[1], "%d", &cmd_mode);
// let's just return if receiving the same command as before one
if ( qdaf_cur_cmd_mode==cmd_mode ) {
if( cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC ||
cmd_mode==QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC) {
QUEST_PRINT("%s : return because qdaf.sh already has been running.\n", __func__);
}else if( cmd_mode==QUEST_QDAF_ACTION_CONTROL_STOP ||
cmd_mode==QUEST_QDAF_ACTION_CONTROL_STOP_WATING) {
QUEST_PRINT("%s : return because qdaf.sh has not been running yet.\n", __func__);
}
return count;
}
argv[0] = QDAF_PROG;
argv[1] = quest_cmd[1];
switch (cmd_mode) {
case QUEST_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC :
QUEST_PRINT("%s : qdaf will be started (without panic)\n",__func__);
wait_val = UMH_WAIT_EXEC;
break;
case QUEST_QDAF_ACTION_CONTROL_START_WITH_PANIC:
QUEST_PRINT("%s : qdaf will be started (with panic)\n",__func__);
wait_val = UMH_WAIT_EXEC;
break;
case QUEST_QDAF_ACTION_CONTROL_STOP :
QUEST_PRINT("%s : qdaf will be stopped\n",__func__);
wait_val = UMH_WAIT_EXEC;
break;
case QUEST_QDAF_ACTION_CONTROL_STOP_WATING :
QUEST_PRINT("%s : qdaf will be stopped (waiting)\n",__func__);
// should wait for completion to gurantee not working of qdaf
wait_val = UMH_WAIT_PROC;
break;
default :
QUEST_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode);
return count;
}
ret_userapp = call_user_prg(argv, wait_val);
if (!ret_userapp) qdaf_cur_cmd_mode = cmd_mode;
else qdaf_cur_cmd_mode = QUEST_QDAF_ACTION_EMPTY;
return count;
}
static DEVICE_ATTR(nad_qdaf_control, S_IRUGO | S_IWUSR, show_quest_qdaf_control, store_quest_qdaf_control);
//////////////////////////////////////////////////
//////////////////////////////////////////////////
/////// NAD_QDAF_RESULT //////////////////////////
//////////////////////////////////////////////////
static ssize_t show_quest_qdaf_result(struct device *dev,
struct device_attribute *attr, char *buf)
{
int failed_cnt;
QUEST_PRINT("%s : is called\n",__func__);
failed_cnt = get_qdaf_failed_cnt();
QUEST_PRINT("%s : failed_cnt = %d\n", __func__, failed_cnt);
if( failed_cnt == 0 )
return snprintf(buf, BUFF_SZ, "OK\n");
else if ( failed_cnt > 0 )
return snprintf(buf, BUFF_SZ, "NG,%d\n", failed_cnt);
return snprintf(buf, BUFF_SZ, "NONE\n");
}
static ssize_t store_quest_qdaf_result(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int idx = 0, cmd_mode=0, wait_val=0;
char temp[QUEST_BUFF_SIZE * 3];
char quest_cmd[QUEST_CMD_LIST][QUEST_BUFF_SIZE];
char *quest_ptr, *string;
char *argv[4] = { NULL, NULL, NULL, NULL };
QUEST_PRINT("%s : is called\n",__func__);
if ((int)count < QUEST_BUFF_SIZE) {
QUEST_PRINT("%s : return error (count=%d)\n", __func__, (int)count);
return -EINVAL;;
}
if (strncmp(buf, "nad_qdaf_result", 15)) {
QUEST_PRINT("%s : return error (buf=%s)\n", __func__, buf);
return -EINVAL;;
}
strlcpy(temp, buf, QUEST_BUFF_SIZE * 3);
string = temp;
while (idx < QUEST_CMD_LIST) {
quest_ptr = strsep(&string, ",");
strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE);
}
sscanf(quest_cmd[1], "%d", &cmd_mode);
argv[0] = QDAF_PROG;
argv[1] = quest_cmd[1];
switch (cmd_mode) {
case QUEST_QDAF_ACTION_RESULT_ERASE :
QUEST_PRINT("%s : qdaf will erase failed count\n",__func__);
wait_val = UMH_WAIT_PROC;
break;
default :
QUEST_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode);
return count;
}
call_user_prg(argv, wait_val);
return count;
}
static DEVICE_ATTR(nad_qdaf_result, S_IRUGO | S_IWUSR, show_quest_qdaf_result, store_quest_qdaf_result);
//////////////////////////////////////////////////
//////////////////////////////////////////////////
/////// NAD_QDAF_DEBUG //////////////////////////
//////////////////////////////////////////////////
static ssize_t store_quest_qdaf_debug(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int idx = 0, cmd_mode=0;
char temp[QUEST_BUFF_SIZE * 3];
char quest_cmd[QUEST_CMD_LIST][QUEST_BUFF_SIZE];
char *quest_ptr, *string;
QUEST_PRINT("%s : is called\n",__func__);
if ((int)count < QUEST_BUFF_SIZE) {
QUEST_PRINT("%s : return error (count=%d)\n", __func__, (int)count);
return -EINVAL;;
}
if (strncmp(buf, "nad_qdaf_debug", 14)) {
QUEST_PRINT("%s : return error (buf=%s)\n", __func__, buf);
return -EINVAL;;
}
strlcpy(temp, buf, QUEST_BUFF_SIZE * 3);
string = temp;
while (idx < QUEST_CMD_LIST) {
quest_ptr = strsep(&string, ",");
strlcpy(quest_cmd[idx++], quest_ptr, QUEST_BUFF_SIZE);
}
sscanf(quest_cmd[1], "%d", &cmd_mode);
switch (cmd_mode) {
case QUEST_QDAF_ACTION_DEBUG_SAVE_LOGS :
QUEST_PRINT("%s : qdaf will save log into kmsg\n",__func__);
qdaf_save_logs();
return count;
case QUEST_QDAF_ACTION_DEBUG_TRIGGER_PANIC :
QUEST_PRINT("%s : will trigger panic\n",__func__);
panic("qdaf_fail");
return count;
}
return count;
}
static DEVICE_ATTR(nad_qdaf_debug, S_IWUSR, NULL, store_quest_qdaf_debug);
//////////////////////////////////////////////////
static int __init sec_quest_qdaf_init(void)
{
int ret = 0;
QUEST_PRINT("%s\n", __func__);
ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_control);
if (ret) {
QUEST_PRINT("%s: Failed to create device file\n", __func__);
goto err_create_nad_sysfs;
}
ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_debug);
if (ret) {
QUEST_PRINT("%s: Failed to create device file\n", __func__);
goto err_create_nad_sysfs;
}
ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_result);
if (ret) {
QUEST_PRINT("%s: Failed to create device file\n", __func__);
goto err_create_nad_sysfs;
}
return 0;
err_create_nad_sysfs:
return ret;
}
module_init(sec_quest_qdaf_init);