/******************************************************************************
 *
 * Copyright 2013, Silicon Image, Inc.  All rights reserved.
 * No part of this work may be reproduced, modified, distributed, transmitted,
 * transcribed, or translated into any language or computer format, in any form
 * or by any means without written permission of
 * Silicon Image, Inc., 1140 East Arques Avenue, Sunnyvale, California 94085
 *
 *****************************************************************************/
/**
 * @file si_drv_ipc.c
 *
 * @brief Host Interface handler
 *
 *****************************************************************************/
#define SII_DEBUG 0

/***** #include statements ***************************************************/

#include "si_system_api.h"
#include "si_drv_cra_api.h"
#include "si_drv_ipc_api.h"

/***** Register Module name **************************************************/

SII_MODULE_NAME_SET(si_drv_ipc);

/***** local macro definitions ***********************************************/

#define MESSAGE_SIZE                        (268)

#define SII_IPC_PACKET_PAYLOAD_ADDR_SIZE    (4)
#define BUFFER_TOTAL_SIZE                   (64)
#define BUFFER_PAYLOAD_SIZE                 (BUFFER_TOTAL_SIZE-SII_IPC_PACKET_PAYLOAD_ADDR_SIZE)

#define PACKET_LAST_SEGMENT_FLAG            0x80

#define INTERRUPT_FLAG_PACKET_RDY           (1<<0)
#define INTERRUPT_FLAG_ACK                  (1<<1)
#define INTERRUPT_FLAG_NACK                 (1<<2)

//#if(SII_APP_DEF_C51BUILD)
#if 0
  #define REG_INT_SET(flag)                 (SiiDrvCraWrite8(0x7060, flag))

  #define PACKET_RX_ADDR_BEGIN              0x7280
  #define PACKET_TX_ADDR_BEGIN              0x7220
#else
  #define REG_INT_SET(flag)                 (SiiDrvCraWrite8(pObj->config.devId, 0x7070, flag))

  #define PACKET_RX_ADDR_BEGIN              0x7220
  #define PACKET_TX_ADDR_BEGIN              0x7280
#endif

/***** local type definitions ************************************************/

typedef struct
{
    uint16_t                 size;
    uint8_t                  payLoad[MESSAGE_SIZE];
} Message_t;

typedef struct
{
    SiiDrvIpcType_t          type;
    SiiDrvIpcOpCode_t        opCode;
    SiiDrvIpcTagCode_t       tagCode;
    uint16_t                 parSize;
    uint16_t                 parPntr;
    uint8_t                  parData[MESSAGE_SIZE];
} Mes_t;
 
typedef struct
{
    SiiDrvIpcConfig_t        config;

    uint8_t                  rxSeqNum;
    uint8_t                  rxFrgNum;
    uint16_t                 rxChrCnt;
    uint16_t                 rxMsgSiz;
    Message_t                rxMessage;

    uint8_t                  txSeqNum;
    uint8_t                  txFrgNum;
    uint16_t                 txChrCnt1;
    uint16_t                 txChrCnt2;
    uint16_t                 txMsgSiz;
    uint8_t                  txRetry;
    Message_t                txMessage;
} Obj_t;

/***** local prototypes ******************************************************/

static void sMessageClear( Mes_t* pMes );
static void sMessageCopy( Mes_t* pMesDes, Mes_t* pMesSrc );
static void sTypeSet( Message_t* pMessage, SiiDrvIpcType_t type );
static SiiDrvIpcType_t sTypeGet( Message_t* pMessage );
static void sOpCodeSet( Message_t* pMessage, SiiDrvIpcOpCode_t opCode );
static SiiDrvIpcOpCode_t sOpCodeGet( Message_t* pMessage );
static void sTagCodeSet( Message_t* pMessage, SiiDrvIpcTagCode_t tagCode );
static SiiDrvIpcTagCode_t sTagCodeGet( Message_t* pMessage );
static uint16_t sParSizeGet( Message_t* pMessage );
static void sParDataSet( Message_t* pMessage, uint8_t* pData, uint16_t parSize );
static void sParDataGet( Message_t* pMessage, uint8_t* pData );
static void sCheckSumUpdate( Message_t* pMessage );
static bool_t sMessageValidIs( Message_t* pMessage );

