/* * Copyright STREAMIT BV, 2010. * * Project : SIR * Module : Log * File name $Workfile: Log.c $ * Last Save $Date: 2003/08/16 15:01:19 $ * $Revision: 0.1 $ * Creation Date : 2003/08/16 15:01:19 * * Description : Keeps track of log messages * As an initial implementation this module * outputs messages to the serial port and uses * no buffering. (As a result logging delays execution * as long as the serial write takes) * At a later stage this module will have a logging * queue. This will not have much impact on execution * time. It will output messages to either a serial port * or telnet client. * */ /*--------------------------------------------------------------------------*/ /* Include files */ /*--------------------------------------------------------------------------*/ #define LOG_MODULE LOG_LOG_MODULE #include #include #include #include #include #include //#pragma text:appcode #include "uart0driver.h" //#include "settings.h" #include "log.h" /*--------------------------------------------------------------------------*/ /* Constant definitions */ /*--------------------------------------------------------------------------*/ /*!\brief Max length of address */ #define MAX_OFFSET_LEN 8 /*!\brief Max byte values printed on a line */ #define BYTES_PER_LINE 16 /*!\brief Max number of characters hex dump takes: 2 digits plus trailing blank */ #define HEX_DUMP_LEN (BYTES_PER_LINE*3) /*!\brief Number of characters hex dump + ascii take: 3 chars, 2 blanks, 1 char */ #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE) /*!\brief Number of characters per line: address, 2 blanks, data dump */ #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN) #define LEVEL_MASK 0x07 // b0...b2 #define NAME_MASK 0xF8 // b3..b7 /*--------------------------------------------------------------------------*/ /* Type declarations */ /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ /* Local variables */ /*--------------------------------------------------------------------------*/ /*!\brief The current log level */ static TLogLevel g_tLevel; /*!\brief Stream to output the log data to */ static FILE *g_tStream; /*--------------------------------------------------------------------------*/ /* Global variables */ /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ /* Local functions */ /*--------------------------------------------------------------------------*/ /*! * \brief Return the prefix-level for the given log level. * * \param tLevel [in] The log level. * * \return Pointer to a string in program space. */ static PGM_P LogPrefixLevel_P(TLogLevel tLevel) { switch (tLevel) { case LOG_EMERG_LEV :return(PSTR("\n#Emerg ")); case LOG_ALERT_LEV :return(PSTR("\n#Alert ")); case LOG_CRIT_LEV :return(PSTR("\n#Crit ")); case LOG_ERR_LEV :return(PSTR("\n#Err ")); case LOG_WARNING_LEV :return(PSTR("\n#Warn ")); case LOG_NOTICE_LEV :return(PSTR("\n#Notic ")); case LOG_INFO_LEV :return(PSTR("\n#Info ")); case LOG_DEBUG_LEV :return(PSTR("\n#Debug ")); default :return(PSTR("\n")); } } /*! * \brief Return the prefix-name for the given log module. * * \param tLevel [in] The log module. * * \return Pointer to a string in program space. */ static PGM_P LogPrefixName_P(TLogLevel tLevel) { switch (tLevel) { case LOG_AUDIO_MODULE :return(PSTR("AU: ")); case LOG_CHANNEL_MODULE :return(PSTR("CH: ")); case LOG_COMAND_MODULE :return(PSTR("CM: ")); case LOG_DISPLAY_MODULE :return(PSTR("DP: ")); case LOG_FAT_MODULE :return(PSTR("FA: ")); case LOG_FLASH_MODULE :return(PSTR("FL: ")); case LOG_HTTP_MODULE :return(PSTR("HT: ")); case LOG_INET_MODULE :return(PSTR("IN: ")); case LOG_KEYBOARD_MODULE :return(PSTR("KB: ")); case LOG_LED_MODULE :return(PSTR("LE: ")); case LOG_LOG_MODULE :return(PSTR("LG: ")); case LOG_MAIN_MODULE :return(PSTR("SY: ")); case LOG_MENU_MODULE :return(PSTR("ME: ")); case LOG_MMC_MODULE :return(PSTR("MM: ")); case LOG_MMCDRV_MODULE :return(PSTR("MD: ")); case LOG_NTP_MODULE :return(PSTR("NT: ")); case LOG_PARSE_MODULE :return(PSTR("PA: ")); case LOG_PLAYER_MODULE :return(PSTR("PL: ")); case LOG_REMCON_MODULE :return(PSTR("RC: ")); case LOG_RTC_MODULE :return(PSTR("RT: ")); case LOG_SELFTEST_MODULE :return(PSTR("ST: ")); case LOG_SESSION_MODULE :return(PSTR("SE: ")); case LOG_SETTINGS_MODULE :return(PSTR("SG: ")); case LOG_SPIDRV_MODULE :return(PSTR("SP: ")); case LOG_STREAMER_MODULE :return(PSTR("SR: ")); case LOG_UART0DRIVER_MODULE :return(PSTR("UA: ")); case LOG_UPDATE_MODULE :return(PSTR("UD: ")); case LOG_UTIL_MODULE :return(PSTR("UT: ")); case LOG_VERSION_MODULE :return(PSTR("VE: ")); case LOG_VS10XX_MODULE :return(PSTR("VS: ")); case LOG_WATCHDOG_MODULE :return(PSTR("WD: ")); default :return(PSTR("?? ")); } } /*--------------------------------------------------------------------------*/ /* Global functions */ /*--------------------------------------------------------------------------*/ /*! * \brief Initialises this module * * \param - * * \return - */ void LogInit(void) { /* Set default level */ g_tLevel = LOG_DEBUG_LEV; LogOpen(); } /*! * \brief Opens the module for use. * * \param - * * \return - */ void LogOpen(void) { /* Associate our stream with a device */ g_tStream = Uart0DriverGetStream(); } /*! * \brief Closes the module. * * All interface functions from this module will result in void * operations. * * \param - * * \return - */ void LogClose(void) { FILE *tPrevStream = g_tStream; /* Don't allow adding of new output. */ g_tStream = NULL; /* Finish all current output. */ fflush(tPrevStream); } /*! * \brief Log a message to the log medium using a fixed string. * * The fixed string must reside in program space. It is parsed * using the rules of (s)printf. * * \param tLevel priority level of the message. * \param szMsg format string of the message. * \param ... arguments to the format string. */ void LogMsg_P(TLogLevel tLevel, PGM_P szMsg, ...) { va_list ap; if (g_tStream) { /* Log the string if the message is more important than the current level */ if ((tLevel&LEVEL_MASK) <= g_tLevel) { fputs_P(LogPrefixLevel_P(tLevel&LEVEL_MASK), g_tStream); fputs_P(LogPrefixName_P(tLevel&NAME_MASK), g_tStream); va_start(ap, szMsg); vfprintf_P(g_tStream, szMsg, ap); va_end(ap); } } } void LogChar_P(const char bChar) { if (g_tStream) { fputc(bChar, g_tStream); } } /*! * \brief Set the priority level * * \param tNewLevel New priority level * * \return The previous priority level */ TLogLevel LogSetLevel(TLogLevel tNewLevel) { TLogLevel tPrevLevel = g_tLevel; if (tNewLevel <= LOG_DEBUG_LEV) { g_tLevel = tNewLevel; } return(tPrevLevel); } /*! * \brief Print a block of memory * * Prints out 16 bytes of data per line * Every line starts with the address (offset), * the data in hex and that same data in ascii. * * \param tStream The stream to print to * \param cp The data * \param length The length of the data * * \return none */ void HexDump(FILE *tStream, CONST u_char *cp, size_t length) { register unsigned int address, i, hex_pos, ascii_pos, l; unsigned int address_len; unsigned char c; char line[MAX_LINE_LEN + 1]; static CONST char binhex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; if (tStream != NULL) { /* * How many digits do we need for the address? * We use at least 4 digits or more as needed */ if (((length - 1) & 0xF0000000) != 0) { address_len = 8; } else if (((length - 1) & 0x0F000000) != 0) { address_len = 7; } else if (((length - 1) & 0x00F00000) != 0) { address_len = 6; } else if (((length - 1) & 0x000F0000) != 0) { address_len = 5; } else { address_len = 4; } address = 0; i = 0; hex_pos = 0; ascii_pos = 0; while (i < length) { if ((i & 15) == 0) { /* * Start of a new line. */ memset(line, ' ', sizeof(line)); hex_pos = 0; ascii_pos = 0; l = address_len; do { l--; c = (address >> (l*4)) & 0xF; line[hex_pos++] = binhex[c]; } while (l != 0); /* 2 spaces */ hex_pos += 2; /* * Offset in line of ASCII dump. */ ascii_pos = hex_pos + HEX_DUMP_LEN + 2; } c = *cp++; /* Dump the hex value */ line[hex_pos++] = binhex[c >> 4]; line[hex_pos++] = binhex[c & 0xF]; hex_pos++; /* Print the ascii value */ line[ascii_pos++] = (c >= ' ' && c < 127) ? c : '.'; i++; if ((i & 15) == 0 || i == length) { /* * We'll be starting a new line, or * we're finished printing this buffer; * dump out the line we've constructed, * and advance the offset. */ line[ascii_pos] = '\0'; fputs(line, tStream); fputc('\n', tStream); address += BYTES_PER_LINE; } } } }