/*!
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* All rights reserved.
*
* \file rndis_fsci.c
* This is the source file for the Serial Manager.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice, this list
*   of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice, this
*   list of conditions and the following disclaimer in the documentation and/or
*   other materials provided with the distribution.
*
* o Neither the name of Freescale Semiconductor, Inc. nor the names of its
*   contributors may be used to endorse or promote products derived from this
*   software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


/*! *********************************************************************************
*************************************************************************************
* Include
*************************************************************************************
********************************************************************************** */

#include "FsciInterface.h"
#include "FsciCommunication.h"
#include "FunctionLib.h"
#include "MemManager.h"
#include "rndis_fsci.h"
#include "rndis.h"

/*! *********************************************************************************
*************************************************************************************
* Private macros
*************************************************************************************
********************************************************************************** */
#define mMacAddrLen_c	6

/*! *********************************************************************************
*************************************************************************************
* Private type definitions
*************************************************************************************
********************************************************************************** */
typedef struct mFsciRegMacAddrReq_tag
{
  rndisMCastMacAddType_t addType; 
  bool_t                 write;
  uint8_t                addr[mMacAddrLen_c];
} mFsciRegMacAddrReq_t;

/*! *********************************************************************************
*************************************************************************************
* Private prototypes
*************************************************************************************
********************************************************************************** */
static void fsciRndisReqHandler(void *pData, void* param, uint32_t interfaceId);
static void RNDIS_Monitor( void* pBuff, uint16_t length );

#if gFsciHost_Rndis_c
static void fsciRndisCnfHandler(void *pData, void* param, uint32_t interfaceId);
static rndisStatus_t Rndis_CmdMonitor(uint8_t opCode, uint8_t* pBuff, uint16_t length);
static rndisStatus_t Rndis_GetCmdStatus(void);
#endif

/*! *********************************************************************************
*************************************************************************************
* Public memory declarations
*************************************************************************************
********************************************************************************** */

#if gFsciIncluded_c && gFsciHost_Rndis_c
#if gFsciHostSupport_c
  extern clientPacket_t *pFsciHostSyncRsp;

  #if gFsciHostSyncUseEvent_c
    extern event_t gFsciHostSyncRspEvent;
  #endif
#else
  #error Please enable also gFsciHostSupport_c to allow Host RNDIS module to work.
#endif
#endif

/*! *********************************************************************************
*************************************************************************************
* Private memory declarations
*************************************************************************************
********************************************************************************** */
static uint32_t mFsciInterface = mFsciInvalidInterface_c;

#if gFsciHost_Rndis_c
static uint32_t mFsciHostInterface = mFsciInvalidInterface_c;
static pfRNDISRxCallback_t mpfFsciRndisRxCb = NULL;
#endif

/*! *********************************************************************************
*************************************************************************************
* Public functions
*************************************************************************************
********************************************************************************** */

/*! *********************************************************************************
* \brief   This function registers a FSCI RNDIS module and request handler
*
* \param[in] fsci interfaceId
*
* \return None.
*
********************************************************************************** */
void fsciRegisterRndis( uint32_t fsciInterfaceId )
{
  mFsciInterface = fsciInterfaceId;
  
  /* Register Handler for requests coming from the serial interface */
  FSCI_RegisterOpGroup( gFSCI_RndisReqOpcodeGroup_c, 
                        gFsciMonitorMode_c, 
                        fsciRndisReqHandler, 
                        NULL, 
                        fsciInterfaceId );	
}

/*! *********************************************************************************
* \brief   This function registers a FSCI RNDIS module as host and confirm handler
*
* \param[in] fsci interfaceId
*
* \return None.
*
********************************************************************************** */
void fsciRegisterRndisToHost( uint32_t fsciInterfaceId )
{
#if gFsciHost_Rndis_c
  mFsciHostInterface = fsciInterfaceId;
  
  /* Register Handler for confirms and indications coming from the serial interface */
  FSCI_RegisterOpGroup( gFSCI_RndisCnfOpcodeGroup_c, 
                        gFsciMonitorMode_c, 
                        fsciRndisCnfHandler, 
                        NULL, 
                        fsciInterfaceId );	
#endif
}

