/***************************************************************************//**
 * @file
 * @brief Cyclic Redundancy Check (CRC) API.
 * @version 4.2.1
 *******************************************************************************
 * @section License
 * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
 *******************************************************************************
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
 * obligation to support this Software. Silicon Labs is providing the
 * Software "AS IS", with no express or implied warranties of any kind,
 * including, but not limited to, any implied warranties of merchantability
 * or fitness for any particular purpose or warranties against infringement
 * of any proprietary rights of a third party.
 *
 * Silicon Labs will not be liable for any consequential, incidental, or
 * special damages, or any other relief, or for any claim by any third party,
 * arising from your use of this Software.
 *
 ******************************************************************************/

#ifndef __SILICON_LABS_EM_CRC_H__
#define __SILICON_LABS_EM_CRC_H__

#include "em_device.h"
#if defined(CRC_COUNT) && (CRC_COUNT > 0)

#include <stdint.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/***************************************************************************//**
 * @addtogroup EM_Library
 * @{
 ******************************************************************************/

/***************************************************************************//**
 * @addtogroup CRC
 * @{
 ******************************************************************************/

/*******************************************************************************
 ********************************   ENUMS   ************************************
 ******************************************************************************/

/** CRC width values. */
typedef enum
{
  /** 8 bit (1 byte) CRC code. */
  crcWidth8 = CRC_CTRL_CRCWIDTH_CRCWIDTH8,

  /** 16 bit (2 byte) CRC code. */
  crcWidth16 = CRC_CTRL_CRCWIDTH_CRCWIDTH16,

  /** 24 bit (3 byte) CRC code. */
  crcWidth24 = CRC_CTRL_CRCWIDTH_CRCWIDTH24,

  /** 32 bit (4 byte) CRC code. */
  crcWidth32 = CRC_CTRL_CRCWIDTH_CRCWIDTH32
} CRC_Width_TypeDef;


/** CRC byte reverse values. */
typedef enum
{
  /** Most significant CRC bytes are transferred first over air via the Frame
   *  Controller (FRC). */
  crcByteOrderNormal = CRC_CTRL_BYTEREVERSE_NORMAL,

  /** Least significant CRC bytes are transferred first over air via the Frame
   *  Controller (FRC). */
  crcByteOrderReversed = CRC_CTRL_BYTEREVERSE_REVERSED
} CRC_ByteOrder_TypeDef;


/** CRC bit order values. */
typedef enum
{
  /** Least significant data bit (LSB) is fed first to the CRC generator. */
  crcBitOrderLSBFirst = CRC_CTRL_INPUTBITORDER_LSBFIRST,

  /** Most significant data bit (MSB) is fed first to the CRC generator. */
  crcBitOrderMSBFirst = CRC_CTRL_INPUTBITORDER_MSBFIRST
} CRC_BitOrder_TypeDef;


/** CRC bit reverse values. */
typedef enum
{
  /** The bit ordering of CRC data is the same as defined by the BITORDER field
   *  in the Frame Controller. */
  crcBitReverseNormal = CRC_CTRL_BITREVERSE_NORMAL,

  /** The bit ordering of CRC data is the opposite as defined by the BITORDER
   *  field in the Frame Controller. */
  crcBitReverseReversed = CRC_CTRL_BITREVERSE_REVERSED
} CRC_BitReverse_TypeDef;


/*******************************************************************************
 *******************************   STRUCTS   ***********************************
 ******************************************************************************/

/** CRC initialization structure. */
typedef struct
{
  /** Width of the CRC code. */
  CRC_Width_TypeDef         crcWidth;

  /** CRC polynomial value. This value defines POLY[31:0], which is used as the
   *  polynomial (in reversed order) during the CRC calculation. If the CRC
   *  width is less than 32 bits, the most significant part of this register
   *  remains unused.
   *  - Set the bit to 1 in the register to get the corresponding degree term
   *  appear in the polynomial with a coefficient of 1.
   *  - Set the bit to 0 in the register to get the corresponding degree term
   *  appear in the polynomial with a coefficient of 0.
   *  Note: If a CRC polynomial of size less than 32 bits is to be used, the
   *  polynomial value must be shifted so that the highest degree term is
   *  located in DATA[0]!
   *  Please refer to the CRC sub-chapter "CRC Polynomial" in the documentation
   *  for more details! */
  uint32_t                   crcPoly;

  /** CRC initialization value. Loaded into the CRC_DATA register upon issuing
   *  the INIT command by calling CRC_InitCommand(), or when the Frame
   *  Controller (FRC) uses the CRC for automatic CRC calculation and
   *  verification. */
  uint32_t                   initValue;

  /** Number of bits per input word. This value defines the number of valid
   *  input bits in the CRC_INPUTDATA register, or in data coming from the Frame
   *  Controller (FRC). The number of bits in each word equals to
   *  (BITSPERWORD + EXTRABITSPERWORD + 1), where EXTRABITSPERWORD is taken from
   *  the currently active Frame Control Descriptor (FCD). */
  uint8_t                    bitsPerWord;

  /** If true, the byte order is reversed and the least significant CRC bytes
   *  are transferred first over the air. (description TBD) */
  CRC_ByteOrder_TypeDef      byteReverse;

  /** Bit order. Defines the order in which bits are fed to the CRC generator.
   *  This setting applies both to data written to the CRC_INPUTDATA register,
   *  and data coming from the Frame Controller (FRC). */
  CRC_BitOrder_TypeDef       inputBitOrder;

  /** Output bit reverse. In most cases, the bit ordering of the CRC value
   *  corresponds to the bit ordering of other data transmitted over air. When
   *  set, the BITREVERSE field has the possibility to reverse this bit ordering
   *  to comply with some protocols. Note that this field does not affect the
   *  way the CRC value is calculated, only how it is transmitted over air. */
  CRC_BitReverse_TypeDef     bitReverse;

  /** Enable/disable CRC input data padding. When set, CRC input data is zero-
   *  padded, such that the number of bytes over which the CRC value is
   *  calculated at least equals the length of the calculated CRC value. If not
   *  set, no zero-padding of CRC input data is applied. */
  bool                       inputPadding;

  /** If true, CRC input is inverted. */
  bool                       invInput;

  /** If true, CRC output to the Frame Controller (FRC) is inverted. */
  bool                       invOutput;
} CRC_Init_TypeDef;

/** Default configuration for CRC_Init_TypeDef structure. */
#define CRC_INIT_DEFAULT                                              \
{                                                                     \
  crcWidth16,           /* CRC width is 16 bits. */                   \
  0x00008408UL,         /* Polynomial value of IEEE 802.15.4-2006. */ \
  0x00000000UL,         /* Initialization value. */                   \
  8U,                   /* 8 bits per word. */                        \
  crcByteOrderNormal,   /* Byte order is normal. */                   \
  crcBitOrderLSBFirst,  /* Bit order (TBD). */                        \
  crcBitReverseNormal,  /* Bit order is not reversed on output. */    \
  false,                /* No zero-padding. */                        \
  false,                /* Input is not inverted. */                  \
  false                 /* Output is not inverted. */                 \
}


/*******************************************************************************
 ******************************   PROTOTYPES   *********************************
 ******************************************************************************/

void CRC_Init(CRC_Init_TypeDef const *init);
void CRC_Reset(void);

/***************************************************************************//**
 * @brief
 *   Issues a command to initialize the CRC calculation.
 *
 * @details
 *   This function issues the command INITIALIZE in CRC_CMD that initializes the
 *   CRC calculation by writing the initial values to the DATA register.
 *
 * @note
 *   Internal notes:
 *   Initialize in CRC_CMD
 *   Conclude on reference of parameters. Register names or config struct members?
 ******************************************************************************/
__STATIC_INLINE void CRC_InitCommand(void)
{
  CRC->CMD = CRC_CMD_INITIALIZE;
}


/***************************************************************************//**
 * @brief
 *   Set the initialization value of the CRC.
 ******************************************************************************/
__STATIC_INLINE void CRC_InitValueSet(uint32_t initValue)
{
  CRC->INIT = initValue;
}


/***************************************************************************//**
 * @brief
 *   Writes data to the input data register of the CRC.
 *
 * @details
 *   Use this function to write input data to the CRC when the FRC is not being
 *   used for automatic handling of the CRC. The CRC calculation is based on
 *   the provided input data using the configured CRC polynomial.
 *
 * @param[in] data
 *   Data to be written to the input data register.
 ******************************************************************************/
__STATIC_INLINE void CRC_InputDataWrite(uint16_t data)
{
  CRC->INPUTDATA = (uint32_t)data;
}


/***************************************************************************//**
 * @brief
 *   Reads the data register of the CRC.
 *
 * @details
 *   Use this function to read the calculated CRC value.
 *
 * @return
 *   Content of the CRC data register.
 ******************************************************************************/
__STATIC_INLINE uint32_t CRC_DataRead(void)
{
  return CRC->DATA;
}


/***************************************************************************//**
 * @brief
 *   Gets if the CRC is busy.
 *
 * @details
 *   Returns true when the CRC module is busy, false otherwise.
 *
 * @return
 *   CRC busy flag.
 *   @li true - CRC module is busy.
 *   @li false - CRC module is not busy.
 ******************************************************************************/
__STATIC_INLINE bool CRC_BusyGet(void)
{
  return (bool)((CRC->STATUS & _CRC_STATUS_BUSY_MASK)
                >> _CRC_STATUS_BUSY_SHIFT);
}


/** @} (end addtogroup CRC) */
/** @} (end addtogroup EM_Library) */

#ifdef __cplusplus
}
#endif

#endif /* defined(CRC_COUNT) && (CRC_COUNT > 0) */
#endif /* __SILICON_LABS_EM_CRC_H__ */
