/******************************************************************************/
/*                       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_message.c                                          */
/*   Purpose         : Part of the Hpi layer visible                          */
/*   Limitations     : None                                                   */
/*   Author          : Emmanuel Hanssens                                      */
/*   Creation Date   : 14-Sep-2001                                            */
/*   History         : Compiled by CVS                                        */
/******************************************************************************/
/*   $Id: bcm_message.c,v 1.18 2004/10/20 10:16:56 hanssens Exp $  */
/******************************************************************************/

#include <string.h>
#include "bcm_message.h"
#include "bcm_transfer.h"
#include "bcm_SharedControlQueue.h"
#include "bcm_bigEndianByteSwap.h"
#ifndef NO_FULL_ERROR_HANDLING
# include "bcm_errorHandler.h"
#endif

/* {{{ Global variables */

struct MsgDev
{
  SharedQueueGeometry requestQueueGeometry;
  SharedQueueGeometry replyQueueGeometry;
  int                 devId;
  SharedQueueControl  replyCtrl;
  SharedQueueControl  requestCtrl;
};

MsgDev  msgDevices[NB_BCM_DEV];

#ifndef NO_FULL_ERROR_HANDLING
const char * hpiErr[ ] =
{
  "no error",
  "message size larger than queue geometry",               /* EHPISIZE */
};
#endif

/* }}} */
/* {{{ private functions declarations */

static void   hpiGetGeometry(MsgDev *dev);
static uint8  hpiReadReplyTail(MsgDev *dev);
static uint8  hpiReadReplyHead(MsgDev *dev);
static uint8  hpiReadRequestTail(MsgDev *dev);
static uint8  hpiReadRequestHead(MsgDev *dev);
static void   hpiWriteReplyHead(MsgDev *dev, uint8 head);
static void   hpiWriteRequestTail(MsgDev *dev, uint8 tail);

/* }}} */
/* {{{ msgDriverInit: reset msg structures for all devices */

void msgDriverInit(void)
{
  uint16 i;

  transferDriverInit();

  for (i=0; i<NB_BCM_DEV; i++)
    memset(&msgDevices[i],0, sizeof(MsgDev));
}

/* }}} */
/* {{{ msgOpen: open the msg driver for specified device */

MsgDev *msgOpen(uint16 bcm6410Id)
{
  MsgDev *msgDev;

#ifndef NO_FULL_ERROR_HANDLING
  /* check validity for the id */
  if (bcm6410Id >= NB_BCM_DEV)
    FATAL_ERROR("Wrong BCM6410 id");
#endif

  msgDev = &msgDevices[bcm6410Id];
  msgDev->devId = bcm6410Id;
  
  return msgDev;
}

/* }}} */
/* {{{ msgInit: initializes the msg structures for specified device */

void msgInit(MsgDev *dev)
{
  hpiGetGeometry(dev);

  /* retrieve current values of requestTail and replyHead */
  dev->requestCtrl.tail = hpiReadRequestTail(dev);
  dev->replyCtrl.head   = hpiReadReplyHead(dev);
}

/* }}} */
/* {{{ msgClose: closes the msg driver for specified device */

void msgClose(MsgDev *dev)
{
  /* reset the msg structure */
  memset(&msgDevices[dev->devId],0, sizeof(MsgDev));
}

/* }}} */
/* {{{ msgPut: write the message at correct queue position */

int msgPut(MsgDev *dev,
           uint8 *header,
           uint16 headerSize,
           uint8 *payload,
           uint16 payloadSize)
{
  SharedQueueGeometry *geometry;
  SharedQueueControl  *qCtrl;
  uint16               msgOffset;
  
  geometry = &dev->requestQueueGeometry;
  qCtrl    = &dev->requestCtrl;
  
#ifndef NO_FULL_ERROR_HANDLING
  /* check size within geometry */
  if ((headerSize + payloadSize) > geometry->element_size)
    return SOFT_ERROR(EHPISIZE, hpiErr[EHPISIZE]);
#endif
  
  /* retrieve the current head information for request fifo */
  qCtrl->head = hpiReadRequestHead(dev);
  
  /* return without error if queue full (calling fn will manage time out) */
  if (QUEUE_FULL(qCtrl, geometry->queue_size))
    return RQUEUEFULL;

  msgOffset = geometry->queue_offset + GEOMETRY_SIZE
    + geometry->element_size * qCtrl->tail;
  
  /* send the header */
  transferReq(DEV_ADDRESS(dev->devId), BCM_WRITE, msgOffset,
              header, headerSize);

  if (payloadSize != 0)
  {
    /* send the payload */
    transferReq(DEV_ADDRESS(dev->devId), BCM_WRITE, 
                msgOffset + headerSize,
                payload, payloadSize);
  }
  
  /* now increment queue pointer and write it back into the BCM6410 */
  qCtrl->tail++;  
  if (qCtrl->tail >= geometry->queue_size)
    qCtrl->tail = 0;
  hpiWriteRequestTail(dev, qCtrl->tail);

  return BCM_SUCCESS;
}