/*! *********************************************************************************
* \brief   This function monitors RNDIS data indications and sends them over the serial interface
*
* \param[in] buffer received
* \param[in] length of buffer received
*
* \return None.
*
********************************************************************************** */
void RNDIS_Monitor(void* pBuff, uint16_t length)
{
#if gFsciIncluded_c
  clientPacket_t *pFsciPacket = NULL;
  uint8_t        *p = NULL;
  uint16_t       size = sizeof(clientPacketHdr_t) + sizeof(uint16_t) + length + 2 * sizeof(uint8_t);
  uint16_t       buffSize = MEM_BufferGetSize(pBuff);
  
  if( buffSize > size )
  {
    pFsciPacket = pBuff;
    p = pFsciPacket->structured.payload;
    FLib_MemInPlaceCpy(p+sizeof(uint16_t), pBuff, length);
    *p++ = length & 0xFF;
    *p++ = (length >> 8) & 0xFF;
  }
  else
  {
    pFsciPacket = MEM_BufferAlloc(size);
    if( pFsciPacket )
    {
        p = pFsciPacket->structured.payload;
        *p++ = length & 0xFF;
        *p++ = (length >> 8) & 0xFF;
        FLib_MemCpy(p, pBuff, length);
    }
  }
  
  if( pFsciPacket )
  {  
    pFsciPacket->structured.header.opGroup = gFSCI_RndisCnfOpcodeGroup_c;
    pFsciPacket->structured.header.opCode = mFsciRndisDataIndOpcode_c;
    pFsciPacket->structured.header.len = sizeof(uint16_t) + length;
    
    FSCI_transmitFormatedPacket(pFsciPacket, mFsciInterface);
  }
  
  if( pBuff != pFsciPacket )
  {
    MEM_BufferFree(pBuff);
  }
#endif
}

#if gFsciHost_Rndis_c
/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_Init()
{
  rndisStatus_t status = gRndis_Success_c;
  
  FSCI_HostSyncLock(mFsciHostInterface, gFSCI_RndisCnfOpcodeGroup_c, mFsciRndisInitCnfOpcode_c);
  status = Rndis_CmdMonitor(mFsciRndisInitReqOpcode_c, NULL, 0);
  if( gRndis_Success_c == status )
  {
    status = Rndis_GetCmdStatus();
  }
  FSCI_HostSyncUnlock(mFsciHostInterface);
  
  return status;
}

/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_Send(uint8_t* pBuff, uint16_t length)
{
  rndisStatus_t status = gRndis_Success_c;
  
  FSCI_HostSyncLock(mFsciHostInterface, gFSCI_RndisCnfOpcodeGroup_c, mFsciRndisSendCnfOpcode_c);
  status = Rndis_CmdMonitor(mFsciRndisSendReqOpcode_c, pBuff, length);
  if( gRndis_Success_c == status )
  {
    status = Rndis_GetCmdStatus();
  }
  FSCI_HostSyncUnlock(mFsciHostInterface);
  
  MEM_BufferFree(pBuff);
  
  return status;   
}

/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_SetDestMacAddress(uint8_t* pBuff)
{
  rndisStatus_t status = gRndis_Success_c;
  
  FSCI_HostSyncLock(mFsciHostInterface, gFSCI_RndisCnfOpcodeGroup_c, mFsciRndisSetDestMacAddrCnfOpcode_c);
  status = Rndis_CmdMonitor(mFsciRndisSetDestMacAddrReqOpcode_c, pBuff, mMacAddrLen_c);
  if( gRndis_Success_c == status )
  {
    status = Rndis_GetCmdStatus();
  }
  FSCI_HostSyncUnlock(mFsciHostInterface);
  
  return status;
}

/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_SetRndisMacAddress(uint8_t* pBuff)
{
  rndisStatus_t status = gRndis_Success_c;
  
  FSCI_HostSyncLock(mFsciHostInterface, gFSCI_RndisCnfOpcodeGroup_c, mFsciRndisSetRndisMacAddrCnfOpcode_c);
  status = Rndis_CmdMonitor(mFsciRndisSetRndisMacAddrReqOpcode_c, pBuff, mMacAddrLen_c);
  if( gRndis_Success_c == status )
  {
    status = Rndis_GetCmdStatus();
  }
  FSCI_HostSyncUnlock(mFsciHostInterface);
  
  return status;
}

/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_EnableIPv6(bool_t en)
{
  rndisStatus_t status = gRndis_Success_c;
  bool_t        enable = en;
  
  FSCI_HostSyncLock(mFsciHostInterface, gFSCI_RndisCnfOpcodeGroup_c, mFsciRndisEnableIpv6CnfOpcode_c);
  status = Rndis_CmdMonitor(mFsciRndisEnableIpv6ReqOpcode_c, &enable, sizeof(bool_t));
  if( gRndis_Success_c == status )
  {
    status = Rndis_GetCmdStatus();
  }
  FSCI_HostSyncUnlock(mFsciHostInterface);
  
  return status;
}

/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_EnableIPv4(bool_t en)
{
  rndisStatus_t status = gRndis_Success_c;
  bool_t        enable = en;
  
  FSCI_HostSyncLock(mFsciHostInterface, gFSCI_RndisCnfOpcodeGroup_c, mFsciRndisEnableIpv4CnfOpcode_c);
  status = Rndis_CmdMonitor(mFsciRndisEnableIpv4ReqOpcode_c, &enable, sizeof(bool_t));
  if( gRndis_Success_c == status )
  {
    status = Rndis_GetCmdStatus();
  }
  FSCI_HostSyncUnlock(mFsciHostInterface);
  
  return status;
}

