blob: 9ac3eea587ad8e4518aeefa886bd1e85c1337d5c [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2008 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
/*--------------------------------------------------------------------------------------------------------------------
* -------------------------------------------------------------------------------------------------------------------
*
* Filename: eeh_api.c
*
* Description: The APIs to process error & exception of system.
*
* History:
* April, 2008 - Rovin Yu Creation of file
*
* Notes:
*
******************************************************************************/
/******************************************************************************
* Include files
******************************************************************************/
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <cutils/properties.h>
#if defined (BIONIC)
#include <hardware_legacy/power.h>
#include <sys/reboot.h> // reboot, RB_AUTOBOOT
#include <cutils/properties.h>
#endif
#include "pxa_dbg.h"
#include "EE_Postmortem.h"
#include "EEHandler.h"
#include "EEHandler_int.h"
#include "EEHandler_config.h"
#include "eeh.h"
#include "eeh_assert_notify.h"
#include "msocket_api.h"
#include "cploader.h"
#include "loadTable.h"
#include "utilities.h"
#include "pxa9xx_cp.h"
// Run-time "sm.logLevel" depends upon
// {eehCfg.logLevel, eehCfg.cpSR_logLevel, sm.cpSR_cntr, eehCfg.cpSR_max}
// where "arc"/"arcive" is ZIP/archive into sd/modem_dump/**.gz
// Refer "eeh_HowToConfig.txt
typedef enum {
EEH_LOGLVL_0 = 0, //Don't save any log.bin (but eeh_journal.txt record only)
EEH_LOGLVL_1_ARC2SD = 1, //Save com_EEH_Buf.bin only on NVM, arc to SDcard
EEH_LOGLVL_2_CPSR = 2, //Save com_EEH_Buf.bin, LogStreamS, dmesg.txt
EEH_LOGLVL_3_CPSR_ARC2SD = 3, //Save com_EEH_Buf.bin, LogStreamS, dmesg.txt (arc to SDcard)
EEH_LOGLVL_4_RDP = 4, //Save com_EEH_Buf.bin, LogStream and SPY-Dumps files on NVM
EEH_LOGLVL_5_RDP_ARC2SD = 5, //Save com_EEH_Buf.bin, LogStream and SPY-Dumps (arc to SDcard)
EEH_LOGLVL_6_DBG = 6, //Save all possible files on NVM+SDcard and STALL (no auto-RAMDUMP)
EEH_LOGLVL_7_DBG_ARC2SD = 7, //Save all possible files on NVM+SDcard and STALL (no auto-RAMDUMP)
EEH_LOGLVL_8_LOG2SD = 8, // All to sd/log directory, rename to sd/log_NNN_DATE (no archive) (takes only ~25sec for all-all logs present on SD)
EEH_LOGLVL_9_LOG2SD_ARC2SD = 9, //All to sd/log directory, arc all but DDR_RW to sd/modem_dump/
EEH_LOGLVL_SIZE
}eehLogLvl_e;
#define EEH_LOGLVL_WITH_ARC2SD(level) (level & 1)
/* Define the timers used by EEH */
#define CPSR_RECOVERY_TO_SEC 30/*increase from 4sec*/
typedef enum {
timerStopped = 0,
CPSilentResetTimerId, //CPSR_RECOVERY_TO_SEC
eehGuardTimerId, //see guardTO[]
cpAssertReqTimerId,
maxTimerId
} eehTmrRun_e;
typedef struct {
EehAssertType inProgress; //eeh_ioctl.h
eehTmrRun_e currentTimerId;
char hawk_enable;
char *hawk_buf;
unsigned cpSR_cntr;
char cpRestartReq;
int logLevel;
char *TimeOutPanicDesc;
int ramdump_level;
char CPASSERT_flag;
char wifiSR_cntr;
} eehStateMachine_s;
static eehStateMachine_s sm;
//eehGuardTimerId: depending upon Log-Level and logging latency
static const unsigned short guardTO[EEH_LOGLVL_SIZE] =
/* level 0 1 2 3 4 5 6 7 8 9 */
/* seconds */ {7, 7, 31, 31, 160, 160, 253, 253 , 31, 160}; //value=0 ~ no timer
void * eeh_mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
int eeh_munmap (void *addr, size_t length);
/***************************************************
* Device drivers used by Error-Handler:
* /dev/kmsg - ERRMSG and trace messages (EEHandler_config.h)
* /dev/seh - seh.ko private EEH driver
* /dev/ramfile - part of seh.ko saving files as ramfile
* /dev/ramdump_ctl - Panic Emulation driver
* /proc/sys/kernel/print-fatal-signals
*/
int EEH_ERRMSG_fd = -1;
int sehdrv_fd = -1;
#define EEH_DEVICE "/dev/seh"
#define EEH_PANIC_DEV "/dev/ramdump_ctl"
#define EEH_RAMFILE_DEV "/dev/ramfile"
//#define EEH_RAMFILE_DBG /* force even on "STALL" + traces */
#ifdef BIONIC
static char* DIAG_FS_MOUNT_POINT = "/data/acat";
#endif
void EehPrint(const char *msg)
{
ERRMSG("%s\n", msg);
DIAG_FILTER(Diag, comMem, Final, DIAG_INFORMATION)
diagPrintf("%s", msg);
}
extern unsigned long PRE_LOADTABLE_PHYADDR;
//static void eehDontSleep(void) { acquire_wake_lock(PARTIAL_WAKE_LOCK, "eeh"); }
//static void eehCouldSleep(void) { release_wake_lock("eeh"); }
static void eehNoSleepLong(void) { ioctl(sehdrv_fd, SEH_IOCTL_DONT_SLEEP, NULL); }
static void eehNoSleepShort(void) { ioctl(sehdrv_fd, SEH_IOCTL_SLEEP_ALLOWED, NULL); }
void eeh_CPASSERT_flag(int set)
{
sm.CPASSERT_flag = set;
}
/*static*/void eeh_clear_fs_cache(int with_sync)
{
FILE *fd;
if (with_sync)
sync();
fd = fopen("/proc/sys/vm/drop_caches", "w");
if (fd) {
fprintf(fd, "3") ;
fclose(fd);
}
}
static void eeh_kill_for_memory(void)
{
#define EEH_AGRESSIVE_PUSH_RAMFILE
#ifdef EEH_AGRESSIVE_PUSH_RAMFILE
// Call this only when going into Final RAMDUMP reset...
static int kill_done;
if (!kill_done) {
kill_done = 1;
system("killall -9 ota charger"); /*about 500kB */
}
#endif
}
/**** "Conditional" HAWK_ENABLED procedures *******************************
* HAWK is an agent reporting about valuable events (including crashes)
* The old name is REM.
* In case of crash a report sent after Silent-Recovery or system restart
* EEH sends reports over eeh_hawk.c::eeh_to_hawk_indication()
* Wrap with macro to minimize #ifdef
*/
#ifndef HAWK_ENABLED
#define HAWK_indication_txt(TXT)
#else
#define HAWK_indication_txt(TXT) if(sm.hawk_enable) eeh_to_hawk_indication(TXT)
#define HAWK_indication_ext(SZ, TYPE, INITIATOR, inBUF) \
if (sm.hawk_enable) { \
char outBuf[SZ]; \
snprintf(outBuf, SZ, TYPE, INITIATOR, inBUF); \
eeh_to_hawk_indication(outBuf); \
}
/*HAWK_indication_ext:
( 200, EEH2HAWK_ASSERT_DETECTED, HAWK_INITIATOR_COMM,eeBuffer);
(1024, EEH2HAWK_VAL_EVENT_DETECTED,HAWK_INITIATOR_TEL,eeString);
( 200, EEH2HAWK_CRASH_DETECTED, HAWK_INITIATOR_TEL,eeString);
*/
#endif //HAWK_ENABLED
/**** "Conditional" EEH_DUMP_ENHANCE ********************************
* Archiving into SD-card all Com/Modem files
*/
#ifndef EEH_DUMP_ENHANCE
static void EEH_DUMP_ENHANCE_draw_panic(char *eeString) { }
static void EEH_DUMP_ENHANCE_record_archive_files(char *eeBuffer){ }
static void EEH_DUMP_ENHANCE_reset_archive_files(void) { }
#else
void EEH_DUMP_ENHANCE_draw_panic(char *eeString);
void EEH_DUMP_ENHANCE_record_archive_files(char *eeBuffer);
void EEH_DUMP_ENHANCE_reset_archive_files(void);
#endif//EEH_DUMP_ENHANCE
/*************************************************/
EEH_STATUS EehInsertCommtoReset(void)
{
EehApiParams ApiParams;
INT32 ret;
ApiParams.eehApiId = _EehInsertComm2Reset;
ApiParams.params = NULL;
EehPrint("Eeh Insert Comm to Reset");
ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams);
if (ret) {
ERRMSG("%s: error=%d\n", __FUNCTION__, errno);
return EEH_ERROR;
}
return ApiParams.status;
}
/************************************************************
* EehChangeResetState
* change the process in crash list no need to reset
************************************************************/
EEH_STATUS EehChangeResetState(void)
{
EehApiParams ApiParams;
INT32 ret;
ApiParams.eehApiId = _EehChangeResetState;
ApiParams.params = NULL;
EehPrint("Eeh Change Reset State");
ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams);
if (ret) {
ERRMSG("%s: error=%d\n", __FUNCTION__, errno);
return EEH_ERROR;
}
return ApiParams.status;
}
/*********************************************************/
EEH_STATUS EehReleaseCommFromReset(void)
{
EehApiParams ApiParams;
INT32 ret;
ApiParams.eehApiId = _EehReleaseCommFromReset;
ApiParams.params = NULL;
EehPrint("Eeh Release Comm From Reset");
ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams);
if (ret) {
ERRMSG("%s: error=%d\n", __FUNCTION__, errno);
return EEH_ERROR;
}
return ApiParams.status;
}
/***************************************************************
* SaveSilentResetStatistics
* Creates log-file with silent reset statistics.
*
* NOTE: to be obsolet. Everything needed is in eeh_journal.txt
***************************************************************/
void SaveSilentResetStatistics(UINT32 msgId)
{
UNUSEDPARAM(msgId)
FILE *fd;
char fullPath[MAX_PATH] = { 0 };
char fileName[] = "SilentResetStatistic.log";
char *nvm_root_dir = eehCfg.logDir;
time_t timep;
if (nvm_root_dir != NULL)
{
strlcpy(fullPath, nvm_root_dir, sizeof(fullPath));
strlcat(fullPath, "/", sizeof(fullPath));
}
strlcat(fullPath, fileName, sizeof(fullPath));
if ((fd = fopen(fullPath, "aw")) != 0)
{
time(&timep);
fprintf(fd, "CPP ERROR causes silent reset No.%d : %s", sm.cpSR_cntr, ctime(&timep));
fclose(fd);
}
}
#define RD_SIZE 1*1024*1024
/*************************************************
* EehCopyCommImageFromFlash
* copy Comm image from flash to DDR.
**************************************************/
EEH_STATUS EehCopyCommImageFromFlash(void)
{
struct cp_load_table_head *hdr;
const char *errmsg = "*** CPSR failed to load CP images ***";
int ret;
EehPrint("Copy Comm Image From Flash to DDR");
/* -1, -1 means have not prepared the current & exist cp property values,
* it means need to get these two property values from property_get */
if((hdr = cp_load_table_init()) == NULL)
return EEH_ERROR;
ret = set_cp_type_property(hdr);
if(ret == -1)
{
cp_load_table_exit(hdr);
ERRMSG("%s (err1)\n", errmsg);
return EEH_ERROR;
}
/* MSA must be loaded before RF */
//coverity[tainted_data: SUPPRESS]
if(cp_load_arbel_image(hdr, 0) == -1 ||
#if defined(CONFIG_ASR1901_EEH) || defined(CONFIG_ASR1903_EEH)
cp_load_bx2_image(hdr, 0) == -1 ||
#endif
#ifndef CONFIG_ASR1903_EEH
cp_load_msa_image(hdr, 0) == -1 ||
#endif
cp_load_rf_image(hdr, 0) == -1 ||
cp_set_cp_addr(hdr, 0) == -1)
{
cp_load_table_exit(hdr);
ERRMSG("%s (err2)\n", errmsg);
return EEH_ERROR;
}
//coverity[tainted_data: SUPPRESS]
if (cp_load_reliable_data(hdr, 0) < 0)
{
cp_load_table_exit(hdr);
return EEH_ERROR;
}
if (cp_load_mep(hdr, 0) < 0)
{
cp_load_table_exit(hdr);
return EEH_ERROR;
}
cp_load_table_exit(hdr);
return EEH_NO_ERROR;
}
//----------------------------------------------------------
static int eeh_log_dir_name_generate(char *buf, int len, int re_gen)
{
static struct tm tm;
time_t ttm;
int number = 0;
if (!buf) //reset request
tm.tm_mon = 0;
if (re_gen) {
if (!tm.tm_mon)
return -1;
} else {
time(&ttm);
gmtime_r(&ttm, &tm);
}
// sd/log_000_mm-dd_hh.mm.ss
if (buf)
snprintf(buf, len, "%slog_%03d_%02d-%02d_%02d.%02d.%02d",
SDCARD_MOUNT, number, 1 + tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
return 0;
}
/*--------------------- final=RAMDUMP utilities -------------------
* EehRamdump - main API including:
* open(EEH_RAMFILE_DEV)
* EehRamdumpPushLogfiles by the EehPushRamfile
* EehRamdumpActivate - panic emulation (called by EehRamdump
* or by guard-timer-expiry)
*-----------------------------------------------------------------
*/
#define RAMFILE_HEADER_SIZE 32 /* should match kernel definition for struct ramfile_desc*/
#define RAMFILE_MAX_LEN 0x400000
static int EehPushRamfile(int ramfdev, const char* file)
{
char *vpa = 0;
FILE* ffd = 0;
int len, l;
struct stat fst;
int ret = -1;
if ((ffd=fopen(file, "rb"))==NULL) {
ERRMSG("RAMFILE: cannot open %s\n", file);
goto bail;
}
if (fstat(fileno(ffd), &fst)) {
ERRMSG("RAMFILE: cannot stat %s\n", file);
goto bail;
}
len = fst.st_size;
if ((len<=0) || (len>RAMFILE_MAX_LEN)) {
ERRMSG("RAMFILE: bad length %d file %s\n", len, file);
goto bail;
}
eeh_clear_fs_cache(0);
if ((vpa=(char *)mmap(0, len + RAMFILE_HEADER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, ramfdev, 0))==MAP_FAILED) {
ERRMSG("RAMFILE: failed to allocate %d bytes for file %s\n", len, file);
goto bail;
}
if ((l = fread((void*)(vpa+RAMFILE_HEADER_SIZE), 1, len, ffd)) != len) {
ERRMSG("RAMFILE: failed to read %d bytes from %s, ret=%d\n", len, file, l);
goto bail;
}
#ifdef EEH_RAMFILE_DBG
ERRMSG("RAMFILE: pushed %d bytes from %s to vpa=%.8x+%d\n",
len, file, vpa, RAMFILE_HEADER_SIZE);
#endif
/* OK, data is in memory */
ret = 0;
bail:
if (ffd)
fclose(ffd);
if (vpa)
munmap(vpa, len);
return ret;
}
#if 1
static int EehRamdumpPushLogfiles(int ramfdev)
{
static int ramfile_do_once;
char *ramfiletmp;
int max = 1024;
char command_buf[max];
FILE *lfd;
int ret, len, i, sz, sd_available, fcntr = 0;
char **p;
if (ramfile_do_once)
return 0; //already done
//file is big. Try to create it on SD instead of flash
sd_available = eeh_sd_card_available(1);
if (sd_available)
ramfiletmp = LOG_DIR_SD_RDP "ramfiletmp.tgz";
else
ramfiletmp = LOG_DIR "ramfiletmp.tgz";
unlink(ramfiletmp);
// If log dir on sd has been used we need to chdir into there
if (sd_available && (sm.logLevel == EEH_LOGLVL_8_LOG2SD) &&
!eeh_log_dir_name_generate(command_buf, max, 1)) {
chdir(command_buf);
}
//Already in LOG_DIR, no chdir() required
/* Note: cvfz that works for standard tar does not gzip in busybox implementation, m.b. because of -f: use cvz */
len = sprintf(command_buf, "tar cvz -f %s", ramfiletmp);
//Create/add loglist from array but only with existing files
//----------------------------------------------------------
p = (char**)ramdump_loglist_ap;
sz = ramdump_loglist_ap_sz;
for (i = 0; i < sz; i++) {
if (access(p[i], F_OK) == 0) {
len += snprintf(&command_buf[len], max-len, " %s", p[i]);
fcntr++;
}
}
p = (char**)ramdump_loglist_cp;
sz = ramdump_loglist_cp_sz;
for (i = 0; i < sz; i++) {
if (access(p[i], F_OK) == 0) {
len += snprintf(&command_buf[len], max-len, " %s", p[i]);
fcntr++;
}
}
p = (char**)ramdump_loglist_sdl;
sz = ramdump_loglist_sdl_sz;
for (i = 0; i < sz; i++) {
if (access(p[i], F_OK) == 0) {
len += snprintf(&command_buf[len], max-len, " %s", p[i]);
fcntr++;
}
}
if (!fcntr)
return -1; //nothing to save
command_buf[max-1] = 0;
// EXECUTE created command/loglist
ret = system(command_buf); // continue even is not OK
#ifdef EEH_RAMFILE_DBG
ERRMSG("RAMFILE-0: %d = %s\n", ret, command_buf);
#endif
eeh_kill_for_memory();
ret = EehPushRamfile(ramfdev, ramfiletmp);
if (!sd_available)
unlink(ramfiletmp); //remove from flash, but keep on sd
// ---- Repeat for BIG file(s) ------
len = sprintf(command_buf, "tar cvz -f %s", ramfiletmp);
fcntr = 0;
//do big files last since memory no guaranteed
p = (char**)ramdump_loglist_cp_big;
sz = ramdump_loglist_cp_big_sz;
for (i = 0; i < sz; i++) {
if (access(p[i], F_OK) == 0) {
len += snprintf(&command_buf[len], max-len, " %s", p[i]);
fcntr++;
}
}
if (!fcntr)
goto exit;
command_buf[max-1] = 0;
// EXECUTE created command/loglist
/*ret =*/ system(command_buf); // continue even is not OK
#ifdef EEH_RAMFILE_DBG
ERRMSG("RAMFILE-1: %s\n", command_buf);
#endif
/*ret =*/ EehPushRamfile(ramfdev, ramfiletmp);
if (!sd_available)
unlink(ramfiletmp); //remove from flash, but keep on sd
exit:
ramfile_do_once = !!(!ret);
return ret;
}
#else//version_0
#define RAMFILE_TMP_ARCHIVE "ramfiletmp.tar" /* temp ramdump filename archived */
#define RAMFILE_TMP_ARCHIVE_COMPRESS "ramfiletmp.tar.gz" /* temp ramdump filename archived and compressed*/
#define LISTFILE_TMP "/tmp/sdl-list"
#define RAMDUMP_LOGLIST_PATH XSTR(TELEPHONY_CONFIG_PATH)"ramdump_loglist"
static int EehRamdumpPushLogfiles()
{
char logfilename[MAX_PATH];
char command_buf[2 * MAX_PATH+50];
int ret = -1;
FILE *lfd = NULL;
int archivefd;
char *nvm_root_dir = eehCfg.logDir;
if (nvm_root_dir == NULL)
return ret;
if ((lfd=fopen(RAMDUMP_LOGLIST_PATH, "rt"))==NULL)
{
fprintf(stderr, "RAMDUMP failed to open %s\n", RAMDUMP_LOGLIST_PATH);
return ret;
}
if(chdir(nvm_root_dir) != 0)
{
fprintf(stderr, "RAMDUMP change directory to %s error\n", nvm_root_dir);
goto out;
}
if((archivefd = archive_open(RAMFILE_TMP_ARCHIVE)) < 0)
{
fprintf(stderr, "RAMDUMP open archive file %s error\n", RAMFILE_TMP_ARCHIVE);
goto out;
}
while(fgets(logfilename, sizeof(logfilename), lfd)) {
/* Must strip end-of-line chars, otherwise filename won't be valid */
char *s_end = strchr(logfilename,'\n');
if (s_end)
*s_end = 0;
if(archive_regular_file(archivefd, logfilename) != 0)
{
fprintf(stderr, "RAMDUMP archive log files %s to %s error\n", logfilename, RAMFILE_TMP_ARCHIVE);
archive_close(archivefd);
unlink(RAMFILE_TMP_ARCHIVE);
goto out;
}
}
if(archive_pad_last(archivefd) != 0)
{
fprintf(stderr, "RAMDUMP archive last padding to %s error\n", RAMFILE_TMP_ARCHIVE);
archive_close(archivefd);
unlink(RAMFILE_TMP_ARCHIVE);
goto out;
}
archive_close(archivefd);
sprintf(command_buf, "gzip %s", RAMFILE_TMP_ARCHIVE);
system(command_buf);
sprintf(command_buf, "cp %s /tmp/", RAMFILE_TMP_ARCHIVE_COMPRESS);
system(command_buf);
unlink(RAMFILE_TMP_ARCHIVE_COMPRESS);
out:
fclose(lfd);
return ret;
}
#endif//version_0
#if 0//PUSH_SDL_TO_RAMDUMP
static int EehRamdumpPushSdlfiles(int fd)
{
char tmpfilename[MAX_PATH];
char command_buf[MAX_PATH+50];
int ret = -1;
FILE* lfd = 0; /* list file */
char s[MAX_PATH];
struct stat fst;
const char* list_file = LISTFILE_TMP;
char *nvm_root_dir = eehCfg.logDir;
if (nvm_root_dir == NULL)
return ret;
sprintf(tmpfilename, "%s/%s", nvm_root_dir, RAMFILE_TMP);
/* Get a list of all SDL files in reverse order: starting from the most recent one */
sprintf(command_buf, "busybox ls -1t %s/marvell/*.sdl > %s", nvm_root_dir, list_file);
system(command_buf);
if ((lfd=fopen(LISTFILE_TMP, "rt"))==NULL) {
fprintf(stderr, "RAMDUMP failed to create %s\n", list_file);
goto bail;
}
while(fgets(s, sizeof(s), lfd)) {
/* Must strip end-of-line chars, otherwise filename won't be valid */
char *s_end = strchr(s,'\n');
if (s_end)
*s_end = 0;
/* List contains full path-names. Use tar -C path basename, so no path appears in tgz */
if (stat(s, &fst)) {
fprintf(stderr, "RAMDUMP cannot stat %s\n", s);
continue; /* cannot stat this file - skip */
}
sprintf(command_buf, "busybox tar cvz -f %s -C %s %s",
tmpfilename, dirname(s), basename(s));
system(command_buf); /* create tgz containing this SDL file; tgz also saves the filename */
if (EehPushRamfile(fd, tmpfilename))
break; /* first failure (no memory) - stop the process */
}
ret = 0;
bail:
if (lfd)
fclose(lfd); /* in tmpfs, leave it there */
unlink(tmpfilename);
return ret;
}
#endif//PUSH_SDL_TO_RAMDUMP
static void eeh_panic_reset(void)
{
if (sehdrv_fd < 0)
sehdrv_fd = open(EEH_DEVICE, O_RDWR | O_SYNC);
if (sehdrv_fd >= 0)
ioctl(sehdrv_fd, SEH_IOCTL_EMULATE_PANIC, NULL);
}
/*GLOB*/ int write_ramdump_control(char *code_txt)
{
//ramdump_ctl: options 0/1=e/d, p[anic], B[UG], a[dvance], j[iffies]
// CRC: 'c' account, 'C' report, 'U'[rgent]='c'+'C'
int fd;
int len = strlen(code_txt);
// Cut text. Suppose "const char*" is predictably small,
// only non-const (writ available) dynamic-description may be huge
if (len > 99)
code_txt[99] = 0;
fd = open(EEH_PANIC_DEV, O_WRONLY | O_SYNC);
if (fd < 0)
return -EIO;
len = write(fd, code_txt, len);
close(fd);
return
(len >= 0) ? 0 : -1;
}
static int read_ramdump_level(void)
{
//On graceful shutdown the ramdump_level is changed to
//ignore erros. So EEH should re-check it befor handling
unsigned char lvl = 0xff;
int fd = open(EEH_PANIC_DEV, O_RDONLY | O_SYNC);
if (fd >= 0) {
//read binary-row byte-data, read() is ASCII and returns -1
read(fd, &lvl, 1);
close(fd);
if (lvl != 0xff)
return (int)lvl;
}
return -1;
}
static int/*bool*/ eeh_is_enabled_by_ramdump(void)
{
int lvl;
if (sm.ramdump_level <= 1)
return 0;
lvl = read_ramdump_level();
if (lvl >= 0)
sm.ramdump_level = lvl;
return (sm.ramdump_level >= 2);
}
static char* eeh_kernel_crc_is_valid(void)
{
/* Disable. It is checked at time of PANIC
int rc = write_ramdump_control("c");
if (rc == ERANGE)
return " !Bad Kernel CRC!";
// OK, or ignore other failures
*/
return NULL;
}
static void EehRamdumpActivate(char *desc_txt)
{
/* Emulate panic that triggers ramdump */
char buf[100];
if (sm.TimeOutPanicDesc)
desc_txt = sm.TimeOutPanicDesc;
while (desc_txt || panicExtraDesc[0]) {
if (!desc_txt) {
if (sm.CPASSERT_flag)
break; //this is [TC]. Description has already been added
desc_txt = panicExtraDesc;
}
snprintf(buf, 99, "p_%s", desc_txt);
buf[99] = 0;
write_ramdump_control(buf);
break;
}
//no-text or fallback
if(!eehCfg.stall_FINAL)
{
eeh_panic_reset();
}
}
static void EehRamdump(int activate)
{
//open device here since more than one "PushLog" may be presetnt
int dev = open(EEH_RAMFILE_DEV, O_RDWR | O_SYNC);
if (dev < 0) {
ERRMSG("%s: failed to open ramfile driver, error=%d\n", __FUNCTION__, errno);
} else {
EehRamdumpPushLogfiles(dev);
//EehRamdumpPush***Logfiles()
close(dev);
}
if (sm.hawk_enable)
sleep(5); // This sleep should allow us to get message on tablet
if (activate)
EehRamdumpActivate(NULL);
}
/* Communicate the error info to SEH (for RAMDUMP and m.b. more) */
static void EehSaveErrorInfo(unsigned err, char *str, unsigned char *context)
{
int ret = -1;
EehErrorInfo info;
info.err = err;
info.str = str;
info.regs = context;
ret = ioctl(sehdrv_fd, SEH_IOCTL_SET_ERROR_INFO, &info);
if (ret)
ERRMSG("%s: error=%d\n", __FUNCTION__, errno);
}
static void EehSetEEConfigCPResetFlag(int reset_active)
{
if (ioctl(sehdrv_fd, SEH_IOCTL_SET_EECONFIG_B_CP_RESET, reset_active))
ERRMSG("%s: error=%d\n", __FUNCTION__, errno);
}
int ee_save_postmortem(const char *path, const char *entry)
{
int ret = -1;
#ifdef EEH_LOG_DUMP_LIB
if (sehdrv_fd >= 0)
{
ERRMSG("%s: the dumping is ongoing, please retry later\n", __FUNCTION__);
return -1;
}
sehdrv_fd = open(EEH_DEVICE, O_RDWR | O_SYNC);
if (sehdrv_fd < 0)
{
ERRMSG("%s: opening %s error %s(%d)\n",
__FUNCTION__, EEH_DEVICE, strerror(errno), errno);
return -1;
}
EehApiParams ApiParams;
EehGetCPLoadAddrParam param;
ApiParams.eehApiId = _EehGetCPLoadAddr;
ApiParams.params = &param;
if (ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams) != 0)
{
ERRMSG("%s: error=%d\n", __FUNCTION__, errno);
close(sehdrv_fd);
sehdrv_fd = -1;
return -1;
}
PRE_LOADTABLE_PHYADDR = param.arbel_load_addr;
commImageTableInit();
#endif
if (getCommImageBaseAddr() != INVALID_ADDRESS)
{
EE_PostmortemDesc_Entry *p = getEntryByName(entry);
if (p != ENTRY_NOT_FOUND)
{
FILE *fp;
DBGMSG("%s: entry found, name %s, addr %08x, len %u\n",
__FUNCTION__, p->name, p->bufAddr, p->bufLen);
if ((fp = fopen(path, "wb")) != NULL)
{
unsigned char *buf = (unsigned char *)eeh_mmap(0, p->bufLen,
PROT_READ, MAP_PRIVATE, sehdrv_fd, (off_t)ADDR_CONVERT(p->bufAddr));
if (buf)
{
(void)fwrite(buf, sizeof(char), p->bufLen, fp);
eeh_munmap(buf, p->bufLen);
ret = 0;
}
fclose(fp);
}
}
}
#ifdef EEH_LOG_DUMP_LIB
commImageTableFree();
close(sehdrv_fd);
sehdrv_fd = -1;
#endif
return ret;
}
//---------------------------------------------------------------------
typedef enum ksig_handl { /* /proc/sys/kernel/print-fatal-signals */
KSIG_HANDL_KILL_NO_PRINTS = 0,
KSIG_HANDL_KILL_SHOWREGS = 1,
KSIG_HANDL_KILL_SHORT_PRINT = 2,
KSIG_HANDL_PANIC = 15, /*0xF Fatal Force panic */
KSIG_HANDL_NO_KILL_FREEZE = 16
} ksig_handl;
/*
static void eeh_set_ksig_handler_cfg(ksig_handl cfg)
{
static FILE *fd; // Open once and keep it
if (!fd) fd = fopen("/proc/sys/kernel/print-fatal-signals", "w");
if (!fd) fprintf(fd, "%d", cfg);
}*/
static int eeh_set_ksig_handler_cfg(ksig_handl cfg)
{
int rc = 0;
FILE *fd = fopen("/proc/sys/kernel/print-fatal-signals", "w");
if (fd) {
rc = fprintf(fd, "%d", cfg);
fclose(fd);
}
return rc;
}
void eeh_set_ksig_fatal_silent_kill(void)
{ eeh_set_ksig_handler_cfg(KSIG_HANDL_KILL_SHORT_PRINT); }
void eeh_set_ksig_fatal_panic(void)
{ eeh_set_ksig_handler_cfg(KSIG_HANDL_PANIC); }
void eeh_set_ksig_fatal_freeze(void)
{
int rc = eeh_set_ksig_handler_cfg(KSIG_HANDL_NO_KILL_FREEZE);
if (rc > 0)
ERRMSG("No panic on fatal SIG11 since now %d\n", rc);
else
ERRMSG(" fatal SIG11 set failed with %d\n", rc);
}
/**********************************************************************************
***********************************************************************************
*************** Main Logic and State-Machine **********************
***********************************************************************************
**********************************************************************************/
static void EehTimerStart(int sec, eehTmrRun_e tmr);
static void EehTimerStop(void);
static void EehDfltFinalAction(int param);
/**********************************************************************************
* FINAL ACTIONS (per Device (CP, WiFi, GPS) or Default
* EehComFinalAction
* EehDfltFinalAction
* RETURN: EEH_TASK_CONTINUE or EEH_TASK_DONE
**********************************************************************************/
static int EehComFinalAction(void)
{
if(eehCfg.cpSR_max != 0)
{
EehTimerStop();
if (eehCfg.apSR_onCP)
{
EehPrint("[CP-ASSERT] ******* Force AP Reset*******");
}
else
{
EehPrint("[CP-ASSERT] ******* Silent Reset*******");
}
}
else
{
if (eehCfg.stall_onCP)
{
EehTimerStop();
EehPrint("[CP-ASSERT] ******* No Reset - STALL *******");
return EEH_TASK_CONTINUE;
}
//return EEH_TASK_DONE;
}
#if 1
if (eehCfg.apSR_onCP || (++sm.cpSR_cntr > eehCfg.cpSR_max))
#else
if (eehCfg.apSR_onCP)
#endif
{
EehDfltFinalAction(-1);
}
eeh_set_ksig_fatal_panic();
// CP-SR enabled. Handle it and
time_t tm;
time(&tm);
if (!sm.cpRestartReq) {
eeh_event_prt_and_rec(1, 1, "CPSR recovery no.%d start at %s", sm.cpSR_cntr, ctime(&tm));
/* NOTE: ctime() string has '\n' at the end */
HAWK_indication_txt(EEH2HAWK_SIRESET_NOTIFY);
HAWK_indication_txt(sm.hawk_buf);//EEH2HAWK_SIRESET_READY_MODE
}
EehTimerStart(CPSR_RECOVERY_TO_SEC, CPSilentResetTimerId);
EehPrint("Start CP Silent Reset Timer......");
//Insert Comm subsystem into Reset
if ( EehInsertCommtoReset() != EEH_SUCCESS )
return EEH_TASK_DONE;
// msocket_linkdown(); already done
#if defined (EE_COPY_COMM_IMAGE_FROM_FLASH)
//Copy CP&DSP image from Flash to DDR
if (EehCopyCommImageFromFlash() != EEH_NO_ERROR)
return EEH_TASK_DONE;
#endif
/* before release cp, check the crash list, change the process which need to reset cp to no need to reset */
EehChangeResetState();
notify_cp_network_mode_on_dual_simcard();
//Release Comm subsystem from Reset
EehReleaseCommFromReset();
if (msocket_linkup() == -1) {
EehPrint("CPSR link up failed!!!\n");
return EEH_TASK_DONE;
}
return EEH_TASK_CONTINUE;
}
static void EehDfltFinalAction(int param __attribute__ ((unused)))
{
int try = 5;
int ramdumpReadyOnce = 0;
/*noreturn*/
sync();
//eehNoSleepLong is long enough, don't call driver if not needed
if (eehCfg.ramdump_ena)
{
while (eehCfg.sd_presence_poll) {
if (eeh_sd_card_available(0))
break;
if (!ramdumpReadyOnce) {
ramdumpReadyOnce++;
EehRamdump(0);
}
EehPrint("*** FINAL: waiting for SD-card presence ***\n");
sleep(10);
}
EehPrint("*** RAMDUMP Emulate PANIC ***\n");
#if 0
if (!ramdumpReadyOnce)
EehRamdump(1); // calls for RESET
else
#endif
EehRamdumpActivate(NULL);
}
while (eehCfg.stall_FINAL) {
EehPrint("FINAL ******* No Reset - STALL *******");
eehNoSleepShort();
sleep(120);
}
//No RAMDUMP required but regular reboot only
//Also, fallback if panic-emulation failed
eehNoSleepLong();
while (try--)
{
//Clear Panic-descriptor placed by SEH.ko
// _TBD_
#if defined (BIONIC)
reboot(RB_AUTOBOOT);
#else
system("reboot");
#endif
sleep(5); //should never be here
}
// If still fail to restart, Use "reboot -f" to force
// reboot system.
sleep(5);
system("reboot -f");
}
/**********************************************************************************
* Main EVENT handlers
* EehTimerEventHandling
* EehCtrlEventHandling
* EehTelStateEventHandling
* EehAppEventHandling -> EehWiFiEventHandling, EehGpsEventHandling
* EehComMsgEventHandling
* EehComEventHandling
* See also "second-level" eehConfig_TxtMsgParser
* RETURN: EEH_TASK_CONTINUE or EEH_TASK_DONE
**********************************************************************************/
/* TBD in eeh_ioctl.h:EehMsgInd and used by SEH.ko */
#define EEH_TIMER_EXPIRED_MSG 0x10
#define EEH_CONTROL_MSG 0x11
#define EEH_ATC_OK_MSG 0x12
/* TEL_OK_FILE presence is a fallback for no EEH_ATC_OK_MSG */
static int EehComEventHandling(EehMsgStruct *msg);
static int EehTimerEventHandling(EehMsgStruct *msg __attribute__ ((unused)))
{
//TBD: all timers should be passed over SEH messages,
// Currently full handling done directly inside of EehTimerHandler()
return EEH_TASK_CONTINUE;
}
static void EehTimerHandler(int signum __attribute__ ((unused)),
struct siginfo *pInfo __attribute__ ((unused)),
void *p __attribute__ ((unused)))
{
//eehTmrRun_e tmr = .. p;
if (sm.currentTimerId == timerStopped)
return;
if (sm.currentTimerId == CPSilentResetTimerId) {
sm.currentTimerId = timerStopped;
if (access(TEL_OK_FILE, F_OK) == 0) {
time_t tm;
time(&tm);
sm.inProgress = EEH_NONE_ASSERT;
sm.CPASSERT_flag = 0;
sm.cpSR_cntr = 0;
panicExtraDesc[0] = 0;
if (sm.cpRestartReq) {
sm.cpRestartReq = 0;
eeh_event_prt_and_rec(1, 1, "CP Switch/Restart passed at %s",
ctime(&tm));
} else {
eeh_event_prt_and_rec(1, 1, "CPSR recovery no.%d passed at %s",
sm.cpSR_cntr, ctime(&tm));
}
eehNoSleepShort();
} else {
eehNoSleepLong();
if (sm.cpRestartReq) {
int fd;
sm.cpRestartReq++;
ERRMSG("CP Switch/Restart TimeOut. Try to CPASSERT\n");
sm.TimeOutPanicDesc = "SwitchRestart_TO";
eehCfg.cpSR_max = 0;
eehCfg.logLevel = 4; //Ramdump
eehCfg.stall_onCP = 0;
EehTimerStart(2, cpAssertReqTimerId);
//echo a > /dev/acipc
fd = open("/dev/acipc", O_RDWR | O_SYNC);
if (fd >= 0) {
write(fd, "a", 1);
close(fd);
return; //don't fail into final action now
}
} else {
ERRMSG("CPSR recovery TimeOut. Go to panic\n");
sm.TimeOutPanicDesc = "CPSR_recoveryTO";
}
EehDfltFinalAction(-1);
}
}
else
if (sm.currentTimerId == eehGuardTimerId) {
ERRMSG("Long Guard Timer %ds (level %d) expired. Go to panic\n",
guardTO[sm.logLevel], sm.logLevel);
sm.currentTimerId = timerStopped;
EehDfltFinalAction(-1);
}
else
if (sm.currentTimerId == cpAssertReqTimerId) {
ERRMSG("CP Switch and CPASSERT failed (%d). Go to panic\n", sm.cpRestartReq);
sm.currentTimerId = timerStopped;
EehDfltFinalAction(-1);
} else {
ERRMSG("Wrong timer state %d\n", sm.currentTimerId);
}
}
static void EehTimerStart(int sec, eehTmrRun_e tmr)
{
int rc;
struct itimerval interval_timer;
sm.currentTimerId = timerStopped; //mark as stop first
interval_timer.it_interval.tv_usec = 0;
interval_timer.it_interval.tv_sec = 0;
interval_timer.it_value.tv_usec = 0;
interval_timer.it_value.tv_sec = sec;
rc = setitimer(ITIMER_REAL, &interval_timer, NULL);
if (rc)
ERRMSG("Cannot start timer\n");
else
sm.currentTimerId = tmr;
}
static void EehTimerStop(void)
{
struct itimerval interval_timer;
if (sm.currentTimerId == timerStopped)
return;
sm.currentTimerId = timerStopped;
memset(&interval_timer, 0, sizeof(interval_timer));
setitimer(ITIMER_REAL, &interval_timer, NULL);
}
static void EehTimerInit(int init)
{
if (init) {
/* set up the handler */
struct sigaction act;
act.sa_sigaction = EehTimerHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sm.currentTimerId = timerStopped;
if (sigaction(SIGALRM, &act, NULL))
ERRMSG("Cannot create timer\n");
} else {
EehTimerStop();
}
}
//-}} TIMER -----------
static int EehCtrlEventHandling(EehMsgStruct *msg __attribute__ ((unused)))
{
return EEH_TASK_CONTINUE;
}
static int EehTelStateEventHandling(EehMsgStruct *msg __attribute__ ((unused)))
{
eeh_event_prt_and_rec(1, 1, "TELEPHONY is OK\n");
HAWK_indication_txt(EEH2HAWK_MTSD_OK);
return EEH_TASK_CONTINUE;
}
static int EehWiFiEventHandling(EehMsgStruct *msg __attribute__ ((unused)))
{
return EEH_TASK_DONE;
}
static int EehGpsEventHandling(EehMsgStruct *msg __attribute__ ((unused)))
{
return EEH_TASK_DONE;
}
static int EehAppEventHandling(EehMsgStruct *msg)
{
int ret = 1; //= go to AP-final-action
char eeString[512+60];
sprintf(eeString, "[AP] %s", msg->msgDesc);
eeh_event_prt_and_rec(1, 1, "%s\n", eeString);
if (sm.inProgress == EEH_NONE_ASSERT) {
sm.inProgress = EEH_AP_ASSERT;
} else {
ERRMSG("New ASSERT ignored since previous ongoing\n");
return EEH_TASK_CONTINUE;
}
EEH_DUMP_ENHANCE_draw_panic(eeString);
msocket_linkdown();
HAWK_indication_txt(EEH2HAWK_ASSERT_NOTIFY);
//if(level...)
eeh_ap_thread_info(1);
if (strcasestr(msg->msgDesc, "WiFi"))
ret = EehWiFiEventHandling(msg);
if (strcasestr(msg->msgDesc, "GPS"))
ret = EehGpsEventHandling(msg);
return ret;
}
/*****************************************************************
* RETURN:
* EEH_TASK_CONTINUE=0 go back to read(msg) main loop
* EEH_TASK_DONE exit from loop and fail into EehDfltFinalAction
******************************************************************
*/
static int EehComMsgEventHandling(const char *prefix, const char *msgDesc)
{
int ret;
char eeString[512+300]={""};
sm.cpRestartReq = 1;
sm.inProgress = EEH_CP_SILENT_RESET;
/* For switch modem, reset cpSR_cntr to avoid Emulate PANIC */
sm.cpSR_cntr = 0;
unlink(TEL_OK_FILE);
if (msgDesc) {
snprintf(eeString, 100, "%s %s", prefix, msgDesc);
eeh_event_prt_and_rec(1, 1, "%s\n", eeString);
}
//In CP-assert case the CP is already stopped
//The reset executed asynchroneously for data Uplink/Downlink
//(including the diag). Stop(!) CP before linkdown
//EehInsertCommtoReset();
if (msocket_linkdown()) {
ERRMSG(" cannot msocket_linkdown but try go ahead\n");
}
//HAWK_indication_txt(EEH2HAWK_RESTART_REQ);
ret = EehComFinalAction();
return ret;
}
static int SaveBmBuffer(void)
{
#define BM_BUFF_FILE "/sys/kernel/debug/bm/buffer"
#define BUF_SIZE (256 * 1024)
int fd_src = -1, fd_dst = -1;
int ret = -1, nr, nw, total = 0;
char *buf = NULL;
char dstfile[128];
if (access(BM_BUFF_FILE, F_OK))
return 0;
ERRMSG("%s: start...\r\n", __func__);
fd_src = open(BM_BUFF_FILE, O_RDONLY);
if (fd_src < 0) {
ERRMSG("open %s failed.\r\n", BM_BUFF_FILE);
goto err;
}
memset(dstfile, 0, sizeof(dstfile));
sprintf(dstfile, "%s/bm_buffer.bin", eehCfg.logDir);
if (access(dstfile, F_OK) == 0)
remove(dstfile);
fd_dst = open(dstfile, O_CREAT | O_RDWR | O_SYNC, 777);
if (fd_dst < 0) {
ERRMSG("open %s failed.\r\n", dstfile);
goto err;
}
buf = malloc(BUF_SIZE);
if (!buf) {
ERRMSG("malloc %d failed.\r\n", BUF_SIZE);
goto err;
}
while (1) {
nr = read(fd_src, buf, BUF_SIZE);
if (nr < 0) {
ERRMSG("Failed to read from %s, nr: %d\r\n", BM_BUFF_FILE, nr);
goto err;
}
if (nr == 0)
break;
total += nr;
nw = write(fd_dst, buf, nr);
if (nw != nr) {
ERRMSG("Failed to write dst file %s, nw: %d, nr: %d\r\n", dstfile, nw, nr);
goto err;
}
}
ret = 0;
err:
if (fd_src >= 0) close(fd_src);
if (fd_dst >= 0) close(fd_dst);
if (buf) free(buf);
ERRMSG("%s: %s, size: 0x%x\r\n", __func__, ret < 0 ? "FAILED" : "OK", total);
return ret;
}
static int EehComEventHandling(EehMsgStruct *msg __attribute__ ((unused)))
{
int ret, curr_dir_sd_log = -1;
int sd_known_available = -1; //unknown
char cmdBuffer[MAX_PATH + 20];
char eeBuffer[512]={""};
char eeString[512+300]={""};
char *crc_txt, *log_dir, *cp_token;
static char cpSR_logLevel_bank = 0;
ERRMSG("EehComEventHandling: cpSR_max %d, apSR_onCP %d, logLevel %d %d\n",
eehCfg.cpSR_max, eehCfg.apSR_onCP, eehCfg.logLevel, eehCfg.cpSR_logLevel);
if ((!eeh_sd_card_available(0)) && eehCfg.cpSR_max && (!eehCfg.apSR_onCP)) {
ERRMSG("EEH/S-RESET: No sdcard, disable cp log saving: %d\n", eehCfg.cpSR_logLevel);
if (eehCfg.cpSR_logLevel)
cpSR_logLevel_bank = eehCfg.cpSR_logLevel;
eehCfg.cpSR_logLevel = 0;
} else {
if (cpSR_logLevel_bank && (eehCfg.cpSR_logLevel == 0))
eehCfg.cpSR_logLevel = cpSR_logLevel_bank;
}
if (sm.inProgress != EEH_NONE_ASSERT) {
ERRMSG("EehComEventHandling: inProgress %d, cpRestartReq %d\n", sm.inProgress, sm.cpRestartReq);
if (access(TEL_OK_FILE, F_OK)) {
if (sm.inProgress == EEH_CP_SILENT_RESET) {
ERRMSG("New ASSERT happened in ongoing silent reset\n");
EehTimerStop();
}
else
{
ERRMSG("New ASSERT ignored since previous ongoing\n");
//TBD: add timeout or/and go to panic
return EEH_TASK_CONTINUE;
}
}
}
if(eehCfg.cpSR_max != 0)
{
sm.cpRestartReq = 1;
sm.inProgress = EEH_CP_SILENT_RESET;
}
else
{
sm.inProgress = EEH_CP_ASSERT;
}
EEH_DUMP_ENHANCE_reset_archive_files();
//Update "working" log-level
sm.logLevel = eehCfg.logLevel;
if (sm.cpRestartReq > 1) {
if (sm.currentTimerId == cpAssertReqTimerId) {
EehTimerStop();
ERRMSG("CP Switch failed. CPASSERT activated\n");
}
}
#if 0
else if (sm.cpRestartReq) {
sm.logLevel = 0;
}
#endif
if (eehCfg.cpSR_max && ((sm.cpSR_cntr+1) <= eehCfg.cpSR_max)) {
//CPSR enabled and active
sm.logLevel = eehCfg.cpSR_logLevel;
} else {
//Not CPSR but FINAL
if (!eehCfg.stall_onCP && (sm.logLevel != EEH_LOGLVL_7_DBG_ARC2SD)) {
sd_known_available = eeh_sd_card_available(0);
if (!sd_known_available) {
if (eehCfg.sd_presence_poll) {
ERRMSG("No SD-card. FINAL would wait for insert or command");
} else {
//Fallback into legacy mode
ERRMSG("No SD-card. Fallback to SaveALL to %s and STALL\n", TEMP_LOG_DIR);
eehCfg.stall_onCP = 1;
sm.logLevel = EEH_LOGLVL_7_DBG_ARC2SD;
}
}
}
}
if ((sm.logLevel | 1) == EEH_LOGLVL_9_LOG2SD_ARC2SD) {
if (sd_known_available < 0)
sd_known_available = eeh_sd_card_available(0);
if (!sd_known_available)
sm.logLevel = EEH_LOGLVL_7_DBG_ARC2SD;
}
if (guardTO[sm.logLevel] && eehCfg.guard_timer) {//if(Zero) no timer required
if (eehCfg.guard_timer > 1) //this is for "adjust debug" only
EehTimerStart(eehCfg.guard_timer, eehGuardTimerId);
else
EehTimerStart(guardTO[sm.logLevel], eehGuardTimerId);
}
eeh_set_ksig_fatal_freeze(); //ignore APPS failures
unlink(TEL_OK_FILE);
crc_txt = eeh_kernel_crc_is_valid();
commImageTableInit();
if (getCommImageBaseAddr() == INVALID_ADDRESS) {
// Likely APPS-problem. Go to AppFinalAction
eeh_event_prt_and_rec(1, 1, "[CP] assert, description is unavailable\n");
HAWK_indication_txt(EEH2HAWK_ASSERT_NOTIFY);
return EEH_TASK_DONE;
}
/* Print out the CP Error Buffer Description Info */
cp_token = "[CP]";
if (!crc_txt && panicExtraDesc[0]) {
crc_txt = panicExtraDesc;
if (sm.CPASSERT_flag)
cp_token = "[TC]";
}
GetComEEDescbuf(eeBuffer, sizeof(eeBuffer), crc_txt);
EehSaveErrorInfo(ERR_EEH_CP, eeBuffer, 0);
sprintf(eeString, "%s %s", cp_token, eeBuffer);
eeh_event_prt_and_rec(1, 1, "%s\n", eeString);
HAWK_indication_txt(EEH2HAWK_ASSERT_NOTIFY);
eeh_cp_assert_info_rec(eeString);
if (!eehCfg.cpSR_max) {
EEH_DUMP_ENHANCE_draw_panic(eeString);
}
// mark linkdown to prevent potential AP failures or CP-SR
if (msocket_linkdown()) {
EehPrint(" cannot msocket_linkdown but try go ahead\n");
}
if (sd_known_available < 0) {
sd_known_available = eeh_sd_card_available(0);
}
ERRMSG("EehComEventHandling: logLevel %d\n", sm.logLevel);
// Save files according to Log-Level
switch (sm.logLevel) {
case EEH_LOGLVL_9_LOG2SD_ARC2SD:
case EEH_LOGLVL_8_LOG2SD:
case EEH_LOGLVL_7_DBG_ARC2SD:
case EEH_LOGLVL_6_DBG:
system("rm com_*.bin* diag*.sdl *.txt");
if ((sm.logLevel | 1) == EEH_LOGLVL_9_LOG2SD_ARC2SD) { //8 or 9
SaveComPostmortem(0, 0);
ERRMSG("change directory(%s), lev %d\n", LOG_DIR_SD_LOG, sm.logLevel);
curr_dir_sd_log = chdir(LOG_DIR_SD_LOG);
if (!curr_dir_sd_log) {
eehCfg.logDir = (char*)LOG_DIR_SD_LOG;
system("rm com_*.bin* diag*.sdl");
}
}
if (sd_known_available) {
if (access(LOG_DIR_SD_LOG, F_OK) != 0) {
if(mkdir(LOG_DIR_SD_LOG, 0777) < 0)
{
ERRMSG("create directory %s error\n", LOG_DIR_SD_LOG);
}
}
curr_dir_sd_log = chdir(LOG_DIR_SD_LOG);
if (!curr_dir_sd_log) {
eehCfg.logDir = (char*)LOG_DIR_SD_LOG;
} else {
ERRMSG("change directory(%s) fail, \n", LOG_DIR_SD_LOG);
}
}
if (sd_known_available && !curr_dir_sd_log)
SaveBmBuffer();
SaveComPostmortem(0, 2);
Linux_EELOG_shareReadFWrite();//log-streaming
SaveComPostmortem(3, 100);
/* com_DDR_RW.bin. */
if (sd_known_available) {
EE_SaveComDDR_RW(1);
}
commImageTableFree(); //free memory asap
eeh_ap_thread_info(1);
#ifdef DIAG_FS_MOUNT_POINT
//In nevo platform, diag over fs will save CP log in /tmp/acat, when CP assert happen, copy it to /data/acat directory.
//Since in 920 platform, diag over fs already save CP log in /data/acat, so below insure have no effect in 920 platform.
if((access(DIAG_FS_MOUNT_POINT, F_OK) == 0) && (strncmp(DIAG_FS_MOUNT_POINT, "/data/", strlen("/data/"))))
{
snprintf(cmdBuffer, sizeof(cmdBuffer), "cp -rf %s /data", DIAG_FS_MOUNT_POINT);
system(cmdBuffer);
}
#endif
// Create "end-of-logging" file-marker = com_WDT_END.bin
system("date > com_WDT_END.bin");
system("dmesg > dmesg.txt");
system("cat /proc/cmdline > cmdline.txt");
system("touch ubootlog.txt");
system("cat /proc/boot_log > ubootlog.txt");
record_archive_files(NULL, "dmesg.txt");
record_archive_files(NULL, "ubootlog.txt");
break;
case EEH_LOGLVL_5_RDP_ARC2SD:
case EEH_LOGLVL_4_RDP:
Linux_EELOG_shareReadFWrite();//log-streaming
commImageTableFree();
eeh_ap_thread_info(1);
#ifdef DIAG_FS_MOUNT_POINT
//In nevo platform, diag over fs will save CP log in /tmp/acat, when CP assert happen, copy it to /data/acat directory.
//Since in 920 platform, diag over fs already save CP log in /data/acat, so below insure have no effect in 920 platform.
if((access(DIAG_FS_MOUNT_POINT, F_OK) == 0) && (strncmp(DIAG_FS_MOUNT_POINT, "/data/", strlen("/data/"))))
{
snprintf(cmdBuffer, sizeof(cmdBuffer), "cp -rf %s /data", DIAG_FS_MOUNT_POINT);
system(cmdBuffer);
}
#endif
break;
case EEH_LOGLVL_3_CPSR_ARC2SD:
case EEH_LOGLVL_2_CPSR:
Linux_EELOG_shareReadFWrite();//log-streaming
commImageTableFree();
system("dmesg > dmesg.txt");
system("touch ubootlog.txt");
system("cat /proc/boot_log > ubootlog.txt");
break;
default:
set_ap_dump_flag();
ERRMSG("unknown logLevel=%d handled as level=%d\n", sm.logLevel, 1);
case EEH_LOGLVL_1_ARC2SD:
SaveComPostmortem(0, 0);
set_ap_dump_flag();
case EEH_LOGLVL_0:
commImageTableFree();
set_ap_dump_flag();
break;
}//END switch(logLevel)
if (!curr_dir_sd_log)
system("cp " EEH_JOURNAL_FILE " ."); //copy from flash/log into sd/log
if (eehCfg.hawk_ena) {
//Here should be HAWK saving procedure
} else {
if (EEH_LOGLVL_WITH_ARC2SD(sm.logLevel)) {
if ((sm.logLevel == EEH_LOGLVL_7_DBG_ARC2SD) ||
(sm.logLevel == EEH_LOGLVL_9_LOG2SD_ARC2SD)) {
//WA: arc for com_DDR_RW.bin takes several minutes
// or never finished. Exclude it by rename
int wa = -1;
EEH_DUMP_ENHANCE_record_archive_files(eeString);
if (!sd_known_available)
{
wa = rename("com_RAMLOG.bin", "com_RAMLOG.bin" "_tmp");
if (!wa)
rename("com_RAMLOG.bin" "_tmp", "com_RAMLOG.bin");
}
else
{
wa = rename("com_DDR_RW.bin", "com_DDR_RW.bin" "_tmp");
if (!wa)
rename("com_DDR_RW.bin" "_tmp", "com_DDR_RW.bin");
}
} else {
EEH_DUMP_ENHANCE_record_archive_files(eeString); //arc files
}
}
}
log_dir = eehCfg.logDir;
if (!curr_dir_sd_log) {
chdir(LOG_DIR); //restore back to default
eehCfg.logDir = (char*)LOG_DIR;
if (sm.logLevel == EEH_LOGLVL_8_LOG2SD) {
//All logs (including huge) saved in sd/log directory.
//Rename sd/log to sd/log_000_mm-dd_hh.mm.ss
eeh_log_dir_name_generate(cmdBuffer, sizeof(cmdBuffer), 0);
ERRMSG("rename directory(%s)\n", cmdBuffer);
rename(LOG_DIR_SD_LOG, cmdBuffer);
mkdir(LOG_DIR_SD_LOG, 0777);
log_dir = cmdBuffer;
}
}
//commImageTableFree();
eeh_event_prt_and_rec(1, 1, "[EEH] Save CP dump bins done to %s\n", log_dir);
ret = EehComFinalAction();
return ret;
}
/****************************************************************************
* EehTask : Error handler TASK (main logic)
* EehInit
* EehDeinit
****************************************************************************/
static void *EehTask(void* arg)
{
UNUSEDPARAM(arg)
EehMsgStruct msg;
int ret = 0;
//Detach is like process-fork. Don't use
// pthread_detach(pthread_self());
prctl(PR_SET_NAME, "EehTask"); /* equal pthread_setname_np(pthread_self(),"EehTask");*/
DBGMSG("%s is running\n", __FUNCTION__);
eeh_event_prt_and_rec(0, 1, "Error Handler Task started\n");
HAWK_indication_txt(EEH2HAWK_BOOT);
while (!ret)
{
ret = read(sehdrv_fd, &msg, sizeof(EehMsgStruct));
/* eehNoSleepLong(); -- already done by the /dev/seh */
if (ret != sizeof(EehMsgStruct)) {
ERRMSG("read fail (ret=%d/errno=%d, msgId=%d)\n", ret, errno, msg.msgId);
//EEH failed. go to FinalAction
ret = -1;
break;
//ret = 0; continue;
}
msg.msgDesc[EEH_MSG_DESC_LEN - 1] = '\0';
switch (msg.msgId)
{
case EEH_TIMER_EXPIRED_MSG:
ret = EehTimerEventHandling(&msg);
break;
case EEH_CONTROL_MSG:
ret = EehCtrlEventHandling(&msg);
break;
case EEH_ATC_OK_MSG:
ret = EehTelStateEventHandling(&msg);
break;
case EEH_AP_ASSERT_MSG:
case EEH_AP_CRASH_MSG: //to be obsolet
ret = EehAppEventHandling(&msg);
break;
case EEH_CP_SILENT_RESET_MSG:
// This is txt-message from other processes
// Use "second-level" eehConfig_TxtMsgParser
ret = eeh_is_enabled_by_ramdump();
if (!ret)
break; //ret=0, ignore the message
msg.msgDesc[EEH_MSG_DESC_LEN - 1] = '\0';
switch (eehConfig_TxtMsgParser(msg.msgDesc, msg.force)) {
/*
* if (ret==0) then EEH_TASK_CONTINUE in the while()
* else {while(!ret)} aborted with goto EehDfltFinalAction()
*/
case EEH_MSG_SWITCHMODEM:
ret = EehComMsgEventHandling("CP", msg.msgDesc);
break;
case EEH_MSG_WIFI_ASSERT:
if (++sm.wifiSR_cntr > eehCfg.wifiSR_max) {
ret = EEH_TASK_DONE;
break;
}
HAWK_indication_txt(msg.msgDesc);
#define MODEM_RESET_ON_WIFI_RESET 0
if (MODEM_RESET_ON_WIFI_RESET) {
ret = weh_do_silent_reset(0);
ret = EehComMsgEventHandling("WIFI", NULL);
ret = weh_do_silent_reset(1);
} else {
ret = weh_do_silent_reset(2);
}
break;
default:
ret = EEH_TASK_CONTINUE;
break;
}
break;
case EEH_WDT_INT_MSG:
ret = eeh_is_enabled_by_ramdump();
if (!ret)
break; //ret=0, ignore the message
ret = EehComEventHandling(&msg);
break;
default:
ERRMSG("Invalid message =%d\n", msg.msgId);
ret = -1; //goto EehDfltFinalAction()
break;
}
if (!ret/*=EEH_TASK_CONTINUE=0*/ && (sm.currentTimerId == timerStopped))
eehNoSleepShort();
}
//ERRMSG("wait-loop exit\n");//dbg
EehDfltFinalAction(ret);
return NULL;
}
#define DSP_LOAD_FNAME "/dev/dsp_load"
/****************************************************************************
* DspLoadTask : DSP LOAD TASK (main logic)
****************************************************************************/
static void *DspLoadTask(void* arg)
{
UNUSEDPARAM(arg)
int ret = 0;
char buffer[4];
int dsp_load_fd = -1;
struct cp_load_table_head *hdr;
prctl(PR_SET_NAME, "DspLoadTask");
DBGMSG("%s is running\n", __FUNCTION__);
dsp_load_fd = open(DSP_LOAD_FNAME, O_RDWR | O_SYNC);
if (dsp_load_fd < 0) {
pxa_dbg("open dsp load file failed\n");
return NULL;
}
while (1)
{
ret = read(dsp_load_fd, buffer, 4);
DBGMSG("%s get load req: %d\n", __FUNCTION__, ret);
/* -1, -1 means have not prepared the current & exist cp property values,
* it means need to get these two property values from property_get */
if ((hdr = cp_load_table_init()) == NULL) {
pxa_dbg("dsp_load: cp_load_table_init failed\n");
sleep(1);
continue;
}
DBGMSG("%s load table ok\n", __FUNCTION__);
ret = set_cp_type_property(hdr);
if (ret == -1)
{
cp_load_table_exit(hdr);
pxa_dbg("dsp_load: set_cp_type_property failed\n");
sleep(1);
continue;
}
DBGMSG("%s get prop ok\n", __FUNCTION__);
/* MSA must be loaded before RF */
if (
#if defined(CONFIG_ASR1901_EEH) || defined(CONFIG_ASR1903_EEH)
cp_load_bx2_image(hdr, 0) == -1 ||
#endif
#ifndef CONFIG_ASR1903_EEH
cp_load_msa_image(hdr, 0) == -1 ||
#endif
cp_load_rf_image(hdr, 0) == -1)
{
cp_load_table_exit(hdr);
pxa_dbg("dsp_load: dsp/rf image loading failed\n");
sleep(1);
continue;
}
DBGMSG("%s image loading ok\n", __FUNCTION__);
write(dsp_load_fd, buffer, 4);
cp_load_table_exit(hdr);
}
return NULL;
}
/********************************************************/
EEH_STATUS EehInit(int createPthread)
{
int ret, ramdump_level;
EehApiParams ApiParams;
EehGetCPLoadAddrParam param;
const char *errmsg = NULL;
pthread_t eeh_task;
pthread_t dsp_load_task;
sm.inProgress = EEH_NONE_ASSERT;
ramdump_level = read_ramdump_level();
sm.ramdump_level = (ramdump_level >= 0) ? ramdump_level : 2;
eehConfigInit();
#ifdef HAWK_ENABLED
{
sm.hawk_enable = eehCfg.hawk_ena;
if (sm.hawk_enable && eehCfg.cpSR_max) {
sm.hawk_buf = malloc(200);
//construct msg once only
snprintf(sm.hawk_buf, 200,
EEH2HAWK_ASSERT_DETECTED, EEH2HAWK_SIRESET_READY_MODE);
}
}
#endif
EEH_DUMP_ENHANCE_reset_archive_files();
// open driver if it still not opened (by main())
// Do it BEFORE pthread_create(EehTask) which also uses it
if (sehdrv_fd < 0)
sehdrv_fd = open(EEH_DEVICE, O_RDWR | O_SYNC);
if (sehdrv_fd < 0) {
errmsg = "open seh-driver";
goto retErr;
}
EehTimerInit(1);
if (createPthread) {
ret = pthread_create(&eeh_task, NULL, (void *)EehTask, NULL);
if (ret) {
errmsg = "create EehTask";
goto retErr;
}
}
ret = pthread_create(&dsp_load_task, NULL, (void *)DspLoadTask, NULL);
if (ret) {
errmsg = "create DspLoadTask";
goto retErr;
}
/* else
* Every thread requires 16*2 contiguous memory
* so it is better to run on the main() thread
* TBD with main() below inits modification and call
* EehTask(NULL); from here
*/
ApiParams.eehApiId = _EehGetCPLoadAddr;
ApiParams.params = &param;
ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams);
if (ret) {
errmsg = "ioctl";
goto retErr;
}
PRE_LOADTABLE_PHYADDR = param.arbel_load_addr;
return EEH_SUCCESS;
retErr:
ERRMSG("%s ERROR: %s\n", __FUNCTION__, errmsg);
return EEH_ERROR_INIT;
}
void eeh_send_to_hawk(char *msg)
{
HAWK_indication_txt(msg);
}
/********************************************************/
EEH_STATUS EehDeinit (void)
{
int ret;
EehApiParams ApiParams;
EehTimerInit(0);
ApiParams.eehApiId = _EehDeInit;
ApiParams.params = NULL;
ret = ioctl(sehdrv_fd, SEH_IOCTL_API, &ApiParams);
if (ret)
return EEH_ERROR;
return ApiParams.status;
}
struct sleep_log_unit {
UINT32 ulTickHigh;
UINT32 ulTickLow;
UINT16 usRsv;
UINT16 usSleepState;
};
struct sleep_log_buf {
UINT32 ulTotal;
UINT32 ulLast;
UINT32 ulRsv;
struct sleep_log_unit stBuf[0];
};
void ee_convert_sleep_record(unsigned char *buf, unsigned int bufLen, double threshold, FILE *fpTxt)
{
unsigned int lrn; //last record number
unsigned int trn; //total record number
unsigned int crn; //current record number
UINT32 max_record_num = bufLen / sizeof(struct sleep_log_unit) - 1;
struct sleep_log_buf *logger = (struct sleep_log_buf *)buf;
struct sleep_log_unit *pCur, *pNext;
UINT64 curTick, nextTick, curDurationTick;
double curTime, nextTime, curDurationTime;
double startTime, endTime;
UINT64 sleepTicks = 0, awakeTicks = 0;
double sleepTimes = 0, awakeTimes = 0;
trn = logger->ulTotal;
lrn = logger->ulLast;
crn = (lrn + 1) % max_record_num; // lrn is the last valid record, so lrn+1 is the oldest valid record
pCur = &logger->stBuf[crn];
curTick = (UINT64)pCur->ulTickHigh << 32 | pCur->ulTickLow;
crn = (crn + 1) % max_record_num;
//locate first record
while (1) {
pNext = &logger->stBuf[crn];
nextTick = (UINT64)pNext->ulTickHigh << 32 | pNext->ulTickLow;
if (nextTick < curTick) {
nextTime = (double)nextTick/TICKSPER13M; //nextTick is the first record
startTime = nextTime;
break;
}
curTick = nextTick; //record curTick as nextTick
crn = (crn + 1) % max_record_num; //move to next crn
};
fprintf(fpTxt, "================= Client:MPSS =================\n");
fprintf(fpTxt, " Time sclk \t Time sec \tState\tDuration\n");
trn = max_record_num - 1;
while (trn-- > 0) {
pCur = pNext;
curTick = nextTick;
curTime = nextTime;
crn = (crn + 1) % max_record_num;
pNext = &logger->stBuf[crn];
nextTick = (UINT64)pNext->ulTickHigh << 32 | pNext->ulTickLow;
curDurationTick = (INT64)nextTick - (INT64)curTick;
curDurationTime = (double)curDurationTick/TICKSPER13M;
nextTime = curTime + curDurationTime;
if (pCur->usSleepState == SLEEP_STATE_WAKE_UP) {
awakeTicks += curDurationTick;
awakeTimes += curDurationTime;
} else {
sleepTicks += curDurationTick;
sleepTimes += curDurationTime;
}
if (curTime > 0 && nextTime - curTime > threshold && pCur->usSleepState == SLEEP_STATE_WAKE_UP)
fprintf(fpTxt, "%020llu\t%.6lf\t%5d\t%.6lf <- NOTICE! AWAKE EXCEED THRESHOLD: %lf\n", curTick, curTime, pCur->usSleepState, curDurationTime, threshold);
else
fprintf(fpTxt, "%020llu\t%.6lf\t%5d\t%.6lf\n", curTick, curTime, pCur->usSleepState, curDurationTime);
};
endTime = nextTime;
fprintf(fpTxt, "\n");
fprintf(fpTxt, "Log available: %.6lf ~ %.6lf (sec)\n", startTime, endTime);
fprintf(fpTxt, "Time Sleep: %20llu (%.6lf sec)\n", sleepTicks, sleepTimes);
fprintf(fpTxt, "Time Awake: %20llu (%.6lf sec)\n", awakeTicks, awakeTimes);
fprintf(fpTxt, "Sleep: %.6lf%% Awake: %.6lf%%\n", 100.0 * sleepTicks / (sleepTicks + awakeTicks), 100.0 * awakeTicks / (sleepTicks + awakeTicks));
}
int ee_convert_sleep_record_file(const char *fileBin, const char *fileTxt, double threshold)
{
FILE *fpBin, *fpTxt;
long sz, len;
unsigned char *buf = NULL;
if ((fpBin = fopen(fileBin, "rb")) == NULL) {
ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileBin);
goto rferr;
}
if ((fpTxt = fopen(fileTxt, "w")) == NULL) {
ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileTxt);
goto wferr;
}
/* get binary file length */
fseek(fpBin, 0L, SEEK_END);
sz = ftell(fpBin);
if (sz < 0) {
ERRMSG("%s: get file: %s size error\n", __FUNCTION__, fileBin);
goto szerr;
}
buf = malloc(sz);
if (!buf) {
ERRMSG("%s: malloc error\n", __FUNCTION__);
goto memerr;
}
/* read binary file to buf */
fseek(fpBin, 0L, SEEK_SET);
if ((len = fread(buf, 1, sz, fpBin)) != sz) {
ERRMSG("%s: failed to read %ld bytes from %s, ret=%ld\n", __FUNCTION__, sz, fileBin, len);
goto readerr;
}
/* convert buf and write to text file */
ee_convert_sleep_record(buf, len, threshold, fpTxt);
/* release resources */
free(buf);
fclose(fpTxt);
fclose(fpBin);
return 0;
readerr:
free(buf);
memerr:
szerr:
fclose(fpTxt);
wferr:
fclose(fpBin);
rferr:
return -1;
}
struct sleep_observer_unit {
UINT32 ulTick;
UINT32 ulCount;
UINT8 ucResource[8];
UINT8 ucClient[8];
};
struct sleep_observer_buf {
UINT32 ulTotal;
UINT32 ulLast;
UINT8 ucRsv[16];
struct sleep_observer_unit stBuf[0];
};
void ee_convert_observer_record(unsigned char *buf, unsigned int bufLen, FILE *fpTxt)
{
unsigned int lrn; //last record number
unsigned int trn; //total record number
unsigned int crn; //current record number
UINT32 max_record_num = bufLen / sizeof(struct sleep_observer_unit) - 1;
struct sleep_observer_buf *observer = (struct sleep_observer_buf *)buf;
struct sleep_observer_unit *pCur;
UINT32 curTick, prevTick;
trn = observer->ulTotal;
lrn = observer->ulLast;
crn = (lrn + 1) % max_record_num; // lrn is the last valid record, so lrn+1 is the oldest valid record
pCur = &observer->stBuf[crn];
curTick = pCur->ulTick;
crn = (crn + 1) % max_record_num;
//locate first record
while (1) {
prevTick = curTick;
pCur = &observer->stBuf[crn];
curTick = pCur->ulTick;
if (curTick < prevTick) //cur record is the first record
break;
crn = (crn + 1) % max_record_num;
};
fprintf(fpTxt, "================= Client:OBSERVER =================\n");
fprintf(fpTxt, " Time sclk \t Time sec \tData\n");
trn = max_record_num - 1;
while (trn-- > 0) {
pCur = &observer->stBuf[crn];
crn = (crn + 1) % max_record_num;
fprintf(fpTxt, "%012u\t%.6lf\t%8s\t%8s\n", pCur->ulTick, (double)pCur->ulTick/TICKSPER32K, pCur->ucResource, pCur->ucClient);
};
}
int ee_convert_observer_record_file(const char *fileBin, const char *fileTxt)
{
FILE *fpBin, *fpTxt;
long sz, len;
unsigned char *buf = NULL;
if ((fpBin = fopen(fileBin, "rb")) == NULL) {
ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileBin);
goto rferr;
}
if ((fpTxt = fopen(fileTxt, "w")) == NULL) {
ERRMSG("%s: cannot open %s\n", __FUNCTION__, fileTxt);
goto wferr;
}
/* get binary file length */
fseek(fpBin, 0L, SEEK_END);
sz = ftell(fpBin);
if (sz < 0) {
ERRMSG("%s: get file: %s size error\n", __FUNCTION__, fileBin);
goto szerr;
}
buf = malloc(sz);
if (!buf) {
ERRMSG("%s: malloc error\n", __FUNCTION__);
goto memerr;
}
/* read binary file to buf */
fseek(fpBin, 0L, SEEK_SET);
if ((len = fread(buf, 1, sz, fpBin)) != sz) {
ERRMSG("%s: failed to read %ld bytes from %s, ret=%ld\n", __FUNCTION__, sz, fileBin, len);
goto readerr;
}
/* convert buf and write to text file */
ee_convert_observer_record(buf, len, fpTxt);
/* release resources */
free(buf);
fclose(fpTxt);
fclose(fpBin);
return 0;
readerr:
free(buf);
memerr:
szerr:
fclose(fpTxt);
wferr:
fclose(fpBin);
rferr:
return -1;
}