/* }}} */
/* {{{ msgGetHeader: read the header at correct queue position */

int msgGetHeader(MsgDev *dev,
                 uint8  *headerBuffer,
                 uint16  headerSize)
{
  SharedQueueGeometry *geometry;
  SharedQueueControl  *qCtrl;
  uint16               msgOffset;
  
  geometry = &dev->replyQueueGeometry;
  qCtrl    = &dev->replyCtrl;

#ifndef NO_FULL_ERROR_HANDLING
  /* check size within geometry */
  if (headerSize > geometry->element_size)
    return SOFT_ERROR(EHPISIZE, hpiErr[EHPISIZE]);
#endif
  
  /* retrieve the current tail information for request fifo */
  qCtrl->tail = hpiReadReplyTail(dev);
  
  /* return without error if queue empty (calling fn will manage time out) */
  if (QUEUE_EMPTY(qCtrl))
    return RQUEUEEMPTY;

  msgOffset = geometry->queue_offset + GEOMETRY_SIZE
    + geometry->element_size * qCtrl->head;

  /* get the header */
  transferReq(DEV_ADDRESS(dev->devId), BCM_READ, 
              msgOffset,
              headerBuffer, headerSize);

  return BCM_SUCCESS;
}

/* }}} */
/* {{{ msgGetPayload: read the payload at correct queue position */

int msgGetPayload(MsgDev *dev,
                  uint16  headerSize,
                  uint8  *payloadBuffer,
                  uint16  payloadSize)
{
  SharedQueueGeometry *geometry;
  SharedQueueControl  *qCtrl;
  uint16               msgOffset;
  
  geometry = &dev->replyQueueGeometry;
  qCtrl    = &dev->replyCtrl;

#ifndef NO_FULL_ERROR_HANDLING
  /* check size within geometry */
  if (headerSize + payloadSize > geometry->element_size)
    return SOFT_ERROR(EHPISIZE, hpiErr[EHPISIZE]);
#endif
  
  msgOffset = geometry->queue_offset + GEOMETRY_SIZE
    + geometry->element_size * qCtrl->head;

  if (payloadSize != 0)
  {
    /* get the payload */
    transferReq(DEV_ADDRESS(dev->devId), BCM_READ,
                msgOffset+headerSize,
                payloadBuffer, payloadSize);
  }
  
  /* now increment queue pointer and write it back into the BCM6410 */
  qCtrl->head++;  
  if (qCtrl->head >= geometry->queue_size)
    qCtrl->head = 0;
  
  hpiWriteReplyHead(dev, qCtrl->head);

  return BCM_SUCCESS;
}

/* }}} */
/* {{{ msgGet: read the payload at correct queue position */

int msgGet(MsgDev *dev,
           uint8  *headerBuffer,
           uint16  headerSize,
           uint8  *payloadBuffer,
           uint16  payloadSize)
{
  int res;

  res = msgGetHeader(dev, headerBuffer, headerSize);
  if (res != BCM_SUCCESS) return res;
  
  return msgGetPayload(dev, headerSize, payloadBuffer, payloadSize);
}

/* }}} */
/* {{{ msgGetReqMsgMaxSize: return the max (request) message size */

int msgGetReqMsgMaxSize(MsgDev *dev)
{
  return  dev->requestQueueGeometry.element_size;
}

/* }}} */
/* {{{ msgGetRepMsgMaxSize: return the max (reply) message size */

int msgGetRepMsgMaxSize(MsgDev *dev)
{
  return  dev->replyQueueGeometry.element_size;
}

/* }}} */
/* {{{ hpiGetGeometry: read request and reply queues geometries */

