/****************************************************************************//**
  \file phy.h

  \brief Phy access APIs for en/decryption - SAMR21

  \author
    Atmel Corporation: http://www.atmel.com \n
    Support email: avr@atmel.com

  Copyright (c) 2015, Atmel Corporation. All rights reserved.
  Licensed under Atmel's Limited License Agreement (BitCloudTM).

  \internal
  History:
    26/06/14 Srinivasan G - Created
*******************************************************************************/

#ifndef _PHY_H_
#define _PHY_H_

#if (USE_IMAGE_SECURITY == 1)

#include <gpio.h>

/******************************************************************************
                   Type Definitions section
******************************************************************************/
/** AES modes */
typedef enum {
  AES_MODE_ECB = (0),
  AES_MODE_CBC = (2),
  AES_MODE_UNKNOWN
} aesModeType_t;

/** AES direction */
typedef enum {
  AES_DIR_ENCRYPT = (0),
  AES_DIR_DECRYPT = (1),
  AES_DIR_UNKNOWN
} aesDirectionType_t;

/** En/Decryption request structure */
typedef struct
{
  //! Pointer to input data for AES command.
  uint8_t *text;

  //! Size of input data in blocks(128-bit)
  uint8_t blockCount;
} endecryptReq_t;

/******************************************************************************
                   Implementation section
******************************************************************************/
/**************************************************************************//**
 \brief Write a value to SPI

 \param[in] value to be written
**************************************************************************/
static inline uint8_t HAL_PhySpiWriteByteInline(uint8_t value)
{
  SERCOM4->SPI.DATA.reg = value;
  while (!SERCOM4->SPI.INTFLAG.bit.RXC);
  return SERCOM4->SPI.DATA.reg;
}

/**************************************************************************//**
 \brief Delay(busy wait) in micro-seconds

 \param[in] micro-seconds delay intended
**************************************************************************/
static inline void HAL_Delay(uint32_t us)
{
  uint32_t i;

  for(i = 0; i < us; i++)
  {
    asm volatile ("nop");
    asm volatile ("nop");
    asm volatile ("nop");
    asm volatile ("nop");
  }
}

/*****************************************************************************
 \brief function to check the SPI CTRL B BUSY status

 \return true - sync busy, false - not busy
*****************************************************************************/
static inline bool halSpiCtrlBSyncing()
{
  return (SC0_SPI_SYNCBUSY & SC0_SPI_SYNCBUSY_CTRLB);
}

/*****************************************************************************
 \brief function to check the SPI ENABLE BUSY status

 \return true - sync busy enabled, false - not enabled
*****************************************************************************/
static inline bool halSpiEnableSyncing()
{
  return (SC0_SPI_SYNCBUSY & SC0_SPI_SYNCBUSY_ENABLE);
}

/*****************************************************************************
 \brief function to check the SPI ENABLE BUSY status

 \return true - sync busy set, false - sync busy reset
*****************************************************************************/
static inline bool halSpiSwrstSyncing()
{
  return (SC0_SPI_SYNCBUSY & SC0_SPI_SYNCBUSY_SWRST);
}

/**************************************************************************//**
 \brief Phy access(through GPIO) static functions

  These macros define set, clear, toggle, in, out, pullup, read and few
  other reg operations
**************************************************************************/
HW_ASSIGN_PIN(PHY_RST,    B, 15);
HW_ASSIGN_PIN(PHY_IRQ,    B, 0);
HW_ASSIGN_PIN(PHY_SLP_TR, A, 20);
HW_ASSIGN_PIN(PHY_CS,     B, 31);
HW_ASSIGN_PIN(PHY_MISO,   C, 19);
HW_ASSIGN_PIN(PHY_MOSI,   B, 30);
HW_ASSIGN_PIN(PHY_SCK,    C, 18);

/******************************************************************************
                   constants section
******************************************************************************/
/** Key length */
#define KEY_SIZE                                (16U)
#define AES_BLOCKSIZE                           KEY_SIZE