/* Packet layer functions */
static void sRawMessageClear( Message_t* pMessage );
static uint8_t sCheckSumGet( uint8_t *pBuf, uint16_t len );
static bool_t sCheckSumFailed( uint8_t *pBuf, uint16_t len );
static void sSendMessageHandler( Obj_t* pObj );
static void sReceiveMessageHandler( Obj_t* pObj );
static void sPacketHandler( Obj_t* pObj, SiiDrvIpcIsrFlags_t flags );
uint16_t sPacketMessageSend( Obj_t* pObj, const Message_t *ipMessage );
static uint16_t sPacketMessageReceive( Obj_t* pObj, Message_t *opMessage );

/***** local data objects ****************************************************/

static SiiSysObjListInst_t sObjListInst = SII_INST_NULL;

/***** public functions ******************************************************/

SiiDrvIpcInst_t SiiDrvIpcCreate( const char *pNameStr, SiiInst_t parentInst, SiiDrvIpcConfig_t *pConfig )
{
    Obj_t* pObj = NULL;

    if( !sObjListInst )
    {
        SII_ASSERT(!sObjListInst);
        sObjListInst = SiiSysObjListCreate(SII_MODULE_NAME_GET(), sizeof(Obj_t));
        SII_ASSERT(sObjListInst);
    }

    /* Allocate memory for object */
    pObj = (Obj_t*)SiiSysObjInstanceCreate(sObjListInst, parentInst, pNameStr);
    SII_ASSERT(pObj);
    SII_LOG1A("Create", pObj, (""));

    pObj->config    = *pConfig;

    /*--------------------------------*/
    /* Initialize internal states     */
    /*--------------------------------*/
    pObj->rxSeqNum  = 0;
    pObj->rxFrgNum  = 0;
    pObj->rxChrCnt  = 0;
    pObj->rxMsgSiz  = 0;
    sRawMessageClear(&pObj->rxMessage);

    pObj->txSeqNum  = 0;
    pObj->txFrgNum  = 0;
    pObj->txChrCnt1 = 0;
    pObj->txChrCnt2 = 0;
    pObj->txMsgSiz  = 0;
    pObj->txRetry   = 0;
    sRawMessageClear(&pObj->txMessage);

    return SII_OBJ2INST(pObj);
}

void SiiDrvIpcDelete( SiiDrvIpcInst_t ipcInst  )
{
    Obj_t* pObj = (Obj_t*)SII_SYS_OBJ_CHECK(sObjListInst, ipcInst);

    SiiSysObjInstanceDelete(pObj);
    if( !SiiSysObjFirstGet(sObjListInst) )
    {
        /* Delete list of objects */
        SiiSysObjListDelete(sObjListInst);
        sObjListInst = SII_INST_NULL;
    }
}

void SiiDrvIpcHandler( SiiDrvIpcInst_t ipcInst, SiiDrvIpcIsrFlags_t flags )
{
    Obj_t* pObj = (Obj_t*)SII_SYS_OBJ_CHECK(sObjListInst, ipcInst);

    sPacketHandler(pObj, flags);
}

void SiiDrvIpcMessageSend( SiiDrvIpcInst_t ipcInst, SiiDrvIpcMesInst_t mesInst )
{
    Obj_t*    pObj = (Obj_t*)SII_SYS_OBJ_CHECK(sObjListInst, ipcInst);
    Mes_t*    pMes = (Mes_t*)mesInst;
    Message_t packMes = {0,{0}};

    sTypeSet(&packMes, pMes->type);
    sOpCodeSet(&packMes, pMes->opCode);
    sTagCodeSet(&packMes, pMes->tagCode);
    pMes->parSize = pMes->parPntr;
    sParDataSet(&packMes, pMes->parData, pMes->parSize);
    sCheckSumUpdate(&packMes);
    sPacketMessageSend(pObj, &packMes);
}

void SiiDrvIpcMessageReceive( SiiDrvIpcInst_t ipcInst, SiiDrvIpcMesInst_t mesInst )
{
    Obj_t*    pObj = (Obj_t*)SII_SYS_OBJ_CHECK(sObjListInst, ipcInst);
    Mes_t*    pMes = (Mes_t*)mesInst;
    Message_t packMes;

    sPacketMessageReceive(pObj, &packMes);
    pMes->type    = sTypeGet(&packMes);
    pMes->opCode  = sOpCodeGet(&packMes);
    pMes->tagCode = sTagCodeGet(&packMes);
    pMes->parSize = sParSizeGet(&packMes);
    pMes->parPntr = 0;
    sParDataGet(&packMes, pMes->parData);
}

