/******************************************************************************/
/*                       Copyright 2001 Broadcom Corporation                  */
/*                                                                            */
/* This material is the confidential trade secret and proprietary information */
/* of Broadcom Corp.  It may not be reproduced, used, sold or transferred to  */
/* any third party without the prior written consent of Broadcom Corp.        */
/* All rights reserved.                                                       */
/******************************************************************************/
/*   FileName        : bcm_hmiQA.c                                            */
/*   Purpose         : Implement the questions-answers protocol of the Host   */
/*                     Messaging Interface                                    */
/*   Limitations     : None                                                   */
/*   Author          : Emmanuel Hanssens                                      */
/*   Creation Date   : 17-Sep-2001                                            */
/*   History         : Compiled by CVS                                        */
/******************************************************************************/
/*   $Id: bcm_hmiQA.c,v 1.17 2004/09/29 08:10:13 hanssens Exp $  */
/******************************************************************************/

#include <string.h>
#include "bcm_hmiQA.h"
#include "bcm_bigEndianByteSwap.h"
#include "bcm_errorHandler.h"
#include "bcm_message.h"
#include "bcm_timer.h"

/* {{{ Global variables */

const char * hmiErr[ ] =
{
  "no error",
  "unkown application Id (dispatch result)",               /* EHMIWGID */
  "write message failed",                                  /* EHMIWRIT */
  "read message failed",                                   /* EHMIREAD */
  "put message time out",                                  /* EHMIPUTO */
  "get message time out",                                  /* EHMIGETO */
  "%s service failed with result %d",                      /* EHMISEFA */
  "firmware could not go operational",                     /* EHMIGOFW */
  "no more Hmi resources available",                       /* EHMIOPEN */
  "msg reply size (%d) is different from expected (%d)",   /* EHMISIZE */
  "bcm6410Id out of range: %d",                            /* EHMIIDOR */
};

struct HmiDev
{
  MsgDev  *msgDev;
  int      devId;
  int      counterRef;
};

static HmiDev hmiDevices[NB_HMIDEV];

/* }}} */
/* {{{ hmiDriverInit */

void hmiDriverInit(void) 
{
  msgDriverInit();  
}

/* }}} */
/* {{{ hmiOpen */

HmiDev *hmiOpen(uint16 bcm6410Id) 
{
  HmiDev *hmi = 0;

  if (bcm6410Id >= sizeof(hmiDevices)/sizeof(HmiDev)) 
    FATAL_ERROR(hmiErr[EHMIIDOR], bcm6410Id);

  hmi = &hmiDevices[bcm6410Id];
  memset(hmi, 0, sizeof(HmiDev));
  hmi->devId = bcm6410Id;
  return hmi;
}

/* }}} */
/* {{{ hmiInit */
#ifdef UDP_PHY_USE_DYNAMIC_IP_ALLOCATION
void hmiInit(HmiDev *hmi, MsgDev *msg) 
{
  hmi->msgDev = msg;
  hmi->counterRef = 0;
}
#else
void hmiInit(HmiDev *hmi) 
{
  hmi->msgDev = msgOpen(hmi->devId);
  hmi->counterRef = 0;
  msgInit(hmi->msgDev);
}
#endif

/* }}} */
/* {{{ hmiClose */

void hmiClose(HmiDev *hmi) 
{
  /* closes the underlaying device driver */
  msgClose(hmi->msgDev);
}

/* }}} */
/* {{{ hmiGetReqMsgMaxSize */
int hmiGetReqMsgMaxSize(HmiDev *hmi) 
{
  return msgGetReqMsgMaxSize(hmi->msgDev) - sizeof(HmiMsgHdr);
}

/* }}} */
/* {{{ hmiQuestion */