/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_RegisterRxCallback(pfRNDISRxCallback_t pCallback)
{
  mpfFsciRndisRxCb = pCallback;
  
  return gRndis_Success_c;
}

/*! *********************************************************************************
* \brief   Host implementation of RNDIS API
*
* \return staus of the operation.
*
********************************************************************************** */
rndisStatus_t RNDIS_RegisterMulticastMacAddress(rndisMCastMacAddType_t addType, bool_t write, uint8_t* pBuff)
{
  rndisStatus_t         status = gRndis_Success_c;
  mFsciRegMacAddrReq_t  req;
  
  FSCI_HostSyncLock(mFsciHostInterface, gFSCI_RndisCnfOpcodeGroup_c, mFsciRndisRegisterMulticastMacAddrCnfOpCode_c);
  req.addType = addType;
  req.write = write;
  FLib_MemCpy(req.addr, pBuff, mMacAddrLen_c);
  status = Rndis_CmdMonitor(mFsciRndisRegisterMulticastMacAddrReqOpCode_c, (uint8_t*)&req, 0);
  if( gRndis_Success_c == status )
  {
    status = Rndis_GetCmdStatus();
  }
  FSCI_HostSyncUnlock(mFsciHostInterface);
  
  return status;
}
#endif

/*! *********************************************************************************
*************************************************************************************
* Private functions
*************************************************************************************
********************************************************************************* */

/*! *********************************************************************************
* \brief This function is the FSCI request handler for a black box running RNDIS
*
* \param[in] fsci packet received
* \param[in] parameter registered
* \param[in] fsci interface on which the packet was received
*
* \return
*
********************************************************************************** */
static void fsciRndisReqHandler(void *pData, void* param, uint32_t interfaceId)
{
  clientPacket_t *pFsciPacket = (clientPacket_t*)pData;
  uint8_t 		 *p = pFsciPacket->structured.payload;
  uint8_t	     *pBuff = NULL;
  uint8_t	     aMacAddr[mMacAddrLen_c];
  uint16_t		 length = 0;
  rndisStatus_t  status = gRndis_Success_c;
  bool_t         write = FALSE;
  rndisMCastMacAddType_t addType = rndisMCastMacAddType_IPv4;
  
  switch(pFsciPacket->structured.header.opCode)
  {
  case mFsciRndisInitReqOpcode_c:
    status = RNDIS_Init();
    if( gRndis_Success_c == status )
    {
      status = RNDIS_RegisterRxCallback(RNDIS_Monitor);
    }
    break;
    
  case mFsciRndisSendReqOpcode_c:
    length = *p++;
    length += *p++ << 8;
    pBuff = MEM_BufferAlloc(length);
    if( pBuff )
    {
      FLib_MemCpy(pBuff, p, length);
      status = RNDIS_Send(pBuff, length);
    }
    else
    {
      status = gRndis_OutOfMemory_c;
    }
    break;
    
  case mFsciRndisSetDestMacAddrReqOpcode_c:
    FLib_MemCpy(aMacAddr, p, mMacAddrLen_c);
    status = RNDIS_SetDestMacAddress(aMacAddr);
    break;
    
  case mFsciRndisSetRndisMacAddrReqOpcode_c:
    FLib_MemCpy(aMacAddr, p, mMacAddrLen_c);
    status = RNDIS_SetRndisMacAddress(aMacAddr);		
    break;
    
  case mFsciRndisEnableIpv6ReqOpcode_c:
    status = RNDIS_EnableIPv6((bool_t)*p);
    break;
    
  case mFsciRndisEnableIpv4ReqOpcode_c:
    status = RNDIS_EnableIPv4((bool_t)*p);
    break;
    
  case mFsciRndisRegisterMulticastMacAddrReqOpCode_c:
    addType = (rndisMCastMacAddType_t)*p++;
    write = (bool_t)*p++;
    FLib_MemCpy(aMacAddr, p, mMacAddrLen_c);
    status = RNDIS_RegisterMulticastMacAddress(addType, write, aMacAddr);
    break;
  }
  
  /* Reuse request FSCI message for confirm, op code is the same as the request */
  pFsciPacket->structured.header.opGroup = gFSCI_RndisCnfOpcodeGroup_c;
  pFsciPacket->structured.header.len = sizeof(rndisStatus_t);
  pFsciPacket->structured.payload[0] = status;
  
  FSCI_transmitFormatedPacket(pFsciPacket, interfaceId);
}