bool_t SiiDrvIpcMessageSent( SiiDrvIpcInst_t ipcInst )
{
    Obj_t* pObj = (Obj_t*)SII_SYS_OBJ_CHECK(sObjListInst, ipcInst);

    return (pObj->txMessage.size) ? (false) : (true);
}

bool_t SiiDrvIpcMessageReceived( SiiDrvIpcInst_t ipcInst )
{
    Obj_t* pObj = (Obj_t*)SII_SYS_OBJ_CHECK(sObjListInst, ipcInst);

    return (pObj->rxMessage.size) ? (false) : (true);
}

void SiiDrvIpcTxReset( SiiDrvIpcInst_t ipcInst )
{
    Obj_t* pObj = (Obj_t*)SII_SYS_OBJ_CHECK(sObjListInst, ipcInst);

    // reset IPC itself
    pObj->txMessage.size = 0;
    pObj->txChrCnt1      = 0;
    pObj->txSeqNum++;           /* Increase sequence number for next message */
}

SiiDrvIpcMesInst_t SiiDrvIpcMessageCreate( void )
{
    /* Allocate memory for object */
    Mes_t* pMes = (Mes_t*)SiiSysMallocCreate(sizeof(Mes_t));
    SII_ASSERT(pMes);
    
    sMessageClear(pMes);
    return (SiiDrvIpcMesInst_t)pMes;
}

void SiiDrvIpcMessageDelete( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    SiiSysMallocDelete(pMes);
}

void SiiDrvIpcMessageClear( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    sMessageClear(pMes);
}

void SiiDrvIpcMessageCopy( SiiDrvIpcMesInst_t mesDesInst, SiiDrvIpcMesInst_t mesSrcInst )
{
    Mes_t* pMesDes = (Mes_t*)mesDesInst;
    Mes_t* pMesSrc = (Mes_t*)mesSrcInst;

    sMessageCopy(pMesDes, pMesSrc);
}

void SiiDrvIpcTypeSet( SiiDrvIpcMesInst_t mesInst, SiiDrvIpcType_t type )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    pMes->type = type;
}

SiiDrvIpcType_t SiiDrvIpcTypeGet( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    return pMes->type;
}

void SiiDrvIpcOpCodeSet( SiiDrvIpcMesInst_t mesInst, SiiDrvIpcOpCode_t opCode )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    pMes->opCode = opCode;
}

SiiDrvIpcOpCode_t SiiDrvIpcOpCodeGet( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    return pMes->opCode;
}

void SiiDrvIpcTagCodeSet( SiiDrvIpcMesInst_t mesInst, SiiDrvIpcTagCode_t tagCode )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    pMes->tagCode = tagCode;
}

SiiDrvIpcTagCode_t SiiDrvIpcTagCodeGet( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    return pMes->tagCode;
}

void SiiDrvIpcBoolPush( SiiDrvIpcMesInst_t mesInst, bool_t par )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    SII_ASSERT(MESSAGE_SIZE >= (pMes->parPntr+1));
    if ( MESSAGE_SIZE >= (pMes->parPntr+1) )
    {
        pMes->parData[pMes->parPntr++] = (uint8_t)par;
    }
}

bool_t SiiDrvIpcBoolPop( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t*  pMes = (Mes_t*)mesInst;
    uint8_t par  = 0;

    SII_ASSERT(pMes->parSize >= (pMes->parPntr+1));
    if ( pMes->parSize >= (pMes->parPntr+1) )
    {
        par = (pMes->parData[pMes->parPntr++] << 0);
    }
    return par;
}

void SiiDrvIpcBytePush( SiiDrvIpcMesInst_t mesInst, uint8_t par )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    SII_ASSERT(MESSAGE_SIZE >= (pMes->parPntr+1));
    if ( MESSAGE_SIZE >= (pMes->parPntr+1) )
    {
        pMes->parData[pMes->parPntr++] = (uint8_t)(par >> 0);
    }
}

uint8_t SiiDrvIpcBytePop( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t*  pMes = (Mes_t*)mesInst;
    uint8_t par  = 0;

    SII_ASSERT(pMes->parSize >= (pMes->parPntr+1));
    if ( pMes->parSize >= (pMes->parPntr+1) )
    {
        par = (pMes->parData[pMes->parPntr++] << 0);
    }
    return par;
}