int hmiQuestion(HmiDev    *hmi,
                uint16     applicationId,
                uint8     *payload,
                uint16     payloadSize,
                uint32     sourceRef,
                uint32    *requestRef,
                MsgLayout *layout)
{
  HmiMsgHdr      hmiHdr;
  int            res;
  int            messageSent = 0;
  int            stop = 0;
  
  /************************** */
  /* build request msg header */
  /************************** */
  
  *requestRef =  hmi->counterRef++;
  
  /* Fill all header fields */
  hmiHdr.sourceRef       = sourceRef;
  hmiHdr.requestRef      = *requestRef;
  hmiHdr.applicationId   = applicationId;
  hmiHdr.length          = payloadSize;
  hmiHdr.replyAddress[0] = 0;   /* not used in this implementation */
  hmiHdr.replyAddress[1] = 0;   /* not used in this implementation */
  hmiHdr.replyAddress[2] = 0;   /* not used in this implementation */
  hmiHdr.replyAddress[3] = 0;   /* not used in this implementation */
  hmiHdr.replyAddress[4] = 0;   /* not used in this implementation */
  hmiHdr.result          = 0;   /* reserved in request */
  hmiHdr.firmwareId      = 0;   /* reserved in request */
  hmiHdr.commandId       = layout->commandId;
  hmiHdr.reserved        = 0;
  
  /************************************ */
  /* swap bytes for big endian machines */
  /************************************ */

  /* message header */
  bigEndianByteSwap((uint8 *) &hmiHdr, HmiMsgHdrLayout);

  /* message data */
  bigEndianByteSwap(payload, layout->requestMsgLayout);
  
  /**************************** */
  /* send message with time out */
  /**************************** */

  /* install timer */
  startTimer();
  
  while (! messageSent && ! stop) 
  {
    /* try to send the message */
    res = msgPut(hmi->msgDev,
                 (uint8 *) &hmiHdr, sizeof(HmiMsgHdr),
                 payload,           payloadSize);

    if (res == BCM_SUCCESS)
      messageSent = 1;

    else if (res == RQUEUEFULL)
    {
      /* check for time out */
      stop = compareTimer(requestTimeOut);
      goSleep(sleepTime);       /* sleep to avoid constant polling */
    }

    else                        /* other error => stop */
      stop = 1;
  }
  
  /* disable timer */
  stopTimer();
  
  /* swap back the message data, so it can still be used by the application
     when the hmi driver returns */
  bigEndianByteSwap(payload, layout->requestMsgLayout);

  /* call errorHandler when an error was detected in the loop */
  if  (res == RQUEUEFULL)
    return SOFT_ERROR(BCM_FAILURE, hmiErr[EHMIPUTO]);

  else if (res != BCM_SUCCESS)
    return SOFT_ERROR(BCM_FAILURE, hmiErr[EHMIWRIT]);
  
  return BCM_SUCCESS;
}

/* }}} */
/* {{{ hmiAnswerHdr: get the answer header */

int hmiAnswerHdr(HmiDev     *hmi,
                 uint32      requestRef,
                 uint16     *payloadSize,
                 uint32     *sourceRef)
{
  HmiMsgHdr hmiHdr;
  int       res;
  int       messageReceived = 0;
  int       stop = 0;
  
  /******************************** */
  /* get reply header with time out */
  /******************************** */

  /* install timer */
  startTimer();
  
  while (!messageReceived && !stop) 
  {
    res = msgGetHeader(hmi->msgDev,
                       (uint8 *) &hmiHdr, sizeof(HmiMsgHdr));
    if (res == BCM_SUCCESS)
      messageReceived = 1;

    else if (res == RQUEUEEMPTY)
    {
      /* check for time out */
      stop = compareTimer(replyTimeOut);
      goSleep(sleepTime);       /* sleep to avoid constant polling */
    }

    else                        /* other error => stop */
      stop = 1;
  }

  /* disable timer */
  stopTimer();

  /* call errorHandler when an error was detected in the loop */
  if (res == RQUEUEEMPTY)
    return SOFT_ERROR(BCM_FAILURE, hmiErr[EHMIGETO]);

  else if (res != BCM_SUCCESS) 
    return SOFT_ERROR(BCM_FAILURE, hmiErr[EHMIREAD]);
    
  /************************************ */
  /* swap bytes for big endian machines */
  /************************************ */

  bigEndianByteSwap((uint8 *) &hmiHdr, HmiMsgHdrLayout);

  /******** */
  /* return */
  /******** */
  
  /* fill in data to be returned */
  *payloadSize = hmiHdr.length;
  *sourceRef   = hmiHdr.sourceRef;

  /* we could check here the requestRef correctness:
     if (requestRef == hmiHdr.requestRef)
        "trigger recovery action"

     we could also check whether the length of the received msg
     corresponds to the expected size
  */
  BCM_UNUSED_PARAMETER(requestRef);
  
  return hmiHdr.result;
}