/******************************************************************************

AES_CTRL:

position:      7      6     5     4     3     2     1     0
            -------------------------------------------------
meaning:    | REQ |  M2 |  M1 |  M0 | DIR |  R  |  R  |  R  |
            -------------------------------------------------

M2 M1 M0
0  0  0  - ECB
0  0  1  - key operation
0  1  0  - CBC

DIR
0  - Encryption
1  - Decryption

REQ
0  - idle
1  - start operation

-------------------------------------------------------------------------------

AES_STATUS:

position:      7      6     5     4     3     2     1     0
            -------------------------------------------------
meaning:    | ERR |  R  |  R  |  R  |  R  |  R  |  R  | DON |
            -------------------------------------------------

ERR
0  - no error
1  - error

DON
0  - operation not completed
1  - operation completed

******************************************************************************/
#define MODEBYTE_FOR_KEY_OP                       (0x10)
#define MODEBYTE_FOR_ECBDECRYPT                   (0x08)
#define MODEBYTE_FOR_ECBENCRYPT                   (0x00)
#define MODEBYTE_FOR_START_OP                     (0x80)

#define RF_CMD_SRAM_W                             (0x40)
#define RF_CMD_SRAM_R                             (0)

#define AES_CORE_CYCLE_TIME_US                    (24)

#define AES_STATUS_REG                            (0x82)
#define AES_CTRL_REG                              (0x83)
#define AES_KEY_REG                               (0x84)
#define AES_STATE_REG                             (0x84)
#define AES_CTRL_M_REG                            (0x94)

#define GPIO_PORTA                                0
#define GPIO_PORTB                                1
#define GPIO_PORTC                                2

#define HAL_RESET_DELAY                           10
#define CLKCTRL_SERCOM_ID                         0x18
#define CLKCTRL_GENERTR_VAL                       0
#define CLKCTRL_GENERTR_CLKEN                     1
#define SERCOM4_SPI_CTRLA_MODE_VAL                3
#define SERCOM4_SPI_CTRLA_DOPO_VAL                1
#define PORTB_PMUX15_PMUXE_VALUE                  5
#define PORTC_PMUX9_PMUXE_VALUE                   5
#define PORTC_PMUX9_PMUXO_VALUE                   5

/**************** Phy access APIs ********************************************/
#define PHY_SPI_SELECT()          GPIO_PHY_CS_clr()
#define PHY_SPI_WRITE_BYTE(value) HAL_PhySpiWriteByteInline((value))
#define PHY_SPI_DESELECT()        GPIO_PHY_CS_set()
#define HAL_DELAY(x)              HAL_Delay(x)

/**************** AES engine access recipes **********************************/
#define WRITE_AES_KEY(key)                                      \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_W);                            \
  PHY_SPI_WRITE_BYTE(AES_CTRL_REG);                             \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_KEY_OP);                      \
  for (uint8_t i = 0; i < AES_BLOCKSIZE; i++)                   \
    PHY_SPI_WRITE_BYTE(key[i]);                                 \
  PHY_SPI_DESELECT();

#define WRITE_AES_ZERO_KEY()                                    \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_W);                            \
  PHY_SPI_WRITE_BYTE(AES_CTRL_REG);                             \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_KEY_OP);                      \
  for (uint8_t i = 0; i < AES_BLOCKSIZE; i++)                   \
    PHY_SPI_WRITE_BYTE(0);                                      \
  PHY_SPI_DESELECT();

#define DO_ECB_DECRYPT(data)                                    \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_W);                            \
  PHY_SPI_WRITE_BYTE(AES_CTRL_REG);                             \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_ECBDECRYPT);                  \
  for (uint8_t i = 0; i < AES_BLOCKSIZE; i++)                   \
    PHY_SPI_WRITE_BYTE(data[i]);                                \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_START_OP|MODEBYTE_FOR_ECBDECRYPT); \
  PHY_SPI_DESELECT();

#define READ_AES_KEY(key)                                       \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_W);                            \
  PHY_SPI_WRITE_BYTE(AES_CTRL_REG);                             \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_KEY_OP);                      \
  PHY_SPI_DESELECT();                                           \
                                                                \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_R);                            \
  PHY_SPI_WRITE_BYTE(AES_STATE_REG);                            \
  for (uint8_t i = 0; i < AES_BLOCKSIZE; i++)                   \
    dataMinusOne[i] = PHY_SPI_WRITE_BYTE(0);                    \
  PHY_SPI_DESELECT();