void SiiDrvIpcWordPush( SiiDrvIpcMesInst_t mesInst, uint16_t par )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    SII_ASSERT(MESSAGE_SIZE >= (pMes->parPntr+2));
    if ( MESSAGE_SIZE >= (pMes->parPntr+2) )
    {
        pMes->parData[pMes->parPntr++] = (uint8_t)(par >> 0);
        pMes->parData[pMes->parPntr++] = (uint8_t)(par >> 8);
    }
}

uint16_t SiiDrvIpcWordPop( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t*   pMes = (Mes_t*)mesInst;
    uint16_t par  = 0;

    SII_ASSERT(pMes->parSize >= (pMes->parPntr+2));
    if ( pMes->parSize >= (pMes->parPntr+2) )
    {
        par |= (pMes->parData[pMes->parPntr++] << 0);
        par |= (pMes->parData[pMes->parPntr++] << 8);
    }
    return par;
}

void SiiDrvIpcLongPush( SiiDrvIpcMesInst_t mesInst, uint32_t par )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    SII_ASSERT(MESSAGE_SIZE >= (pMes->parPntr+4));
    if ( MESSAGE_SIZE >= (pMes->parPntr+4) )
    {
        pMes->parData[pMes->parPntr++] = (uint8_t)(par >>  0);
        pMes->parData[pMes->parPntr++] = (uint8_t)(par >>  8);
        pMes->parData[pMes->parPntr++] = (uint8_t)(par >> 16);
        pMes->parData[pMes->parPntr++] = (uint8_t)(par >> 24);
    }
}

uint32_t SiiDrvIpcLongPop( SiiDrvIpcMesInst_t mesInst )
{
    Mes_t*   pMes = (Mes_t*)mesInst;
    uint32_t par  = 0;

    SII_ASSERT(pMes->parSize >= (pMes->parPntr+4));
    if ( pMes->parSize >= (pMes->parPntr+4) )
    {
        par |= (((uint32_t)pMes->parData[pMes->parPntr++]) <<  0);
        par |= (((uint32_t)pMes->parData[pMes->parPntr++]) <<  8);
        par |= (((uint32_t)pMes->parData[pMes->parPntr++]) << 16);
        par |= (((uint32_t)pMes->parData[pMes->parPntr++]) << 24);
    }
    return par;
}

void SiiDrvIpcArrayPush( SiiDrvIpcMesInst_t mesInst, const uint8_t* pPar, uint16_t size )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    SII_ASSERT(MESSAGE_SIZE >= (pMes->parPntr+size));
    if ( MESSAGE_SIZE >= (pMes->parPntr+size) )
    {
        SII_MEMCPY(&pMes->parData[pMes->parPntr], pPar, size);
        pMes->parPntr += size;
    }
}

void SiiDrvIpcArrayPop( SiiDrvIpcMesInst_t mesInst, uint8_t* pPar, uint16_t size )
{
    Mes_t* pMes = (Mes_t*)mesInst;

    SII_ASSERT(pMes->parSize >= (pMes->parPntr+size));
    if ( pMes->parSize >= (pMes->parPntr+size) )
    {
        SII_MEMCPY(pPar, &pMes->parData[pMes->parPntr], size);
        pMes->parPntr += size;
    }
}

/***** local functions *******************************************************/

static void sMessageClear( Mes_t* pMes )
{
    pMes->type     = SII_DRV_IPC_TYPE__NONE;
    pMes->opCode   = 0;
    pMes->tagCode  = 0;
    pMes->parSize  = 0;
    pMes->parPntr  = 0;
    SII_MEMSET(pMes->parData, 0, MESSAGE_SIZE);
}

static void sMessageCopy( Mes_t* pMesDes, Mes_t* pMesSrc )
{
    pMesDes->type     = pMesSrc->type;
    pMesDes->opCode   = pMesSrc->opCode;
    pMesDes->tagCode  = pMesSrc->tagCode;
    pMesDes->parSize  = pMesSrc->parSize;
    pMesDes->parPntr  = pMesSrc->parPntr;
    SII_MEMCPY(pMesDes->parData, pMesSrc->parData, MESSAGE_SIZE);
}