/* }}} */
/* {{{ hmiAnswerPayload: get the answer payload */

int hmiAnswerPayload(HmiDev     *hmi,
                     uint8      *payload,
                     uint16      payloadSize,
                     MsgLayout  *layout)
{
  int  res;
  
  /* get reply payload */
  res = msgGetPayload(hmi->msgDev,
                      sizeof(HmiMsgHdr),
                      payload,
                      payloadSize);
    
  /* call errorHandler when an error was detected in the loop */
  if (res != BCM_SUCCESS)
    return SOFT_ERROR(BCM_FAILURE, hmiErr[EHMIREAD]);
    
  /* swap bytes for big endian machines */
  bigEndianByteSwap(payload, layout->replyMsgLayout);
  
  return BCM_SUCCESS;
}

/* }}} */
/* {{{ hmiQA: question-answer */

int hmiQA      (HmiDev    *hmi,
                uint16     applicationId,
                uint8     *reqMsg,
                uint16     reqMsgSize,
                uint8     *repMsgBuffer,
                uint16     repMsgBufferSize,
                MsgLayout *layout)
{
  uint32  requestRef, sourceRef;
  int     res = BCM_FAILURE;
  uint16  triesLeft = NB_HMIRETRIES+1;
  uint16  payloadSize;
  
  while (triesLeft-- && res == BCM_FAILURE) 
  {
    /* send question */
    res = hmiQuestion(hmi, applicationId, reqMsg, reqMsgSize,
                      0, &requestRef, layout);
    
    /* get answer header */
    if (res != BCM_FAILURE)
      res = hmiAnswerHdr(hmi, requestRef, &payloadSize, &sourceRef);
    /* get answer payload if res is a valid HMI return value */
    if (res != BCM_FAILURE)
    {
      /* in case of success, check if returned size is the one expected */
      if (res == HMI_RESULT_SUCCESS && payloadSize != repMsgBufferSize)
        SOFT_ERROR(EHMISIZE, hmiErr[EHMISIZE], payloadSize, repMsgBufferSize);

      if (hmiAnswerPayload(hmi, repMsgBuffer,
                           min(payloadSize, repMsgBufferSize), layout)
          != BCM_SUCCESS)
        /* couldn't get the payload => return failure iso HMI return value */
        return BCM_FAILURE;
    }
  }
  return res;
}

/* }}} */
/* {{{ hmiQvarA: question-variable size answer */

int hmiQvarA   (HmiDev    *hmi,
                uint16     applicationId,
                uint8     *reqMsg,
                uint16     reqMsgSize,
                uint8     *repMsgBuffer,
                uint16     repMsgBufferSize,
                uint16    *repMsgSize,
                MsgLayout *layout)
{
  uint32  requestRef, sourceRef;
  uint16  triesLeft = NB_HMIRETRIES+1;
  int     res = BCM_FAILURE;
  
  while (triesLeft-- && res == BCM_FAILURE) 
  {
    /* send question */
    res = hmiQuestion(hmi, applicationId, reqMsg, reqMsgSize,
                      0, &requestRef, layout);
    
    /* get answer header */
    if (res != BCM_FAILURE)
      res = hmiAnswerHdr(hmi, requestRef, repMsgSize, &sourceRef);
    
    /* get answer payload if res is a valid HMI return value */
    if (res != BCM_FAILURE)
    {
      if (hmiAnswerPayload(hmi, repMsgBuffer,
                           min(*repMsgSize, repMsgBufferSize), layout)
          != BCM_SUCCESS)
        /* couldn't get the payload => return failure iso HMI return value */
        return BCM_FAILURE;
    }
  }
  return res;
}

/* }}} */
/* {{{ hmiHasAnswer */

#ifdef UDP_PHY
/* this primitive is only necessary when using udp message layer */
uint8 hmiHasAnswer(HmiDev *hmi) 
{
  return msgHasData(hmi->msgDev);
}
#endif

/* }}} */