#define READ_AES_DATA(data)                                     \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_R);                            \
  PHY_SPI_WRITE_BYTE(AES_STATE_REG);                            \
  for (uint8_t i = 0; i < AES_BLOCKSIZE; i++)                   \
    data[i] = PHY_SPI_WRITE_BYTE(0);                            \
  PHY_SPI_DESELECT();

#define DO_ECB_ENCRYPT(data)                                    \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_W);                            \
  PHY_SPI_WRITE_BYTE(AES_CTRL_REG);                             \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_ECBENCRYPT);                  \
  for (uint8_t i = 0; i < AES_BLOCKSIZE; i++)                   \
    PHY_SPI_WRITE_BYTE(data[i]);                                \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_START_OP|MODEBYTE_FOR_ECBENCRYPT); \
  PHY_SPI_DESELECT();

#define DO_ECB_ZERO_ENCRYPT()                                   \
  PHY_SPI_SELECT();                                             \
  PHY_SPI_WRITE_BYTE(RF_CMD_SRAM_W);                            \
  PHY_SPI_WRITE_BYTE(AES_CTRL_REG);                             \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_ECBENCRYPT);                  \
  for (uint8_t i = 0; i < AES_BLOCKSIZE; i++)                   \
    PHY_SPI_WRITE_BYTE(0);                                      \
  PHY_SPI_WRITE_BYTE(MODEBYTE_FOR_START_OP|MODEBYTE_FOR_ECBENCRYPT); \
  PHY_SPI_DESELECT();

#define WAIT_FOR_AES_OP_END()                                   \
  HAL_DELAY(AES_CORE_CYCLE_TIME_US)

/******************************************************************************
                   extern variables section
******************************************************************************/
/* payload for the API call */
extern endecryptReq_t req;

/******************************************************************************
                   Prototypes section
******************************************************************************/

/**************************************************************************//**
 \brief Setup AES unit

  This function performs the following tasks as part of the setup of the
  AES unit: key initialization, set encryption direction and encryption mode.

 \return  False if some parameter was illegal or operation failed, true else
**************************************************************************/
bool aesSetup(void);

/**************************************************************************//**
 \brief Reads the result of previous AES en/decryption
        This function returns the result of the previous AES operation

 \param[out] data - result of previous operation
**************************************************************************/
void aesRead(uint8_t *data);

/**************************************************************************//**
\brief En/decrypt one AES block.
       The function returns after the AES operation is finished.

       AES CBC decryption flow :

       key cipher1----              key cipher2----
        |     |       |              |     |
       ---------      |             ---------
       |decrypt|      |             |decrypt|
       ---------      |             ---------
           |          |                 |               .......
  iv ---- XOR         ---------------- XOR
           |                            |
      Plain text 1                  plain text 2

\param[in/out]  data - AES block to be en/decrypted
******************************************************************************/
void aesExec(uint8_t *data);

/****************** external functions ************************************/

/**************************************************************************//**
\brief Reads the last or previous 128-bit data processed

\return
  true - succesfull read operation
  false - failed to read
***************************************************************************/
bool phyReadLastEncData(uint8_t *data);

/**************************************************************************//**
\brief Clear any carry-forward data/feedback

\return
  true - succesfull operation
  false - failed
***************************************************************************/
bool phyClearHistory(void);

/**************************************************************************//**
\brief Phy call to encrypt/decrypt a cipher block

\param
  [in] cipher block
  [in] number of blocks of 16bytes

\return
  true - succesful en/decryption
  false - failed operation
***************************************************************************/
bool phyEnDecrypt(void);

/**************************************************************************//**
\brief Phy call to set mode for en/decryption

\param
  [in] AES mode
  [in] AES direction

\return
  true - succesfully set
  false - failed to set
***************************************************************************/
bool phySetModeAndDir(aesModeType_t mode, aesDirectionType_t dir);

/**************************************************************************//**
\brief Phy call to plumb key and IV for en/decryption

\param
  [in] 128-bit key
  [in] 128-bit IV

\return
  true - succesfully set
  false - failed to set
***************************************************************************/
bool phySetKeyAndIv(uint8_t *inKey, uint8_t *inIv);

#endif /* #if (USE_IMAGE_SECURITY == 1) */
#endif /* _PHY_H_ */

//eof phy.h