static void sTypeSet( Message_t* pMessage, SiiDrvIpcType_t type )
{
    uint8_t val = 0;

    switch( type )
    {
        case SII_DRV_IPC_TYPE__NONE     : val = 0; break;
        case SII_DRV_IPC_TYPE__REQUEST  : val = 1; break;
        case SII_DRV_IPC_TYPE__RESPONSE : val = 2; break;
        case SII_DRV_IPC_TYPE__NOTIFY   : val = 3; break;
        default : break;
    }
    pMessage->payLoad[1] = (pMessage->payLoad[1]&0x0F) | (val<<4);
}

static SiiDrvIpcType_t sTypeGet( Message_t* pMessage )
{
    SiiDrvIpcType_t type = SII_DRV_IPC_TYPE__NONE;

    switch( pMessage->payLoad[1] >> 4 )
    {
        case 0x1 : type = SII_DRV_IPC_TYPE__REQUEST;  break;
        case 0x2 : type = SII_DRV_IPC_TYPE__RESPONSE; break;
        case 0x3 : type = SII_DRV_IPC_TYPE__NOTIFY;   break;
        default : break;
    }
    return type;
}

static void sOpCodeSet( Message_t* pMessage, SiiDrvIpcOpCode_t opCode )
{
    pMessage->payLoad[0] = (uint8_t)(opCode & 0x00FF);
    pMessage->payLoad[1] = (pMessage->payLoad[1]&0xF0) | (uint8_t)((opCode>>8) & 0x000F);
}

static SiiDrvIpcOpCode_t sOpCodeGet( Message_t* pMessage )
{
    return (SiiDrvIpcOpCode_t)((((uint16_t)(pMessage->payLoad[1]&0x0F))<<8) | pMessage->payLoad[0]);
}

static void sTagCodeSet( Message_t* pMessage, SiiDrvIpcTagCode_t tagCode )
{
    pMessage->payLoad[4] = (uint8_t)(tagCode);
}

static SiiDrvIpcTagCode_t sTagCodeGet( Message_t* pMessage )
{
    return (SiiDrvIpcTagCode_t)pMessage->payLoad[4];
}

static uint16_t sParSizeGet( Message_t* pMessage )
{
    return (uint16_t)((((uint16_t)(pMessage->payLoad[3]&0x3))<<8) | pMessage->payLoad[2]);
}

static void sParDataSet( Message_t* pMessage, uint8_t* pData, uint16_t parSize )
{
    pMessage->payLoad[2] = (uint8_t)(parSize & 0x00FF);
    pMessage->payLoad[3] = (uint8_t)((parSize>>8) & 0x0003);
    SII_MEMCPY(&pMessage->payLoad[6], pData, parSize);
}

static void sParDataGet( Message_t* pMessage, uint8_t* pData )
{
    SII_MEMCPY(pData, &pMessage->payLoad[6], pMessage->size-6);
}

static void sCheckSumUpdate( Message_t* pMessage )
{
    uint16_t    size  = 6 + sParSizeGet(pMessage);
    uint8_t*    pData = pMessage->payLoad;
    uint8_t     cs    = 0;

    pMessage->size       = size;
    pMessage->payLoad[5] = 0;
    while( size-- )
    {
        cs += *pData;
        pData++;
    }
    pMessage->payLoad[5] = (uint8_t)(0-((int16_t)cs));
}

static bool_t sMessageValidIs( Message_t* pMessage )
{
    uint16_t    size  = pMessage->size;
    uint8_t*    pData = pMessage->payLoad;
    uint8_t     cs    = 0;

    while( size-- )
    {
        cs += *pData;
        pData++;
    }
    return (cs) ? (false) : (true);
}

/*---------------------------------------------------------------------------*/
/* Packet layer functions                                                    */
/*---------------------------------------------------------------------------*/

static void sRawMessageClear( Message_t* pMessage )
{
    pMessage->size = 0;
    SII_MEMSET(pMessage->payLoad, 0, MESSAGE_SIZE);
}

static uint8_t sCheckSumGet( uint8_t *pBuf, uint16_t len )
{
    uint8_t checksum = 0;

    while( len-- )
    {
        checksum += *pBuf;
        pBuf++;
    }
    return (uint8_t)((int16_t)0-checksum);
}

static bool_t sCheckSumFailed( uint8_t *pBuf, uint16_t len )
{
    uint8_t checksum = 0;

    while( len-- )
    {
        checksum += *pBuf;
        pBuf++;
    }
    return (checksum) ? (true) : (false);
}

