/** @file */

// xos_syslog.h - XOS Event logging module.

// Copyright (c) 2003-2015 Cadence Design Systems, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// NOTE: Do not include this file directly in your application. Including
// xos.h will automatically include this file.


#ifndef __XOS_SYSLOG_H__
#define __XOS_SYSLOG_H__

#include "xos_types.h"

#ifdef __cplusplus
extern "C" {
#endif


//-----------------------------------------------------------------------------
//  The XOS system log is an array of fixed size entries. The size of the log
//  is determined by the application, and memory for the log must be provided
//  at init time. Every time the log function is called, an entry is made in
//  the log and the next pointer advanced. When the log is full, it will wrap
//  around and start overwriting the oldest entries.
//  Logging can be done from C/C++ code as well as assembly code, and at any
//  interrupt level, even from high level interrupt handlers.
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//  Defines.
//-----------------------------------------------------------------------------
#define XOS_SYSLOG_ENABLED              0x0001


///----------------------------------------------------------------------------
///
/// Use this macro to compute how much memory to allocate for the syslog.
///
///----------------------------------------------------------------------------
#define XOS_SYSLOG_SIZE(num_entries) \
    ( sizeof(XosSysLog) + ((num_entries - 1) * sizeof(XosSysLogEntry)) )


///----------------------------------------------------------------------------
///
///  System log entry structure.
///
///----------------------------------------------------------------------------
typedef struct XosSysLogEntry {
    uint32_t                timestamp;  ///< Timestamp in clock cycles
    uint32_t                param1;     ///< User defined value
    uint32_t                param2;     ///< User defined value
    struct XosSysLogEntry * next;       ///< Link to next entry
} XosSysLogEntry;


///----------------------------------------------------------------------------
///
///  System log structure.
///
///----------------------------------------------------------------------------
typedef struct XosSysLog {
    uint16_t         flags;             ///< Flags
    uint16_t         size;              ///< Number of entries
    XosSysLogEntry * next;              ///< Next write position
    XosSysLogEntry   entries[1];        ///< First entry
} XosSysLog;


//-----------------------------------------------------------------------------
//  Pointer to syslog area.
//-----------------------------------------------------------------------------
extern XosSysLog * xos_syslog;


//----------------------------------------------------------------------------
///
///  Initialize the syslog. Initializing the log also enables it. The system
///  log always wraps around when full and overwrites the oldest entries.
///
///  \param     log_mem         Pointer to allocated memory for the log.
///
///  \param     num_entries     The number of entries that the log can contain.
///
///  \return Returns nothing.
///
//----------------------------------------------------------------------------
static inline void
xos_syslog_init(void * log_mem, uint16_t num_entries)
{
    uint16_t i;

    xos_syslog = (XosSysLog *) log_mem;
    xos_syslog->size = num_entries;
    xos_syslog->next = xos_syslog->entries;

    for (i = 0; i < num_entries - 1; i++) {
        xos_syslog->entries[i].next = &(xos_syslog->entries[i+1]);
        xos_syslog->entries[i].timestamp = 0;
    }
    xos_syslog->entries[i].next = xos_syslog->entries;
    xos_syslog->entries[i].timestamp = 0;

    xos_syslog->flags = XOS_SYSLOG_ENABLED;
}


///----------------------------------------------------------------------------
///
///  Reset the syslog. All entries made up to now are abandoned and the write
///  pointer is set to the first entry location.
///
///  No parameters.
///
///  \return Returns nothing.
///
///----------------------------------------------------------------------------
static inline void
xos_syslog_clear()
{
#if XCHAL_HAVE_INTERRUPTS
    uint32_t ps = XT_RSIL(XCHAL_NUM_INTLEVELS);
#endif

    xos_syslog_init(xos_syslog, xos_syslog->size);
#if XCHAL_HAVE_INTERRUPTS
    xos_restore_int_pri_level(ps);
#endif
}


///----------------------------------------------------------------------------
///
///  Enable logging to the syslog. This function needs to be called only if
///  logging had been previously disabled via xos_syslog_disable(), since
///  initializing the syslog automatically enables it.
///
///  No parameters.
///
///  \return Returns nothing.
///
///----------------------------------------------------------------------------
static inline void
xos_syslog_enable() 
{
#if XCHAL_HAVE_INTERRUPTS
    uint32_t ps = XT_RSIL(XCHAL_NUM_INTLEVELS);
#endif

    xos_syslog->flags |= XOS_SYSLOG_ENABLED;
#if XCHAL_HAVE_INTERRUPTS
    xos_restore_int_pri_level(ps);
#endif
}


///----------------------------------------------------------------------------
///
///  Disable logging to the syslog. It is sometimes useful to disable logging
///  while the log is being examined or dumped.
///
///  No parameters.
///
///  \return Returns nothing.
///
///----------------------------------------------------------------------------
static inline void
xos_syslog_disable()
{
#if XCHAL_HAVE_INTERRUPTS
    uint32_t ps = XT_RSIL(XCHAL_NUM_INTLEVELS);
#endif
    xos_syslog->flags &= ~XOS_SYSLOG_ENABLED;
#if XCHAL_HAVE_INTERRUPTS
    xos_restore_int_pri_level(ps);
#endif
}


///----------------------------------------------------------------------------
///
///  Write an entry into the syslog. This function does disable all interrupts
///  since logging can be done from interrupt handlers as well. It will write
///  into the log only if the log exists and is enabled.
///
///  \param     param1              User defined value.
///
///  \param     param2              User defined value.
///
///  \return Returns nothing.
///
///----------------------------------------------------------------------------
static inline void
xos_syslog_write(uint32_t param1, uint32_t param2)
{
    if (xos_syslog != XOS_NULL) {
#if XCHAL_HAVE_INTERRUPTS
        uint32_t ps = XT_RSIL(XCHAL_NUM_INTLEVELS);
#endif

        if ((xos_syslog->flags & XOS_SYSLOG_ENABLED) != 0) {
            XosSysLogEntry * next = xos_syslog->next;

            next->timestamp = xos_get_ccount();
            next->param1    = param1;
            next->param2    = param2;

            xos_syslog->next = next->next;
        }

#if XCHAL_HAVE_INTERRUPTS
        xos_restore_int_pri_level(ps);
#endif
    }
}


///----------------------------------------------------------------------------
///
///  Read the first (oldest) entry in the syslog. Will return an error if the
///  log has not been created or is empty. Storage to copy the entry must be
///  provided by the caller.
///
///  \param     entry       Pointer to storage where the entry data will be
///                         copied. This pointer must be passed to 
///                         xos_syslog_get_next().
///
///  \return Returns XOS_OK on success, else error code.
///
///----------------------------------------------------------------------------
static inline int32_t
xos_syslog_get_first(XosSysLogEntry * entry)
{
    if (xos_syslog == XOS_NULL) {
        return XOS_ERR_NOT_FOUND;
    }

    if (entry != XOS_NULL) {
#if XCHAL_HAVE_INTERRUPTS
        uint32_t         ps   = XT_RSIL(XCHAL_NUM_INTLEVELS);
#endif
        XosSysLogEntry * next = xos_syslog->next;

        // 'next' should be pointing to the next entry to be overwritten, if we
        // have wrapped. This means it is the oldest entry. However if this entry
        // has a zero timestamp then we have not wrapped, in which case we must
        // look at the first entry in the list.
        if (next->timestamp == 0) {
            next = xos_syslog->entries;
        }

        *entry = *next;
#if XCHAL_HAVE_INTERRUPTS
        xos_restore_int_pri_level(ps);
#endif
        return entry->timestamp ? XOS_OK : XOS_ERR_NOT_FOUND;
    }

    return XOS_ERR_INVALID_PARAMETER;
}


///----------------------------------------------------------------------------
///
///  Get the next sequential entry from the syslog. This function must be called
///  only after xos_syslog_get_first() has been called.
///
///  \param     entry       Pointer to storage where entry data will be copied.
///                         Must be the same pointer that was passed in the call
///                         to xos_syslog_get_first(), as it is used to keep track
///                         of the current position.
///
///  \return Returns XOS_OK on success, else error code.
///
///----------------------------------------------------------------------------
static inline int32_t
xos_syslog_get_next(XosSysLogEntry * entry)
{
    if (entry != XOS_NULL) {
#if XCHAL_HAVE_INTERRUPTS
        uint32_t         ps   = XT_RSIL(XCHAL_NUM_INTLEVELS);
#endif
        XosSysLogEntry * next = entry->next;
        int32_t          ret  = XOS_OK;

        // Make sure we're not pointing past the last entry.
        if ((next != XOS_NULL) && (next != xos_syslog->next) && (next->timestamp != 0)) {
            *entry = *next;
        }
        else {
            ret = XOS_ERR_NOT_FOUND;
        }
#if XCHAL_HAVE_INTERRUPTS
        xos_restore_int_pri_level(ps);
#endif
        return ret;
    }

    return XOS_ERR_INVALID_PARAMETER;
}


#ifdef __cplusplus
}
#endif

#endif // __XOS_SYSLOG_H__