static void hpiGetGeometry(MsgDev *dev)
{
  /* read request queue config: at addr 0 in visible page */
  transferReq(DEV_ADDRESS(dev->devId), BCM_READ, 0,
              (uint8 *) &dev->requestQueueGeometry,
              sizeof(SharedQueueGeometry));

  /* swap bytes for big endian machine */
  bigEndianByteSwap((uint8 *) &dev->requestQueueGeometry,
                    SharedQueueGeometryLayout);

  /* read reply queue config: at addr 8 in visible page  */
  transferReq(DEV_ADDRESS(dev->devId), BCM_READ, 8,
              (uint8 *) &dev->replyQueueGeometry,
              sizeof(SharedQueueGeometry));

  /* swap bytes for big endian machine */
  bigEndianByteSwap((uint8 *) &dev->replyQueueGeometry,
                    SharedQueueGeometryLayout);

  /* some sanity checks to avoid divide by 0 later and bad memory access */
  /* (suppose the device is not responding) */
  if (dev->requestQueueGeometry.queue_offset > 2*MAX_HMI_FIFO_SIZE ||
      dev->requestQueueGeometry.queue_size   < 2                     ||
      dev->requestQueueGeometry.queue_size   > 20                    ||
      dev->requestQueueGeometry.element_size > MAX_HMI_FIFO_SIZE   ||
      dev->replyQueueGeometry.queue_offset   > 2*MAX_HMI_FIFO_SIZE ||
      dev->replyQueueGeometry.queue_size     < 2                     ||
      dev->replyQueueGeometry.queue_size     > 20                    ||
      dev->replyQueueGeometry.element_size   > MAX_HMI_FIFO_SIZE)
  {
    dev->requestQueueGeometry.queue_offset = 0;
    dev->requestQueueGeometry.queue_size   = 2;
    dev->requestQueueGeometry.element_size = 512;
    dev->replyQueueGeometry.queue_offset   = 0;
    dev->replyQueueGeometry.queue_size     = 2;
    dev->replyQueueGeometry.element_size   = 512;
  }
}

/* }}} */
/* {{{ hpiReadReplyTail: retrieve the tail value for the reply FIFO */

static uint8 hpiReadReplyTail(MsgDev *dev)
{
  SharedQueueGeometry *geometry = &dev->replyQueueGeometry;
  uint8                value;
  
  transferReq(DEV_ADDRESS(dev->devId), BCM_READ,
              geometry->queue_offset + TAIL_OFFSET,
              &value,
              sizeof(uint8));

  return value;
}

/* }}} */
/* {{{ hpiReadReplyHead: retrieve the head value for the reply FIFO */

static uint8 hpiReadReplyHead(MsgDev *dev)
{
  SharedQueueGeometry *geometry = &dev->replyQueueGeometry;
  uint8                value;
  
  transferReq(DEV_ADDRESS(dev->devId), BCM_READ,
              geometry->queue_offset + HEAD_OFFSET,
              &value,
              sizeof(uint8));

  return value;
}

/* }}} */
/* {{{ hpiReadRequestTail: retrieve the tail value for the request FIFO */

static uint8 hpiReadRequestTail(MsgDev *dev)
{
  SharedQueueGeometry *geometry = &dev->requestQueueGeometry;
  uint8                value;
  
  transferReq(DEV_ADDRESS(dev->devId), BCM_READ,
              geometry->queue_offset + TAIL_OFFSET,
              &value,
              sizeof(uint8));

  return value;
}

/* }}} */
/* {{{ hpiReadRequestHead: retrieve the head value for the request FIFO */

static uint8 hpiReadRequestHead(MsgDev *dev)
{
  SharedQueueGeometry *geometry = &dev->requestQueueGeometry;
  uint8                value;
  
  transferReq(DEV_ADDRESS(dev->devId), BCM_READ,
              geometry->queue_offset + HEAD_OFFSET,
              &value,
              sizeof(uint8));

  return value;
}

/* }}} */
/* {{{ hpiWriteReplyHead: write the head value for the reply FIFO */

static void hpiWriteReplyHead(MsgDev *dev, uint8 head)
{
  SharedQueueGeometry *geometry = &dev->replyQueueGeometry;
  
  transferReq(DEV_ADDRESS(dev->devId), BCM_WRITE,
              geometry->queue_offset + HEAD_OFFSET,
              &head,
              sizeof(uint8));
}

/* }}} */
/* {{{ hpiWriteRequestTail: write the tail value for the request FIFO */

static void hpiWriteRequestTail(MsgDev *dev, uint8 tail)
{
  SharedQueueGeometry *geometry = &dev->requestQueueGeometry;
  
  transferReq(DEV_ADDRESS(dev->devId), BCM_WRITE,
              geometry->queue_offset + TAIL_OFFSET,
              &tail,
              sizeof(uint8));
}

/* }}} */