static void sSendMessageHandler( Obj_t* pObj )
{
    uint8_t  buffer[BUFFER_TOTAL_SIZE];
    uint8_t  *pbuf    = buffer;
    uint16_t check    = pObj->txMessage.size - pObj->txChrCnt1;
    uint8_t  chrCnt   = 0;

    pObj->txChrCnt2 = pObj->txChrCnt1;

    if( BUFFER_PAYLOAD_SIZE < check )
    {
        chrCnt = BUFFER_PAYLOAD_SIZE;
    }
    else
    {
        chrCnt = (uint8_t)check;
        /* Flag fragment number as last packet of message */
        pObj->txFrgNum |= 0x80;
    }

    *(pbuf++) = pObj->txSeqNum;
    *(pbuf++) = pObj->txFrgNum;
    *(pbuf++) = chrCnt;
    *(pbuf++) = 0;
    SII_MEMCPY(pbuf, &pObj->txMessage.payLoad[pObj->txChrCnt2], chrCnt);
    pObj->txChrCnt2 += chrCnt;
    chrCnt += 4;

    /* Write Checksum */
    buffer[3] = sCheckSumGet(buffer, chrCnt);

    /* Write packet to shared memory */
    SiiDrvCraBlockWrite8(pObj->config.devId, PACKET_TX_ADDR_BEGIN, buffer, chrCnt);

    REG_INT_SET(INTERRUPT_FLAG_PACKET_RDY); // Set ready flag for receiver

    SII_LOG2A("sSendMessageHandler", 0 , ("seq=%02X, frg=%02X, len=%02X, cs=%02X\n", (uint16_t)pObj->txSeqNum, (uint16_t)pObj->txFrgNum, (uint16_t)chrCnt, (uint16_t)buffer[3]));	
}

static void sReceiveMessageHandler( Obj_t* pObj )
{
    uint8_t  buffer[BUFFER_TOTAL_SIZE];
    bool_t   bNoError  = true;
    uint8_t  seqNum    = 0;
    uint8_t  frgNum    = 0;
    uint8_t  datlen    = 0;
    bool_t   bLastFrag = false;

    SiiDrvCraBlockRead8(pObj->config.devId, PACKET_RX_ADDR_BEGIN, buffer, 4);
    seqNum    = buffer[0];
    frgNum    = buffer[1] & 0x7F;
    datlen    = buffer[2];
    bLastFrag = (buffer[1] & 0x80) ? (true) : (false);

    SII_LOG2A("sReceiveMessageHandler", 0 , ("seq=%02X, frg=%02X, len=%02X, cs=%02X\n", (uint16_t)buffer[0], (uint16_t)buffer[1], (uint16_t)buffer[2], (uint16_t)buffer[3]));	

    /* Check data length byte */
    if( bNoError )
    {
        if( BUFFER_PAYLOAD_SIZE < datlen )
        {
            SII_LOG2B(("Too many bytes in package!\n"));	
            bNoError = false;
        }
    }

    /* Read packet and calculate checksum */
    if( bNoError )
    {
        SiiDrvCraBlockRead8(pObj->config.devId, PACKET_RX_ADDR_BEGIN+4, &buffer[4], datlen);
    }

    /* Check checksum value */
    if( bNoError )
    {
        if( sCheckSumFailed(buffer, datlen+4) )
        {
            REG_INT_SET(INTERRUPT_FLAG_NACK); /* checksum error */
            SII_LOG2B(("Wrong checksum!\n"));
            bNoError = false;
        }
    }

    /* Check fragment number */
    if( bNoError )
    {
        if( 0 == pObj->rxFrgNum )
        {
            /* Reset message sequence number */
            pObj->rxSeqNum = seqNum;
        }
        else if( frgNum != pObj->rxFrgNum )
        {
            REG_INT_SET(INTERRUPT_FLAG_NACK); /* Message is too big */
            SII_LOG2B(("wrong fragment number!\n"));	
            bNoError = false;
        }
    }

    /* Check message sequence number */
    if( bNoError )
    {
        if( pObj->rxSeqNum != seqNum )
        {
            REG_INT_SET(INTERRUPT_FLAG_NACK); /* Wrong message sequence number received */
            SII_LOG2B(("Packet with wrong sequence number!\n"));
            bNoError = false;
        }
    }

    /* Process Packet */
    if( bNoError )
    {
        SII_MEMCPY(&pObj->rxMessage.payLoad[pObj->rxChrCnt], &buffer[4], datlen);
        pObj->rxChrCnt += datlen;
        pObj->rxFrgNum++;
        pObj->rxMessage.size = 0;

        if( bLastFrag )
        {
            pObj->rxMessage.size = pObj->rxChrCnt;
            pObj->rxChrCnt = 0;
            pObj->rxFrgNum = 0;
        }

        /* Indicate to sender that packet has been accepted */
        REG_INT_SET(INTERRUPT_FLAG_ACK);

        if( bLastFrag )
        {
            /* Call back parent before acknowledging last packet */
            if( sMessageValidIs(&pObj->rxMessage) )
            {
                SiiDrvIpcCallBack(SII_OBJ2INST(pObj), SII_DRV_IPC_EVENT__MESSAGE_RECEIVED);
            }
        }
    }
    else
    {
        /* Reset packet state state machine */
        pObj->rxChrCnt = 0;
        pObj->rxFrgNum = 0;
        REG_INT_SET(INTERRUPT_FLAG_NACK);
    }
}

