1720 lines
45 KiB
C
1720 lines
45 KiB
C
/*
|
|
* drivers/debug/sec_nad.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/sec_nad.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/sec_class.h>
|
|
#include <linux/module.h>
|
|
|
|
#define NAD_PRINT(format, ...) printk(KERN_ERR "[NAD] " format, ##__VA_ARGS__)
|
|
#define NAD_DEBUG
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/io.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <linux/sec_debug.h>
|
|
#include <soc/qcom/subsystem_restart.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sec_param.h>
|
|
#include <linux/types.h>
|
|
|
|
|
|
#define BUFF_SZ 256
|
|
/* flag for nad test mode : SMD_NAD or ETC_NAD or MAIN_NAD*/
|
|
static int nad_test_mode = -1;
|
|
|
|
#define SMD_NAD_PROG "/system/bin/qnad/qnad.sh"
|
|
#define MAIN_NAD_PROG "/system/bin/qnad/qnad_main.sh"
|
|
#define ETC_NAD_PROG "/system/bin/qnad/qnad.sh"
|
|
#define ERASE_NAD_PRG "/system/bin/qnad/remove_files.sh"
|
|
|
|
#define SMD_NAD_RESULT "/nad_refer/NAD_SMD/test_result.csv"
|
|
#define MAIN_NAD_RESULT "/nad_refer/NAD_MAIN/test_result.csv"
|
|
#define ETC_NAD_RESULT "/nad_refer/NAD/test_result.csv"
|
|
|
|
#define SMD_NAD_LOGPATH "logPath:/nad_refer/NAD_SMD"
|
|
#define MAIN_NAD_LOGPATH "logPath:/nad_refer/NAD_MAIN"
|
|
#define ETC_NAD_LOGPATH "logPath:/nad_refer/NAD"
|
|
|
|
struct param_qnad param_qnad_data;
|
|
static int erase_pass;
|
|
extern unsigned int lpcharge;
|
|
unsigned int clk_limit_flag;
|
|
int curr_smd = ETC_NAD;
|
|
int main_reboot;
|
|
|
|
char *STR_TEST_ITEM[ITEM_CNT + 1] = {
|
|
"UFS",
|
|
"QMESACACHE",
|
|
"QMESADDR",
|
|
"VDDMIN",
|
|
"SUSPEND",
|
|
"CCOHERENCY",
|
|
"ICACHE",
|
|
"CRYPTO",
|
|
"DDR",
|
|
"SENSOR",
|
|
//
|
|
"FULL"
|
|
};
|
|
|
|
struct kobj_uevent_env nad_uevent;
|
|
|
|
int get_nad_result(char *buf, int piece)
|
|
{
|
|
int iCnt;
|
|
unsigned int smd_result;
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
smd_result = param_qnad_data.total_test_result;
|
|
|
|
NAD_PRINT("%s : param_qnad_data.total_test_result=%u,", __func__,
|
|
param_qnad_data.total_test_result);
|
|
|
|
if (piece == ITEM_CNT) {
|
|
for (iCnt = 0; iCnt < ITEM_CNT; iCnt++) {
|
|
switch (smd_result & 0x3) {
|
|
case 0:
|
|
strcat(buf, "[X]");
|
|
break;
|
|
case 1:
|
|
strcat(buf, "[F]");
|
|
break;
|
|
case 2:
|
|
strcat(buf, "[P]");
|
|
break;
|
|
case 3:
|
|
strcat(buf, "[N]");
|
|
break;
|
|
}
|
|
|
|
smd_result >>= 2;
|
|
}
|
|
} else {
|
|
smd_result >>= 2 * piece;
|
|
switch (smd_result & 0x3) {
|
|
case 1:
|
|
strlcpy(buf, "FAIL", sizeof(buf));
|
|
break;
|
|
case 2:
|
|
strlcpy(buf, "PASS", sizeof(buf));
|
|
break;
|
|
case 3:
|
|
strlcpy(buf, "NA", sizeof(buf));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ITEM_CNT;
|
|
err_out:
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(get_nad_result);
|
|
|
|
static int NadResult(int smd_nad)
|
|
{
|
|
int fd = 0;
|
|
int ret = TOTALTEST_UNKNOWN;
|
|
int ddr_result = 0;
|
|
char buf[512] = { '\0', };
|
|
mm_segment_t old_fs = get_fs();
|
|
|
|
set_fs(KERNEL_DS);
|
|
|
|
switch (smd_nad) {
|
|
case SMD_NAD:
|
|
fd = sys_open(SMD_NAD_RESULT, O_RDONLY, 0);
|
|
break;
|
|
case MAIN_NAD:
|
|
fd = sys_open(MAIN_NAD_RESULT, O_RDONLY, 0);
|
|
break;
|
|
case ETC_NAD:
|
|
fd = sys_open(ETC_NAD_RESULT, O_RDONLY, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (fd >= 0) {
|
|
int found = 0;
|
|
|
|
printk(KERN_DEBUG);
|
|
|
|
while (sys_read(fd, buf, 512)) {
|
|
char *ptr;
|
|
char *div = "\n";
|
|
char *tok = NULL;
|
|
|
|
ptr = buf;
|
|
while ((tok = strsep(&ptr, div)) != NULL) {
|
|
|
|
if ((strstr(tok, "FAIL"))) {
|
|
ret = TOTALTEST_FAIL;
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
if ((strstr(tok, "AllTestDone"))) {
|
|
ret = TOTALTEST_PASS;
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
ret = TOTALTEST_NO_RESULT_STRING;
|
|
|
|
sys_close(fd);
|
|
} else {
|
|
NAD_PRINT("The result file is not existed. %s\n", __func__);
|
|
ret = TOTALTEST_NO_RESULT_FILE;
|
|
}
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
if ((smd_nad == SMD_NAD) || (smd_nad == MAIN_NAD)) {
|
|
ddr_result = GET_DDR_TEST_RESULT(smd_nad, param_qnad_data.ddrtest_result);
|
|
if (ddr_result == DDRTEST_PASS) {
|
|
NAD_PRINT("ddr test was succeeded\n");
|
|
} else if ((ret != TOTALTEST_UNKNOWN) && (ddr_result == DDRTEST_FAIL)) {
|
|
NAD_PRINT("ddr test was failed\n");
|
|
ret = TOTALTEST_FAIL;
|
|
} else
|
|
NAD_PRINT("ddr test was not executed\n");
|
|
}
|
|
|
|
err_out:
|
|
set_fs(old_fs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void do_nad(int smd_nad)
|
|
{
|
|
char *argv[4] = { NULL, NULL, NULL, NULL };
|
|
char *envp[5] = {
|
|
"HOME=/",
|
|
"PATH=/system/bin/qnad:/system/bin:/system/xbin",
|
|
"ANDROID_DATA=/data",
|
|
"ANDROID_ROOT=/system",
|
|
NULL };
|
|
int ret_userapp;
|
|
|
|
switch (smd_nad) {
|
|
case SMD_NAD:
|
|
argv[0] = SMD_NAD_PROG;
|
|
argv[1] = SMD_NAD_LOGPATH;
|
|
NAD_PRINT("SMD_NAD, nad_test_mode : %d\n", nad_test_mode);
|
|
break;
|
|
case MAIN_NAD:
|
|
argv[0] = MAIN_NAD_PROG;
|
|
argv[1] = MAIN_NAD_LOGPATH;
|
|
if (main_reboot == 1)
|
|
argv[2] = "Reboot";
|
|
NAD_PRINT("MAIN_NAD, nad_test_mode : %d", nad_test_mode);
|
|
NAD_PRINT("reboot option enabled \n");
|
|
break;
|
|
case ETC_NAD:
|
|
argv[0] = ETC_NAD_PROG;
|
|
argv[1] = ETC_NAD_LOGPATH;
|
|
NAD_PRINT("ETC_NAD, nad_test_mode : %d\n", nad_test_mode);
|
|
argv[2] = "Reboot";
|
|
NAD_PRINT
|
|
("Setting an argument to reboot after NAD completes.\n");
|
|
break;
|
|
default:
|
|
NAD_PRINT("Invalid smd_nad value, nad_test_mode : %d\n",
|
|
nad_test_mode);
|
|
break;
|
|
}
|
|
|
|
ret_userapp = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
|
|
|
|
if (!ret_userapp) {
|
|
NAD_PRINT("%s is executed. ret_userapp = %d\n", argv[0],
|
|
ret_userapp);
|
|
|
|
if (erase_pass)
|
|
erase_pass = 0;
|
|
} else {
|
|
NAD_PRINT("%s is NOT executed. ret_userapp = %d\n", argv[0],
|
|
ret_userapp);
|
|
nad_test_mode = -1;
|
|
}
|
|
}
|
|
|
|
static ssize_t show_nad_acat(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int nad_result;
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
NAD_PRINT("%s : magic %x, nad cnt %d, ddr cnt %d, ddr result 0x%2X\n",
|
|
__func__, param_qnad_data.magic,
|
|
param_qnad_data.nad_remain_count,
|
|
param_qnad_data.ddrtest_remain_count,
|
|
GET_DDR_TEST_RESULT(ETC_NAD, param_qnad_data.ddrtest_result));
|
|
|
|
nad_result = NadResult(ETC_NAD);
|
|
switch (nad_result) {
|
|
case TOTALTEST_PASS: {
|
|
NAD_PRINT("NAD Passed\n");
|
|
return snprintf(buf, BUFF_SZ, "OK_ACAT_NONE\n");
|
|
} break;
|
|
|
|
case TOTALTEST_FAIL: {
|
|
NAD_PRINT("NAD fail\n");
|
|
return snprintf(buf, BUFF_SZ, "NG_ACAT_ASV\n");
|
|
} break;
|
|
|
|
default: {
|
|
NAD_PRINT("NAD No Run\n");
|
|
return snprintf(buf, BUFF_SZ, "OK\n");
|
|
}
|
|
}
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "UNKNOWN\n");
|
|
}
|
|
|
|
static ssize_t store_nad_acat(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret = -1;
|
|
int idx = 0;
|
|
int nad_loop_count, dram_loop_count;
|
|
char temp[NAD_BUFF_SIZE * 3];
|
|
char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE];
|
|
char *nad_ptr, *string;
|
|
NAD_PRINT("buf : %s count : %d\n", buf, (int)count);
|
|
if ((int)count < NAD_BUFF_SIZE)
|
|
return -EINVAL;
|
|
|
|
/* Copy buf to nad temp */
|
|
strlcpy(temp, buf, NAD_BUFF_SIZE * 3);
|
|
string = temp;
|
|
|
|
while (idx < NAD_CMD_LIST) {
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE);
|
|
}
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
if (!strncmp(buf, "nad_acat", 8)) {
|
|
// checking 1st boot and setting test count
|
|
if (param_qnad_data.magic != NAD_SMD_MAGIC) { // <======================== 1st boot at right after SMD D/L done
|
|
nad_test_mode = SMD_NAD;
|
|
NAD_PRINT("1st boot at SMD\n");
|
|
param_qnad_data.magic = NAD_SMD_MAGIC;
|
|
param_qnad_data.nad_remain_count = 0x0;
|
|
param_qnad_data.ddrtest_remain_count = 0x0;
|
|
|
|
// flushing to param partition
|
|
if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err
|
|
("%s : fail - set param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
curr_smd = nad_test_mode;
|
|
do_nad(nad_test_mode);
|
|
} else { // <========================not SMD, it can be LCIA, CAL, FINAL and 15 ACAT.
|
|
nad_test_mode = ETC_NAD;
|
|
ret = sscanf(nad_cmd[1], "%d\n", &nad_loop_count);
|
|
if (ret != 1)
|
|
return -EINVAL;
|
|
|
|
ret = sscanf(nad_cmd[2], "%d\n", &dram_loop_count);
|
|
if (ret != 1)
|
|
return -EINVAL;
|
|
|
|
NAD_PRINT("ETC NAD, nad_acat%d,%d\n", nad_loop_count,
|
|
dram_loop_count);
|
|
if (!nad_loop_count && !dram_loop_count) { // <+++++++++++++++++ both counts are 0, it means 1. testing refers to current remain_count
|
|
|
|
// stop retrying when failure occur during retry test at ACAT/15test
|
|
if (param_qnad_data.magic == NAD_SMD_MAGIC
|
|
&& NadResult(ETC_NAD) == TOTALTEST_FAIL) {
|
|
pr_err
|
|
("%s : nad test fail - set the remain counts to 0\n",
|
|
__func__);
|
|
param_qnad_data.nad_remain_count = 0;
|
|
param_qnad_data.ddrtest_remain_count =
|
|
0;
|
|
|
|
// flushing to param partition
|
|
if (!sec_set_param
|
|
(param_index_qnad,
|
|
¶m_qnad_data)) {
|
|
pr_err
|
|
("%s : fail - set param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
if (param_qnad_data.nad_remain_count > 0) { // NAD count still remain
|
|
NAD_PRINT
|
|
("nad : nad_remain_count = %d, ddrtest_remain_count = %d\n",
|
|
param_qnad_data.nad_remain_count,
|
|
param_qnad_data.
|
|
ddrtest_remain_count);
|
|
param_qnad_data.nad_remain_count--;
|
|
param_qnad_data.total_test_result &= 0xffffffff;
|
|
|
|
/* if(param_qnad_data.ddrtest_remain_count && param_qnad_data.nad_remain_count == 0) { // last NAD count, and next is ddr test. rebooting will be done by NAD
|
|
NAD_PRINT("switching : nad_remain_count = %d, ddrtest_remain_count = %d\n", param_qnad_data.nad_remain_count, param_qnad_data.ddrtest_remain_count);
|
|
param_qnad_data.ddrtest_remain_count--;
|
|
|
|
do_ddrtest();
|
|
}*/
|
|
|
|
// flushing to param partition
|
|
if (!sec_set_param
|
|
(param_index_qnad,
|
|
¶m_qnad_data)) {
|
|
pr_err
|
|
("%s : fail - set param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
|
|
curr_smd = nad_test_mode;
|
|
do_nad(nad_test_mode);
|
|
} else if (param_qnad_data.ddrtest_remain_count) { // NAD already done before, only ddr test remains. then it needs selfrebooting.
|
|
NAD_PRINT
|
|
("ddrtest : nad_remain_count = %d, ddrtest_remain_count = %d\n",
|
|
param_qnad_data.nad_remain_count,
|
|
param_qnad_data.
|
|
ddrtest_remain_count);
|
|
|
|
//do_msm_restart(REBOOT_HARD, "sec_debug_hw_reset");
|
|
while (1)
|
|
;
|
|
}
|
|
} else { // <+++++++++++++++++ not (0,0) means 1. new test count came, 2. so overwrite the remain_count, 3. and not reboot by itsself, 4. reboot cmd will come from outside like factory PGM
|
|
|
|
param_qnad_data.nad_remain_count =
|
|
nad_loop_count;
|
|
param_qnad_data.ddrtest_remain_count =
|
|
dram_loop_count;
|
|
param_qnad_data.ddrtest_mode = UPLOAD_CAUSE_DDR_TEST;
|
|
// flushing to param partition
|
|
if (!sec_set_param
|
|
(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err
|
|
("%s : fail - set param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
|
|
NAD_PRINT
|
|
("new cmd : nad_remain_count = %d, ddrtest_remain_count = %d\n",
|
|
param_qnad_data.nad_remain_count,
|
|
param_qnad_data.ddrtest_remain_count);
|
|
}
|
|
}
|
|
return count;
|
|
} else
|
|
return count;
|
|
err_out:
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_acat, S_IRUGO | S_IWUSR, show_nad_acat, store_nad_acat);
|
|
|
|
static ssize_t show_nad_stat(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int nad_result;
|
|
|
|
// refer to qpnp_pon_reason (index=boot_reason-1)
|
|
NAD_PRINT("%s : boot_reason was %d\n", __func__, boot_reason);
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
NAD_PRINT("%s : magic %x, nad cnt %d, ddr cnt %d, ddr result 0x%2X\n",
|
|
__func__, param_qnad_data.magic,
|
|
param_qnad_data.nad_remain_count,
|
|
param_qnad_data.ddrtest_remain_count,
|
|
GET_DDR_TEST_RESULT(SMD_NAD, param_qnad_data.ddrtest_result));
|
|
|
|
if (param_qnad_data.magic != NAD_SMD_MAGIC) {
|
|
NAD_PRINT("SMD NAD NOT_TESTED\n");
|
|
return snprintf(buf, BUFF_SZ, "NOT_TESTED\n");
|
|
} else {
|
|
nad_result = NadResult(SMD_NAD);
|
|
switch (nad_result) {
|
|
case TOTALTEST_PASS: {
|
|
// there is "AllTestDone" at SMD/test_result.csv
|
|
NAD_PRINT("SMD NAD PASS\n");
|
|
return snprintf(buf, BUFF_SZ, "OK_2.0\n");
|
|
} break;
|
|
|
|
case TOTALTEST_FAIL: {
|
|
// there is "FAIL" at SMD/test_result.csv
|
|
char strResult[BUFF_SZ-14] = { '\0', };
|
|
|
|
get_nad_result(strResult, ITEM_CNT);
|
|
NAD_PRINT("SMD NAD FAIL, %s", buf);
|
|
return snprintf(buf, BUFF_SZ, "NG_2.0_FAIL_%s\n", strResult);
|
|
} break;
|
|
|
|
case TOTALTEST_NO_RESULT_FILE: {
|
|
// there is no SMD/test_result.csv
|
|
if (nad_test_mode == SMD_NAD) {
|
|
// will be executed soon
|
|
NAD_PRINT("SMD NAD TESTING\n");
|
|
return snprintf(buf, BUFF_SZ, "TESTING\n");
|
|
} else {
|
|
// not exeuted by unknown reasons
|
|
// ex1) magic exists but nad_refer was removed
|
|
// ex2) fail to execute qnad.sh
|
|
// ex3) fail to make test_result.csv
|
|
// ex4) etc...
|
|
NAD_PRINT("SMD NAD NO_RESULT_FILE && not started\n");
|
|
return snprintf(buf, BUFF_SZ, "RE_WORK\n");
|
|
}
|
|
} break;
|
|
|
|
case TOTALTEST_NO_RESULT_STRING: {
|
|
if (nad_test_mode == SMD_NAD) {
|
|
// will be completed
|
|
NAD_PRINT("SMD NAD TESTING\n");
|
|
return snprintf(buf, BUFF_SZ, "TESTING\n");
|
|
} else {
|
|
// need to rework
|
|
NAD_PRINT("SMD NAD NO_RESULT_STRING && not started\n");
|
|
return snprintf(buf, BUFF_SZ, "RE_WORK\n");
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
err_out:
|
|
NAD_PRINT("SMD NAD UNKNOWN\n");
|
|
return snprintf(buf, BUFF_SZ, "RE_WORK\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_stat, S_IRUGO, show_nad_stat, NULL);
|
|
|
|
static ssize_t show_ddrtest_remain_count(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
return snprintf(buf, BUFF_SZ, "%d\n",
|
|
param_qnad_data.ddrtest_remain_count);
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "PARAM ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_ddrtest_remain_count, S_IRUGO, show_ddrtest_remain_count,
|
|
NULL);
|
|
|
|
static ssize_t show_nad_remain_count(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
return snprintf(buf, BUFF_SZ, "%d\n", param_qnad_data.nad_remain_count);
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "PARAM ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_qmvs_remain_count, 0444, show_nad_remain_count, NULL);
|
|
|
|
static ssize_t store_nad_erase(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
if (!strncmp(buf, "erase", 5)) {
|
|
char *argv[4] = { NULL, NULL, NULL, NULL };
|
|
char *envp[3] = { NULL, NULL, NULL };
|
|
int ret_userapp;
|
|
int api_gpio_test = 0;
|
|
char api_gpio_test_result[256] = { 0, };
|
|
|
|
argv[0] = ERASE_NAD_PRG;
|
|
|
|
envp[0] = "HOME=/";
|
|
envp[1] =
|
|
"PATH=/system/bin/qnad/:/system/bin:/system/xbin";
|
|
ret_userapp =
|
|
call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
|
|
|
|
if (!ret_userapp) {
|
|
NAD_PRINT
|
|
("remove_files.sh is executed. ret_userapp = %d\n",
|
|
ret_userapp);
|
|
erase_pass = 1;
|
|
} else {
|
|
NAD_PRINT
|
|
("remove_files.sh is NOT executed. ret_userapp = %d\n",
|
|
ret_userapp);
|
|
erase_pass = 0;
|
|
}
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
|
|
param_qnad_data.magic = 0x0;
|
|
param_qnad_data.nad_remain_count = 0x0;
|
|
param_qnad_data.ddrtest_remain_count = 0x0;
|
|
param_qnad_data.ddrtest_result = 0x0;
|
|
param_qnad_data.ddrtest_mode = 0x0;
|
|
param_qnad_data.total_test_result = 0x0;
|
|
|
|
// flushing to param partition
|
|
if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - set param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
|
|
NAD_PRINT("clearing MAGIC code done = %d\n",
|
|
param_qnad_data.magic);
|
|
NAD_PRINT("nad_remain_count = %d\n",
|
|
param_qnad_data.nad_remain_count);
|
|
NAD_PRINT("ddrtest_remain_count = %d\n",
|
|
param_qnad_data.ddrtest_remain_count);
|
|
NAD_PRINT("ddrtest_result = 0x%8X\n",
|
|
param_qnad_data.ddrtest_result);
|
|
NAD_PRINT("clearing smd ddr test MAGIC code done = %d\n",
|
|
param_qnad_data.magic);
|
|
|
|
// clearing API test result
|
|
if (!sec_set_param(param_index_api_gpio_test, &api_gpio_test)) {
|
|
pr_err("%s : fail - set param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
|
|
if (!sec_set_param
|
|
(param_index_api_gpio_test_result, api_gpio_test_result)) {
|
|
pr_err("%s : fail - set param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
return count;
|
|
} else
|
|
return count;
|
|
err_out:
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_nad_erase(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
if (erase_pass)
|
|
return snprintf(buf, BUFF_SZ, "OK\n");
|
|
else
|
|
return snprintf(buf, BUFF_SZ, "NG\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_erase, S_IRUGO | S_IWUSR, show_nad_erase,
|
|
store_nad_erase);
|
|
|
|
static ssize_t show_nad_dram(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
// The factory app needs only the ddrtest result of ACAT now.
|
|
// If the ddrtest result of SMD and MAIN are also needed,
|
|
// implement an additional sysfs node or a modification of app.
|
|
ret = GET_DDR_TEST_RESULT(ETC_NAD, param_qnad_data.ddrtest_result);
|
|
|
|
if (ret == DDRTEST_PASS)
|
|
return snprintf(buf, BUFF_SZ, "OK_DRAM\n");
|
|
else if (ret == DDRTEST_FAIL)
|
|
return snprintf(buf, BUFF_SZ, "NG_DRAM_DATA\n");
|
|
else
|
|
return snprintf(buf, BUFF_SZ, "NO_DRAMTEST\n");
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "READ ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_dram, S_IRUGO, show_nad_dram, NULL);
|
|
|
|
static ssize_t show_nad_dram_debug(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
// int ret=0;
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
return snprintf(buf, BUFF_SZ, "0x%x\n", param_qnad_data.ddrtest_result);
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "READ ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_dram_debug, S_IRUGO, show_nad_dram_debug, NULL);
|
|
|
|
static ssize_t show_nad_dram_err_addr(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int ret = 0;
|
|
int i = 0;
|
|
struct param_qnad_ddr_result param_qnad_ddr_result_data;
|
|
|
|
if (!sec_get_param
|
|
(param_index_qnad_ddr_result, ¶m_qnad_ddr_result_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_ddr_result_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
|
|
ret =
|
|
snprintf(buf, BUFF_SZ, "Total : %d\n\n",
|
|
param_qnad_ddr_result_data.ddr_err_addr_total);
|
|
for (i = 0; i < param_qnad_ddr_result_data.ddr_err_addr_total; i++) {
|
|
ret +=
|
|
snprintf(buf + ret - 1, BUFF_SZ, "[%d] 0x%llx\n", i,
|
|
param_qnad_ddr_result_data.ddr_err_addr[i]);
|
|
}
|
|
|
|
return ret;
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "READ ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_dram_err_addr, S_IRUGO, show_nad_dram_err_addr, NULL);
|
|
|
|
static ssize_t show_nad_support(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
#if defined(CONFIG_ARCH_MSM8998) || defined(CONFIG_ARCH_MSM8996) || defined(CONFIG_ARCH_SDM845)
|
|
return snprintf(buf, BUFF_SZ, "SUPPORT\n");
|
|
#else
|
|
return snprintf(buf, BUFF_SZ, "NOT_SUPPORT\n");
|
|
#endif
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_support, S_IRUGO, show_nad_support, NULL);
|
|
|
|
static ssize_t store_nad_logs(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int fd = 0, i = 0;
|
|
char logbuf[500] = { '\0' };
|
|
char path[100] = { '\0' };
|
|
char ptr;
|
|
mm_segment_t old_fs = get_fs();
|
|
|
|
set_fs(KERNEL_DS);
|
|
|
|
NAD_PRINT("%s\n", buf);
|
|
|
|
sscanf(buf, "%s", path);
|
|
fd = sys_open(path, O_RDONLY, 0);
|
|
|
|
if (fd >= 0) {
|
|
while (sys_read(fd, &ptr, 1) && ptr != -1) {
|
|
//NAD_PRINT("%c\n", ptr);
|
|
logbuf[i] = ptr;
|
|
i++;
|
|
if (ptr == '\n') {
|
|
NAD_PRINT("%s", logbuf);
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
sys_close(fd);
|
|
} else {
|
|
NAD_PRINT("The File is not existed. %s\n", __func__);
|
|
}
|
|
|
|
set_fs(old_fs);
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_logs, 0200, NULL, store_nad_logs);
|
|
|
|
static ssize_t store_nad_end(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char result[20] = { '\0' };
|
|
|
|
NAD_PRINT("result : %s\n", buf);
|
|
|
|
sscanf(buf, "%s", result);
|
|
|
|
if (!strcmp(result, "nad_pass")) {
|
|
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, nad_uevent.envp);
|
|
NAD_PRINT
|
|
("NAD result : %s, nad_pass, Send to Process App for Nad test end : %s\n",
|
|
result, __func__);
|
|
} else {
|
|
if (nad_test_mode == MAIN_NAD || nad_test_mode == ETC_NAD) {
|
|
NAD_PRINT
|
|
("NAD result : %s, Device enter the upload mode because it is ETC_NAD : %s\n",
|
|
result, __func__);
|
|
panic(result);
|
|
} else {
|
|
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE,
|
|
nad_uevent.envp);
|
|
NAD_PRINT
|
|
("NAD result : %s, Send to Process App for Nad test end : %s\n",
|
|
result, __func__);
|
|
}
|
|
}
|
|
|
|
nad_test_mode = -1;
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_end, 0200, NULL, store_nad_end);
|
|
|
|
static ssize_t show_nad_api(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
unsigned int api_gpio_test;
|
|
char api_gpio_test_result[256];
|
|
|
|
if (!sec_get_param(param_index_api_gpio_test, &api_gpio_test)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
if (api_gpio_test) {
|
|
if (!sec_get_param
|
|
(param_index_api_gpio_test_result, api_gpio_test_result)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n",
|
|
__func__);
|
|
goto err_out;
|
|
}
|
|
return snprintf(buf, BUFF_SZ, "%s", api_gpio_test_result);
|
|
} else
|
|
return snprintf(buf, BUFF_SZ, "NONE\n");
|
|
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "READ ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_api, 0444, show_nad_api, NULL);
|
|
|
|
static ssize_t store_nad_result(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int _result = -1;
|
|
char test_name[NAD_BUFF_SIZE * 2] = { '\0', };
|
|
char temp[NAD_BUFF_SIZE * 3] = { '\0', };
|
|
char nad_test[2][NAD_BUFF_SIZE * 2]; // 2: "test_name", "result"
|
|
char result_string[NAD_BUFF_SIZE] = { '\0', };
|
|
char *nad_ptr, *string;
|
|
int smd = curr_smd;
|
|
int item = -1;
|
|
|
|
if (curr_smd != SMD_NAD) {
|
|
NAD_PRINT("store nad_result only at smd_nad\n");
|
|
return -EIO;
|
|
}
|
|
|
|
NAD_PRINT("buf : %s count : %d\n", buf, (int)count);
|
|
|
|
if (NAD_BUFF_SIZE * 3 < (int)count || (int)count < 4) {
|
|
NAD_PRINT("result cmd size too long : NAD_BUFF_SIZE<%d\n",
|
|
(int)count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Copy buf to nad temp */
|
|
strlcpy(temp, buf, NAD_BUFF_SIZE * 3);
|
|
string = temp;
|
|
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_test[0], nad_ptr, NAD_BUFF_SIZE * 2);
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_test[1], nad_ptr, NAD_BUFF_SIZE * 2);
|
|
|
|
sscanf(nad_test[0], "%s", test_name);
|
|
sscanf(nad_test[1], "%s", result_string);
|
|
|
|
NAD_PRINT("test_name : %s, test result=%s\n", test_name, result_string);
|
|
|
|
if (TEST_PASS(result_string))
|
|
_result = 2;
|
|
else if (TEST_FAIL(result_string))
|
|
_result = 1;
|
|
else
|
|
_result = 0;
|
|
|
|
// _results = 0(N/A), 1(FAIL), 2(PASS) from QNAD
|
|
|
|
if (TEST_CRYPTO(test_name))
|
|
item = CRYPTO;
|
|
else if (TEST_ICACHE(test_name))
|
|
item = ICACHE;
|
|
else if (TEST_CCOHERENCY(test_name))
|
|
item = CCOHERENCY;
|
|
else if (TEST_SUSPEND(test_name))
|
|
item = SUSPEND;
|
|
else if (TEST_VDDMIN(test_name))
|
|
item = VDDMIN;
|
|
else if (TEST_QMESADDR(test_name))
|
|
item = QMESADDR;
|
|
else if (TEST_QMESACACHE(test_name))
|
|
item = QMESACACHE;
|
|
else if (TEST_UFS(test_name))
|
|
item = UFS;
|
|
else if (TEST_DDR(test_name))
|
|
item = DDR;
|
|
else if (TEST_SENSOR(test_name))
|
|
item = SENSOR;
|
|
else
|
|
item = NOT_ASSIGN;
|
|
|
|
if (item == NOT_ASSIGN) {
|
|
pr_err("%s : fail - get test item in QNAD!! \n", __func__);
|
|
return count;
|
|
}
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
param_qnad_data.total_test_result |=
|
|
TEST_ITEM_RESULT(smd, item, _result);
|
|
|
|
NAD_PRINT("total_test_result=%u, smd=%d, item=%d, _result=%d\n",
|
|
param_qnad_data.total_test_result, smd, item, _result);
|
|
|
|
if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - set param!! param_qnad_data\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_nad_result(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
ssize_t info_size = 0;
|
|
int iCnt;
|
|
|
|
for (iCnt = 0; iCnt < ITEM_CNT; iCnt++) {
|
|
char strResult[NAD_BUFF_SIZE] = { '\0', };
|
|
|
|
if (!get_nad_result(strResult, iCnt))
|
|
goto err_out;
|
|
|
|
info_size +=
|
|
snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size,
|
|
"\"%s\":\"%s\",", STR_TEST_ITEM[iCnt], strResult);
|
|
}
|
|
|
|
pr_info("%s, result=%s\n", __func__, buf);
|
|
|
|
return info_size;
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "PARAM ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_result, S_IRUGO | S_IWUSR, show_nad_result,
|
|
store_nad_result);
|
|
|
|
static ssize_t store_nad_info(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
char info_name[NAD_BUFF_SIZE * 2] = { '\0', };
|
|
char temp[NAD_BUFF_SIZE * 3] = { '\0', };
|
|
char nad_test[2][NAD_BUFF_SIZE * 2]; // 2: "info_name", "result"
|
|
int resultValue;
|
|
char *nad_ptr, *string;
|
|
|
|
NAD_PRINT("buf : %s count : %d\n", buf, (int)count);
|
|
|
|
if (NAD_BUFF_SIZE * 3 < (int)count || (int)count < 4) {
|
|
NAD_PRINT("result cmd size too long : NAD_BUFF_SIZE<%d\n",
|
|
(int)count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Copy buf to nad temp */
|
|
strlcpy(temp, buf, NAD_BUFF_SIZE * 3);
|
|
string = temp;
|
|
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_test[0], nad_ptr, NAD_BUFF_SIZE * 2);
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_test[1], nad_ptr, NAD_BUFF_SIZE * 2);
|
|
|
|
sscanf(nad_test[0], "%s", info_name);
|
|
sscanf(nad_test[1], "%d", &resultValue);
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!strcmp("thermal", info_name))
|
|
param_qnad_data.thermal = resultValue;
|
|
else if (!strcmp("clock", info_name))
|
|
param_qnad_data.tested_clock = resultValue;
|
|
|
|
NAD_PRINT("info_name : %s, result=%d\n", info_name, resultValue);
|
|
|
|
if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - set param!! param_qnad_data\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_nad_info(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
ssize_t info_size = 0;
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
goto err_out;
|
|
}
|
|
|
|
info_size +=
|
|
snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size,
|
|
"\"REMAIN_CNT\":\"%d\",",
|
|
param_qnad_data.nad_remain_count);
|
|
info_size +=
|
|
snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size,
|
|
"\"THERMAL\":\"%d\",", param_qnad_data.thermal);
|
|
info_size +=
|
|
snprintf((char *)(buf + info_size), MAX_LEN_STR - info_size,
|
|
"\"CLOCK\":\"%d\",", param_qnad_data.tested_clock);
|
|
|
|
return info_size;
|
|
err_out:
|
|
return snprintf(buf, BUFF_SZ, "PARAM ERROR\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_info, S_IRUGO | S_IWUSR, show_nad_info, store_nad_info);
|
|
|
|
static ssize_t show_nad_version(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
#if 0
|
|
//QNAD_2.0.0_SS_09012017 - Trial version
|
|
return snprintf(buf, BUFF_SZ, "S845.0201.01.TR\n");
|
|
|
|
//QNAD_2.0.0_SS_10152017 - Offical version
|
|
return snprintf(buf, BUFF_SZ, "S845.0202.01.OF\n");
|
|
|
|
//QNAD_2.0.0_SS_11152017 - S/R, Storage added
|
|
return snprintf(buf, BUFF_SZ, "S845.0203.01.SRSTO\n");
|
|
|
|
//QNAD_2.0.0_SS_12152017 - QMESA Version up
|
|
return snprintf(buf, BUFF_SZ, "S845.0204.01.QM\n");
|
|
|
|
//QNAD_2.0.0_SS_12152017 - HMAC, AES parallel Disable
|
|
return snprintf(buf, BUFF_SZ, "S845.0204.02.HAESd\n");
|
|
|
|
//QNAD_2.0.0_SS_12152017 - HOT PLUG Enable
|
|
return snprintf(buf, BUFF_SZ, "S845.0204.03.HOTe\n");
|
|
|
|
//QNAD_2.0.0_SS_12152017 - SLPI Enable
|
|
return snprintf(buf, BUFF_SZ, "S845.0204.04.SLPIe\n");
|
|
|
|
//QNAD_2.0.1_SS_SLT_06142018_NS_GCM - CRYPTO_AES_GCM_ENABLE
|
|
return snprintf(buf, BUFF_SZ, "S845.0205.04.AESGCM\n");
|
|
#else
|
|
//QNAD_2.0.1_SS_SLT_06142018_NS_GCM - GFX_ENABLE_at_MAIN
|
|
return snprintf(buf, BUFF_SZ, "S845.0205.05.GFXMAIN\n");
|
|
#endif
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_version, 0444, show_nad_version, NULL);
|
|
|
|
static ssize_t store_nad_main(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int idx = 0;
|
|
int ret = -1;
|
|
char temp[NAD_BUFF_SIZE * 3];
|
|
char nad_cmd[NAD_MAIN_CMD_LIST][NAD_BUFF_SIZE];
|
|
char *nad_ptr, *string;
|
|
int running_time;
|
|
|
|
/* Copy buf to nad temp */
|
|
strlcpy(temp, buf, NAD_BUFF_SIZE * 3);
|
|
string = temp;
|
|
|
|
while (idx < NAD_MAIN_CMD_LIST) {
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE);
|
|
}
|
|
|
|
if (nad_test_mode == MAIN_NAD) {
|
|
NAD_PRINT("duplicated!\n");
|
|
return count;
|
|
}
|
|
|
|
if (!strncmp(buf, "start", 5)) {
|
|
ret = sscanf(nad_cmd[1], "%d\n", &running_time);
|
|
if (ret != 1)
|
|
return -EINVAL;
|
|
|
|
main_reboot = 1;
|
|
|
|
nad_test_mode = MAIN_NAD;
|
|
|
|
if (!sec_get_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - get param!! param_qnad_data\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
param_qnad_data.ddrtest_mode = UPLOAD_CAUSE_DDR_TEST_FOR_MAIN;
|
|
param_qnad_data.ddrtest_remain_count = 1;
|
|
param_qnad_data.nad_remain_count = 0;
|
|
param_qnad_data.total_test_result &= 0xffffffff;
|
|
|
|
if (!sec_set_param(param_index_qnad, ¶m_qnad_data)) {
|
|
pr_err("%s : fail - set param!! param_qnad_data\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
do_nad(MAIN_NAD);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_nad_main(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int nad_result;
|
|
|
|
nad_result = NadResult(MAIN_NAD);
|
|
switch (nad_result) {
|
|
case TOTALTEST_PASS: {
|
|
NAD_PRINT("MAIN NAD Passed\n");
|
|
return snprintf(buf, BUFF_SZ, "OK_2.0\n");
|
|
} break;
|
|
|
|
case TOTALTEST_FAIL: {
|
|
NAD_PRINT("MAIN NAD fail\n");
|
|
return snprintf(buf, BUFF_SZ, "NG_2.0_FAIL\n");
|
|
} break;
|
|
|
|
default: {
|
|
NAD_PRINT("MAIN NAD No Run\n");
|
|
return snprintf(buf, BUFF_SZ, "OK\n");
|
|
}
|
|
}
|
|
|
|
return snprintf(buf, BUFF_SZ, "MAIN NAD UNKNOWN\n");
|
|
}
|
|
|
|
static DEVICE_ATTR(balancer, S_IRUGO | S_IWUSR, show_nad_main, store_nad_main);
|
|
|
|
static ssize_t show_nad_main_timeout(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, BUFF_SZ, "%d\n", NAD_MAIN_TIMEOUT);
|
|
}
|
|
|
|
static DEVICE_ATTR(timeout, 0444, show_nad_main_timeout, NULL);
|
|
|
|
|
|
enum nad_qdaf_action_t {
|
|
NAD_QDAF_ACTION_EMPTY = 0,
|
|
|
|
/*==== from AtNadCheck.java to qdaf.sh ====*/
|
|
NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC = 1,
|
|
NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC = 2,
|
|
NAD_QDAF_ACTION_CONTROL_STOP = 3,
|
|
/*==== from qnad.sh to qdaf.sh ====*/
|
|
NAD_QDAF_ACTION_CONTROL_STOP_WATING_FOR_QNAD = 4,
|
|
|
|
/*==== from AtNadCheck.java to qdaf.sh ====*/
|
|
NAD_QDAF_ACTION_RESULT_ERASE = 5,
|
|
NAD_QDAF_ACTION_RESULT_GET = 6,
|
|
|
|
/*==== from qdaf.sh ====*/
|
|
NAD_QDAF_ACTION_DEBUG_SAVE_LOGS = 7,
|
|
NAD_QDAF_ACTION_DEBUG_TRIGGER_PANIC = 8,
|
|
};
|
|
|
|
enum nad_qdaf_result_string_t {
|
|
NAD_QDAF_RESULT_OK = 0,
|
|
NAD_QDAF_RESULT_NG = 1,
|
|
NAD_QDAF_RESULT_NONE = 2,
|
|
};
|
|
|
|
#define QDAF_PROG "/system/bin/qnad/qdaf.sh"
|
|
#define QDAF_QMESA_LOG "/data/log/qdaf_qmesa_log.txt"
|
|
|
|
static int qdaf_cur_cmd_mode;
|
|
|
|
static ssize_t show_nad_qdaf_control(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
if( qdaf_cur_cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC ||
|
|
qdaf_cur_cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC )
|
|
return snprintf(buf, BUFF_SZ, "RUNNING\n");
|
|
|
|
return snprintf(buf, BUFF_SZ, "READY\n");
|
|
}
|
|
static ssize_t store_nad_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[NAD_BUFF_SIZE * 3];
|
|
char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE];
|
|
char *nad_ptr, *string;
|
|
int ret_userapp;
|
|
char *argv[4] = { NULL, NULL, NULL, NULL };
|
|
char *envp[5] = {
|
|
"HOME=/",
|
|
"PATH=/system/bin/qnad:/system/bin:/system/xbin",
|
|
"ANDROID_DATA=/data",
|
|
"ANDROID_ROOT=/system",
|
|
NULL };
|
|
|
|
NAD_PRINT("%s : is called\n",__func__);
|
|
|
|
if ((int)count < NAD_BUFF_SIZE) {
|
|
NAD_PRINT("%s : return error (count=%d)\n", __func__, (int)count);
|
|
return -EINVAL;;
|
|
}
|
|
|
|
if (strncmp(buf, "nad_qdaf_control", 16)) {
|
|
NAD_PRINT("%s : return error (buf=%s)\n", __func__, buf);
|
|
return -EINVAL;;
|
|
}
|
|
|
|
strlcpy(temp, buf, NAD_BUFF_SIZE * 3);
|
|
string = temp;
|
|
while (idx < NAD_CMD_LIST) {
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE);
|
|
}
|
|
sscanf(nad_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==NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC ||
|
|
cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC) {
|
|
NAD_PRINT("%s : return because qdaf.sh already has been running.\n", __func__);
|
|
}else if( cmd_mode==NAD_QDAF_ACTION_CONTROL_STOP ||
|
|
cmd_mode==NAD_QDAF_ACTION_CONTROL_STOP_WATING_FOR_QNAD) {
|
|
NAD_PRINT("%s : return because qdaf.sh has not been running yet.\n", __func__);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// if receiving control command from AtNadCheck when qnad is running, let's return NG
|
|
// if receiving control command from qnad.sh, invoke qdaf.sh to stop qdaf with UMH_WAIT_PROC
|
|
if( nad_test_mode!=-1 &&
|
|
(cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC ||
|
|
cmd_mode==NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC ||
|
|
cmd_mode==NAD_QDAF_ACTION_CONTROL_STOP) ) {
|
|
NAD_PRINT("%s : return because qnad is running.\n", __func__);
|
|
return count;
|
|
}
|
|
|
|
argv[0] = QDAF_PROG;
|
|
argv[1] = nad_cmd[1];
|
|
switch (cmd_mode) {
|
|
case NAD_QDAF_ACTION_CONTROL_START_WITHOUT_PANIC :
|
|
NAD_PRINT("%s : qdaf will be started (without panic)\n",__func__);
|
|
wait_val = UMH_WAIT_EXEC;
|
|
break;
|
|
case NAD_QDAF_ACTION_CONTROL_START_WITH_PANIC:
|
|
NAD_PRINT("%s : qdaf will be started (with panic)\n",__func__);
|
|
wait_val = UMH_WAIT_EXEC;
|
|
break;
|
|
case NAD_QDAF_ACTION_CONTROL_STOP :
|
|
NAD_PRINT("%s : qdaf will be stopped\n",__func__);
|
|
wait_val = UMH_WAIT_EXEC;
|
|
break;
|
|
case NAD_QDAF_ACTION_CONTROL_STOP_WATING_FOR_QNAD :
|
|
NAD_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 :
|
|
NAD_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode);
|
|
return count;
|
|
}
|
|
|
|
ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val);
|
|
if (!ret_userapp) {
|
|
qdaf_cur_cmd_mode = cmd_mode;
|
|
NAD_PRINT("%s : succeded to trigger qdaf.sh\n", __func__);
|
|
NAD_PRINT("%s : qdaf_cur_cmd_mode=%s\n", __func__, nad_cmd[1]);
|
|
}else {
|
|
qdaf_cur_cmd_mode = NAD_QDAF_ACTION_EMPTY;
|
|
// evaulate return value after ret_userapp>>8
|
|
NAD_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp);
|
|
}
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR(nad_qdaf_control, S_IRUGO | S_IWUSR, show_nad_qdaf_control, store_nad_qdaf_control);
|
|
|
|
/*
|
|
static int qdaf_check_prev_result(void)
|
|
{
|
|
int fd = 0;
|
|
int ret;
|
|
char buf[512] = { '\0', };
|
|
mm_segment_t old_fs = get_fs();
|
|
|
|
set_fs(KERNEL_DS);
|
|
|
|
fd = sys_open(QDAF_QMESA_LOG, O_RDONLY, 0);
|
|
if (fd >= 0) {
|
|
int found = 0;
|
|
|
|
printk(KERN_DEBUG);
|
|
|
|
while (sys_read(fd, buf, 512)) {
|
|
char *ptr;
|
|
char *div = "\n";
|
|
char *tok = NULL;
|
|
|
|
ptr = buf;
|
|
while ((tok = strsep(&ptr, div)) != NULL) {
|
|
if ((strstr(tok, "failure"))) {
|
|
ret = NAD_QDAF_TEST_RESULT_NG;
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found) break;
|
|
}
|
|
|
|
if (!found) ret = NAD_QDAF_TEST_RESULT_OK;
|
|
sys_close(fd);
|
|
} else {
|
|
NAD_PRINT("%s : result file is not existed\n", __func__);
|
|
ret = NAD_QDAF_TEST_RESULT_NONE;
|
|
}
|
|
|
|
set_fs(old_fs);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t show_nad_qdaf_prev_result(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int qdaf_result = qdaf_check_prev_result();
|
|
switch (qdaf_result) {
|
|
case NAD_QDAF_TEST_RESULT_OK:
|
|
NAD_PRINT("%s : previous test result : pass\n", __func__);
|
|
return snprintf(buf, BUFF_SZ, "OK\n");
|
|
break;
|
|
case NAD_QDAF_TEST_RESULT_NG:
|
|
NAD_PRINT("%s : previous test result : fail\n", __func__);
|
|
return snprintf(buf, BUFF_SZ, "NG\n");
|
|
break;
|
|
default:
|
|
NAD_PRINT("%s : previous test result : unknown\n", __func__);
|
|
return snprintf(buf, BUFF_SZ, "NONE\n");
|
|
}
|
|
|
|
return snprintf(buf, BUFF_SZ, "NONE\n");
|
|
|
|
}
|
|
*/
|
|
|
|
static ssize_t show_nad_qdaf_result(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int ret_userapp, wait_val=0, failed_cnt=0;
|
|
char act[NAD_BUFF_SIZE]={0,};
|
|
char *argv[4] = { NULL, NULL, NULL, NULL };
|
|
char *envp[5] = {
|
|
"HOME=/",
|
|
"PATH=/system/bin/qnad:/system/bin:/system/xbin",
|
|
"ANDROID_DATA=/data",
|
|
"ANDROID_ROOT=/system",
|
|
NULL };
|
|
|
|
NAD_PRINT("%s : is called\n",__func__);
|
|
|
|
// refer to qpnp_pon_reason (index=boot_reason-1)
|
|
NAD_PRINT("%s : boot_reason was %d\n", __func__, boot_reason);
|
|
|
|
snprintf(act, 1, "%d", NAD_QDAF_ACTION_RESULT_GET);
|
|
|
|
argv[0] = QDAF_PROG;
|
|
// TODO : conversion int->string
|
|
argv[1] = "6"; //NAD_QDAF_ACTION_RESULT_GET
|
|
wait_val = UMH_WAIT_PROC;
|
|
ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val);
|
|
if (!ret_userapp) {
|
|
NAD_PRINT("%s : succeded to trigger qdaf.sh and failed_cnt=0\n", __func__);
|
|
return snprintf(buf, BUFF_SZ, "OK\n");
|
|
}else if (ret_userapp > 0 ) {
|
|
// 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
|
|
failed_cnt = ret_userapp>>8;
|
|
NAD_PRINT("%s : succeded to trigger qdaf.sh and return_value=%d(failed_cnt=%d)\n",
|
|
__func__, ret_userapp, failed_cnt);
|
|
return snprintf(buf, BUFF_SZ, "NG,%d\n", failed_cnt);
|
|
}else {
|
|
// evaulate return value after ret_userapp>>8
|
|
NAD_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp);
|
|
return snprintf(buf, BUFF_SZ, "NONE\n");
|
|
}
|
|
|
|
return snprintf(buf, BUFF_SZ, "NONE\n");
|
|
}
|
|
|
|
static ssize_t store_nad_qdaf_result(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int idx = 0, cmd_mode=0, wait_val=0;
|
|
int ret_userapp;
|
|
char temp[NAD_BUFF_SIZE * 3];
|
|
char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE];
|
|
char *nad_ptr, *string;
|
|
char *argv[4] = { NULL, NULL, NULL, NULL };
|
|
char *envp[5] = {
|
|
"HOME=/",
|
|
"PATH=/system/bin/qnad:/system/bin:/system/xbin",
|
|
"ANDROID_DATA=/data",
|
|
"ANDROID_ROOT=/system",
|
|
NULL };
|
|
|
|
NAD_PRINT("%s : is called\n",__func__);
|
|
|
|
if ((int)count < NAD_BUFF_SIZE) {
|
|
NAD_PRINT("%s : return error (count=%d)\n", __func__, (int)count);
|
|
return -EINVAL;;
|
|
}
|
|
|
|
if (strncmp(buf, "nad_qdaf_result", 15)) {
|
|
NAD_PRINT("%s : return error (buf=%s)\n", __func__, buf);
|
|
return -EINVAL;;
|
|
}
|
|
|
|
strlcpy(temp, buf, NAD_BUFF_SIZE * 3);
|
|
string = temp;
|
|
while (idx < NAD_CMD_LIST) {
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE);
|
|
}
|
|
sscanf(nad_cmd[1], "%d", &cmd_mode);
|
|
|
|
argv[0] = QDAF_PROG;
|
|
argv[1] = nad_cmd[1];
|
|
switch (cmd_mode) {
|
|
case NAD_QDAF_ACTION_RESULT_ERASE :
|
|
NAD_PRINT("%s : qdaf will erase failed count\n",__func__);
|
|
wait_val = UMH_WAIT_PROC;
|
|
break;
|
|
default :
|
|
NAD_PRINT("%s : return because invalid cmd mode(%d)\n", __func__, cmd_mode);
|
|
return count;
|
|
}
|
|
|
|
ret_userapp = call_usermodehelper(argv[0], argv, envp, wait_val);
|
|
if (!ret_userapp)
|
|
NAD_PRINT("%s : succeded to trigger qdaf.sh\n", __func__);
|
|
else {
|
|
// evaulate return value after ret_userapp>>8
|
|
NAD_PRINT("%s : failed to trigger qdaf.sh. error=%d\n", __func__, ret_userapp);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(nad_qdaf_result, S_IRUGO | S_IWUSR, show_nad_qdaf_result, store_nad_qdaf_result);
|
|
|
|
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);
|
|
|
|
fd = sys_open(QDAF_QMESA_LOG, O_RDONLY, 0);
|
|
if (fd >= 0) {
|
|
while (sys_read(fd, temp, 1) == 1) {
|
|
buf[idx++] = temp[0];
|
|
if( temp[0]=='\n' ) {
|
|
buf[idx] = '\0';
|
|
NAD_PRINT("%s", buf);
|
|
idx = 0;
|
|
}
|
|
}
|
|
sys_close(fd);
|
|
}
|
|
set_fs(old_fs);
|
|
}
|
|
|
|
static ssize_t store_nad_qdaf_debug(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int idx = 0, cmd_mode=0;
|
|
char temp[NAD_BUFF_SIZE * 3];
|
|
char nad_cmd[NAD_CMD_LIST][NAD_BUFF_SIZE];
|
|
char *nad_ptr, *string;
|
|
|
|
NAD_PRINT("%s : is called\n",__func__);
|
|
|
|
if ((int)count < NAD_BUFF_SIZE) {
|
|
NAD_PRINT("%s : return error (count=%d)\n", __func__, (int)count);
|
|
return -EINVAL;;
|
|
}
|
|
|
|
if (strncmp(buf, "nad_qdaf_debug", 14)) {
|
|
NAD_PRINT("%s : return error (buf=%s)\n", __func__, buf);
|
|
return -EINVAL;;
|
|
}
|
|
|
|
strlcpy(temp, buf, NAD_BUFF_SIZE * 3);
|
|
string = temp;
|
|
while (idx < NAD_CMD_LIST) {
|
|
nad_ptr = strsep(&string, ",");
|
|
strlcpy(nad_cmd[idx++], nad_ptr, NAD_BUFF_SIZE);
|
|
}
|
|
sscanf(nad_cmd[1], "%d", &cmd_mode);
|
|
|
|
switch (cmd_mode) {
|
|
case NAD_QDAF_ACTION_DEBUG_SAVE_LOGS :
|
|
NAD_PRINT("%s : qdaf will save log into kmsg\n",__func__);
|
|
qdaf_save_logs();
|
|
return count;
|
|
case NAD_QDAF_ACTION_DEBUG_TRIGGER_PANIC :
|
|
NAD_PRINT("%s : will trigger panic\n",__func__);
|
|
panic("qdaf_fail");
|
|
return count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR(nad_qdaf_debug, S_IWUSR, NULL, store_nad_qdaf_debug);
|
|
|
|
|
|
|
|
static int __init sec_nad_init(void)
|
|
{
|
|
int ret = 0;
|
|
struct device *sec_nad;
|
|
struct device *sec_nad_balancer;
|
|
|
|
NAD_PRINT("%s\n", __func__);
|
|
|
|
/* Skip nad init when device goes to lp charging */
|
|
if (lpcharge)
|
|
return ret;
|
|
|
|
sec_nad = sec_device_create(0, NULL, "sec_nad");
|
|
|
|
if (IS_ERR(sec_nad)) {
|
|
pr_err("%s Failed to create device(sec_nad)!\n", __func__);
|
|
return PTR_ERR(sec_nad);
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_stat);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_ddrtest_remain_count);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_qmvs_remain_count);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_erase);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_acat);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_dram);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_support);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_logs);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_end);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_dram_debug);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_dram_err_addr);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_result);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_api);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_info);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_version);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad, &dev_attr_nad_qdaf_control);
|
|
if (ret) {
|
|
pr_err("%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) {
|
|
pr_err("%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) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
if (add_uevent_var(&nad_uevent, "NAD_TEST=%s", "DONE")) {
|
|
pr_err("%s : uevent NAD_TEST_AND_PASS is failed to add\n",
|
|
__func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
sec_nad_balancer = sec_device_create(0, NULL, "sec_nad_balancer");
|
|
|
|
if (IS_ERR(sec_nad)) {
|
|
pr_err("%s Failed to create device(sec_nad)!\n", __func__);
|
|
return PTR_ERR(sec_nad);
|
|
}
|
|
|
|
ret = device_create_file(sec_nad_balancer, &dev_attr_balancer);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
ret = device_create_file(sec_nad_balancer, &dev_attr_timeout);
|
|
if (ret) {
|
|
pr_err("%s: Failed to create device file\n", __func__);
|
|
goto err_create_nad_sysfs;
|
|
}
|
|
|
|
return 0;
|
|
err_create_nad_sysfs:
|
|
return ret;
|
|
}
|
|
|
|
module_init(sec_nad_init);
|