#if gFsciHost_Rndis_c
/*! *********************************************************************************
* \brief This function is the FSCI confirm handler(data indication) for a RNDIS host
*
* \param[in] fsci packet
* \param[in] parameter registered
* \param[in] fsci interface on which the packet was received
*
* \return
*
********************************************************************************** */
static void fsciRndisCnfHandler(void *pData, void* param, uint32_t interfaceId)
{
  uint8_t  *p = ((clientPacket_t*)pData)->structured.payload;
  uint16_t length = 0;
  
  switch(((clientPacket_t*)pData)->structured.header.opCode)
  {
  case mFsciRndisDataIndOpcode_c:
    length = *p++;
    length += *p++ << 8;
    FLib_MemInPlaceCpy(pData, p, length);
    mpfFsciRndisRxCb(pData, length);
    break;
  default:
    MEM_BufferFree(pData);
    break;
  }
}

/*! *********************************************************************************
* \brief This function sends a RNDIS command to the black box
*
* \param[in] op code of the RNDIS request
* \param[in] buffer with the request parameters
* \param[in] length of the buffer
*
* \return
*
********************************************************************************** */
static rndisStatus_t Rndis_CmdMonitor(uint8_t opCode, uint8_t* pBuff, uint16_t length)
{
  clientPacket_t  *pFsciPacket = NULL;
  uint8_t         *p = NULL;
  uint16_t        size = sizeof(clientPacketHdr_t) + 2 * sizeof(uint8_t);
  uint16_t        payloadLen = 0;
  rndisStatus_t   status = gRndis_Success_c;
  
  switch(opCode)
  {
  case mFsciRndisSendReqOpcode_c:
    payloadLen = sizeof(uint16_t) + length;
    break;
    
  case mFsciRndisSetDestMacAddrReqOpcode_c:
  case mFsciRndisSetRndisMacAddrReqOpcode_c:
    payloadLen = mMacAddrLen_c;
    break;
    
  case mFsciRndisEnableIpv6ReqOpcode_c:
  case mFsciRndisEnableIpv4ReqOpcode_c:
    payloadLen = sizeof(bool_t);
    break;
    
  case mFsciRndisRegisterMulticastMacAddrReqOpCode_c:
    payloadLen = sizeof(rndisMCastMacAddType_t) + sizeof(bool_t) + mMacAddrLen_c;
    break;
  }
  
  pFsciPacket = MEM_BufferAlloc(size + payloadLen);
  if( pFsciPacket )
  {
    pFsciPacket->structured.header.opGroup = gFSCI_RndisReqOpcodeGroup_c;
    pFsciPacket->structured.header.opCode = opCode;
    pFsciPacket->structured.header.len = payloadLen;
    p = pFsciPacket->structured.payload;
    
    switch(opCode)
    {
    case mFsciRndisSendReqOpcode_c:
      *p++ = length & 0xFF;
      *p++ = length >> 8;
      FLib_MemCpy(p, pBuff, length);
      break;
      
    case mFsciRndisSetDestMacAddrReqOpcode_c:
    case mFsciRndisSetRndisMacAddrReqOpcode_c:
      FLib_MemCpy(p, pBuff, mMacAddrLen_c);
      break;
      
    case mFsciRndisEnableIpv6ReqOpcode_c:
    case mFsciRndisEnableIpv4ReqOpcode_c:
      *p = *pBuff;
      break;
      
    case mFsciRndisRegisterMulticastMacAddrReqOpCode_c:
      *p++ = ((mFsciRegMacAddrReq_t*)pBuff)->addType;
      *p++ = ((mFsciRegMacAddrReq_t*)pBuff)->write;
      FLib_MemCpy(p, ((mFsciRegMacAddrReq_t*)pBuff)->addr, mMacAddrLen_c);
      break;
    }
    
    FSCI_transmitFormatedPacket(pFsciPacket, mFsciHostInterface);
  }
  else
  {
    status = gRndis_OutOfMemory_c;
  }
  
  return status;
}

/*! *********************************************************************************
* \brief This function gets a RNDIS command result from the black box
*
* \return status of the operation
*
********************************************************************************** */
static rndisStatus_t Rndis_GetCmdStatus(void)
{
  rndisStatus_t status = gRndis_Success_c;
  
  /* Wait for result from the serial interface */
  while( !pFsciHostSyncRsp )
  {
#if gFsciHostSyncUseEvent_c
    OSA_EventWait(&gFsciHostSyncRspEvent, 0x00FFFFFF, FALSE, OSA_WAIT_FOREVER, &fsciHostSyncRspEventFlags);
#else
    FSCI_receivePacket((void*)mFsciHostInterface);
#endif
    
    if( NULL == pFsciHostSyncRsp )
    {
      continue;
    }
    
    status = (rndisStatus_t)pFsciHostSyncRsp->structured.payload[0];
  }
  
  /* Free FSCI packet */
  MEM_BufferFree(pFsciHostSyncRsp);
  
  return status;    
}
#endif