static void sPacketHandler( Obj_t* pObj, SiiDrvIpcIsrFlags_t flags )
{
    /* Collect interrupt Flags */
    SII_LOG2A("sPacketHandler", 0, ("Process interrupt flags : 0x%02X\n", (uint16_t)flags));	
    if( flags & SII_DRV_IPC_ISR_FLAG__NACK )
    {
        if( pObj->txMessage.size )
        {
            pObj->txRetry++;
            if( 5 <= pObj->txRetry )
            {
                /* T.b.d. Some error handling may be needed here */
                pObj->txMessage.size = 0;
            }
            else
            {
                sSendMessageHandler(pObj);  /* send remaining packets */
            }
        }
    }

    if( flags & SII_DRV_IPC_ISR_FLAG__ACK )
    {
        if( pObj->txMessage.size )
        {
            pObj->txChrCnt1 = pObj->txChrCnt2;

            if( pObj->txMessage.size > pObj->txChrCnt1 )
            {
                pObj->txFrgNum++;           /* Increase fragment number */
                pObj->txRetry = 0;          /* Reset retry counter */
                sSendMessageHandler(pObj);  /* send remaining packets */
            }
            else
            {
                pObj->txMessage.size = 0;   /* Message has been sent */
                pObj->txChrCnt1 = 0;        /* Message has been sent */
                pObj->txSeqNum++;           /* Increase sequence number for next message */
                SiiDrvIpcCallBack(SII_OBJ2INST(pObj), SII_DRV_IPC_EVENT__MESSAGE_SENT);
            }
        }
    }

    if( flags & SII_DRV_IPC_ISR_FLAG__PACKET_RDY )
    {
        sReceiveMessageHandler(pObj);
    }
}

uint16_t sPacketMessageSend( Obj_t* pObj, const Message_t *ipMessage )
{
    SII_LOG2A("sPacketMessageSend", 0 , ("size=%d,", ipMessage->size));	
    {
        uint16_t i = 0;

        for( i=0; i<ipMessage->size; i++ )
        {
            SII_LOG2B((" %02X ", (uint16_t)ipMessage->payLoad[i]));	
        }    
    }
    
    SII_ASSERT(0 == pObj->txMessage.size);

    SII_ASSERT(MESSAGE_SIZE >= ipMessage->size);

    pObj->txMessage = *ipMessage;
    pObj->txFrgNum = 0;          /* Reset Fragment numer */
    sSendMessageHandler(pObj);   /* Start sending first packet */
    return ipMessage->size;
}

static uint16_t sPacketMessageReceive( Obj_t* pObj, Message_t *opMessage )
{
    uint16_t size = pObj->rxMessage.size;

    SII_LOG2A("sPacketMessageReceive", 0 , ("size=%d,", size));	
    if( size )
    {
        *opMessage = pObj->rxMessage;

        {
            uint16_t i = 0;
    
            for( i=0; i<size; i++ )
            {
                SII_LOG2B((" %02X ", (uint16_t)pObj->rxMessage.payLoad[i]));	
            }    
        }
    }
    return size;
}

/***** end of file ***********************************************************/
