view src/g23m-gprs/llc/llc_pei.c @ 220:0ed36de51973

ABB semaphore protection overhaul The ABB semaphone protection logic that came with TCS211 from TI was broken in several ways: * Some semaphore-protected functions were called from Application_Initialize() context. NU_Obtain_Semaphore() called with NU_SUSPEND fails with NU_INVALID_SUSPEND in this context, but the return value wasn't checked, and NU_Release_Semaphore() would be called unconditionally at the end. The latter call would increment the semaphore count past 1, making the semaphore no longer binary and thus no longer effective for resource protection. The fix is to check the return value from NU_Obtain_Semaphore() and skip the NU_Release_Semaphore() call if the semaphore wasn't properly obtained. * Some SPI hardware manipulation was being done before entering the semaphore- protected critical section. The fix is to reorder the code: first obtain the semaphore, then do everything else. * In the corner case of L1/DSP recovery, l1_abb_power_on() would call some non-semaphore-protected ABB & SPI init functions. The fix is to skip those calls in the case of recovery. * A few additional corner cases existed, all of which are fixed by making ABB semaphore protection 100% consistent for all ABB functions and code paths. There is still one remaining problem of priority inversion: suppose a low- priority task calls an ABB function, and some medium-priority task just happens to preempt right in the middle of that semaphore-protected ABB operation. Then the high-priority SPI task is locked out for a non-deterministic time until that medium-priority task finishes its work and goes back to sleep. This priority inversion problem remains outstanding for now.
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 26 Apr 2021 20:55:25 +0000
parents fa8dc04885d8
children
line wrap: on
line source

/*
+-----------------------------------------------------------------------------
|  Project :
|  Modul   :
+-----------------------------------------------------------------------------
|  Copyright 2002 Texas Instruments Berlin, AG
|                 All rights reserved.
|
|                 This file is confidential and a trade secret of Texas
|                 Instruments Berlin, AG
|                 The receipt of or possession of this file does not convey
|                 any rights to reproduce or disclose its contents or to
|                 manufacture, use, or sell anything it may describe, in
|                 whole, or in part, without the specific written consent of
|                 Texas Instruments Berlin, AG.
+-----------------------------------------------------------------------------
|  Purpose :  This module implements the process body interface
|             for the entity Logical Link Control (LLC)
|
|             Exported functions:
|
|             pei_create    - Create the Protocol Stack Entity
|             pei_init      - Initialize Protocol Stack Entity
|             pei_primitive - Process Primitive
|             pei_timeout   - Process Timeout
|             pei_exit      - Close resources and terminate
|             pei_run       - Process Primitive
|             pei_config    - Dynamic Configuration
|             pei_monitor   - Monitoring of physical Parameters
+-----------------------------------------------------------------------------
*/

#define LLC_PEI_C

#define ENTITY_LLC

/*==== INCLUDES =============================================================*/

#include <stddef.h>     /* to get definition of offsetof(), for MAK_FUNC_S */
#include <stdlib.h>     /* to get atoi() */
#include "typedefs.h"   /* to get Condat data types */
#include "vsi.h"        /* to get a lot of macros */
#include "macdef.h"
#include "gprs.h"
#include "gsm.h"        /* to get a lot of macros */
#include "cnf_llc.h"    /* to get cnf-definitions */
#include "mon_llc.h"    /* to get mon-definitions */
#include "prim.h"       /* to get the definitions of used SAP and directions */
#include "pei.h"        /* to get PEI interface */
#include "llc.h"        /* to get the global entity definitions */
#include "llc_f.h"        /* to get the global entity definitions */

#include "llc_llmep.h"  /* to get primitive interface to LLME */
#include "llc_up.h"     /* to get primitive interface to U */
#include "llc_itxp.h"   /* to get primitive interface to ITX */
#include "llc_irxp.h"   /* to get primitive interface to IRX */
#include "llc_itxt.h"   /* to get timer interface to IRX */
#include "llc_uitxp.h"  /* to get primitive interface to UITX */
#include "llc_uirxp.h"  /* to get primitive interface to UIRX */
#include "llc_t200p.h"  /* to get primitive interface to T200 */
#include "llc_txp.h"    /* to get primitive interface to TX */
#include "llc_rxp.h"    /* to get primitive interface to RX */
#include "llc_llmef.h"  /* to get init function of LLME */
#include "llc_uf.h"     /* to get init function of U */
#include "llc_itxf.h"   /* to get init function of ITX */
#include "llc_irxf.h"   /* to get init function of IRX */
#include "llc_uitxf.h"  /* to get init function of UITX */
#include "llc_uirxf.h"  /* to get init function of UIRX */
#include "llc_t200f.h"  /* to get init function of T200 */
#include "llc_txf.h"    /* to get init function of TX */
#include "llc_rxf.h"    /* to get init function of RX */



/*==== DEFINITIONS ==========================================================*/

/*==== TYPES ================================================================*/

/*==== GLOBAL VARS ==========================================================*/

/*==== LOCAL VARS ===========================================================*/

static  BOOL          first_access  = TRUE;
static  T_MONITOR     llc_mon;

/*
 * Jumptables to primitive handler functions. One table per SAP.
 *
 * Use MAK_FUNC_0 for primitives which contains no SDU.
 * Use MAK_FUNC_S for primitives which contains a SDU.
 */

/*
 * This Function is needed for primitives, that are not (yet?) supported.
 * It frees the primitive for which it is specified in the jump table.
 */
LOCAL void primitive_not_supported (T_PRIM_HEADER *data);


static const T_FUNC ll_table[] =
{
  MAK_FUNC_S(u_ll_establish_req,        LL_ESTABLISH_REQ),
  MAK_FUNC_S(u_ll_establish_res,        LL_ESTABLISH_RES),
  MAK_FUNC_0(u_ll_release_req,          LL_RELEASE_REQ),
  MAK_FUNC_S(u_ll_xid_req,              LL_XID_REQ),
  MAK_FUNC_S(u_ll_xid_res,              LL_XID_RES),
  MAK_FUNC_0(irx_ll_getdata_req,        LL_GETDATA_REQ),
  MAK_FUNC_0(uirx_ll_getunitdata_req,   LL_GETUNITDATA_REQ),
#ifdef LL_DESC
  MAK_FUNC_S(itx_ll_data_req,           LL_DATA_REQ),
  MAK_FUNC_S(uitx_ll_unitdata_req,      LL_UNITDATA_REQ),
  MAK_FUNC_0(itx_ll_desc_req,           LL_DESC_REQ),
  MAK_FUNC_0(uitx_ll_unitdesc_req,      LL_UNITDESC_REQ),
#else
  MAK_FUNC_S(itx_ll_desc_req,           LL_DATA_REQ),
  MAK_FUNC_S(uitx_ll_unitdesc_req,      LL_UNITDATA_REQ)
#endif
};

static const T_FUNC llgmm_table[] =
{
  MAK_FUNC_0(llme_llgmm_assign_req,     LLGMM_ASSIGN_REQ),
  MAK_FUNC_0(llme_llgmm_trigger_req,    LLGMM_TRIGGER_REQ),
  MAK_FUNC_0(llme_llgmm_suspend_req,    LLGMM_SUSPEND_REQ),
  MAK_FUNC_0(llme_llgmm_resume_req,     LLGMM_RESUME_REQ)
};

static const T_FUNC grlc_table[] =
{
  MAK_FUNC_0(rx_grlc_data_ind,          GRLC_DATA_IND),
#ifdef _SIMULATION_
  MAK_FUNC_S(rx_grlc_data_ind_test,      GRLC_DATA_IND_TEST),
#else
  MAK_FUNC_N(primitive_not_supported,   GRLC_DATA_IND_TEST),
#endif
  MAK_FUNC_0(rx_grlc_unitdata_ind,       GRLC_UNITDATA_IND),
#ifdef _SIMULATION_
  MAK_FUNC_S(rx_grlc_unitdata_ind_test,  GRLC_UNITDATA_IND_TEST),
#else
  MAK_FUNC_N(primitive_not_supported,   GRLC_UNITDATA_IND_TEST),
#endif
  MAK_FUNC_N(tx_grlc_ready_ind,          GRLC_READY_IND),
  MAK_FUNC_N(tx_grlc_suspend_ready_ind,  GRLC_SUSPEND_READY_IND)
};


/*==== PRIVATE FUNCTIONS ====================================================*/

/*
+------------------------------------------------------------------------------
| Function    : primitive_not_supported
+------------------------------------------------------------------------------
| Description : This function handles unsupported primitives.
|
| Parameters  : -
|
| Return      : -
|
+------------------------------------------------------------------------------
*/
LOCAL void primitive_not_supported (T_PRIM_HEADER *data)
{
  TRACE_FUNCTION ("primitive_not_supported");

  PFREE (data);
}


/*==== PUBLIC FUNCTIONS =====================================================*/

/*
+------------------------------------------------------------------------------
| Function    : pei_primitive
+------------------------------------------------------------------------------
| Description :  This function is called by the frame when a primitive is
|                received and needs to be processed.
|
|                          |           |
|                        LLGMM         LL               UPLINK
|                          |           |
|                   +------v-----------v-------+
|                   |                          |
|                   |            LLC           |
|                   |                          |
|                   +-------------^------------+
|                                 |
|                                GRLC                    DOWNLINK
|                                 |
|
|
| Parameters  : prim      - Pointer to the received primitive
|
| Return      : PEI_OK    - function succeeded
|               PEI_ERROR - function failed
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_primitive (void * primptr)
{
  TRACE_FUNCTION ("pei_primitive");

  if (primptr != NULL)
  {
    T_PRIM *prim  = (T_PRIM *)primptr;
    ULONG     opc = prim->custom.opc;
    USHORT           n;
    const T_FUNC    *table;

    /*
     * This must be called to enable Partition Pool (memory) supervision.
     */
    VSI_PPM_REC (&prim->custom, __FILE__, __LINE__);

    switch (SAP_NR(opc))
    {
      case SAP_NR(LLGMM_UL):
        table = llgmm_table;
        n = TAB_SIZE (llgmm_table);
        break;
      case SAP_NR(LL_UL):
#ifdef TRACE_EVE
        {
          /*
           * following line requires sapi is always the first struct member
           * in all LL primitive headers and always using the same size.
           */
          T_LL_GETUNITDATA_REQ* pData = (T_LL_GETUNITDATA_REQ*)(P2D(prim));

          switch (pData->sapi)
          {
            case LL_SAPI_1: TRACE_PRIM_FROM("GMM");  break;
#ifdef LL_2to1
            case LL_SAPI_7: TRACE_PRIM_FROM("MM"); break;
#else
            case LL_SAPI_7: TRACE_PRIM_FROM("GSMS"); break;
#endif
            default:                                 break;
          }
        }
#endif
        table = ll_table;
        n = TAB_SIZE (ll_table);
        break;
      case SAP_NR(GRLC_DL):
        table = grlc_table;
        n = TAB_SIZE (grlc_table);
        break;
      default:
        table = NULL;
        n = 0;
        break;
    }

    PTRACE_IN (opc);

    if (table != NULL)
    {
      if ((PRIM_NR(opc)) < n)
      {
        table += PRIM_NR(opc);
#ifdef PALLOC_TRANSITION
        P_SDU(prim) = table->soff ? (T_sdu*) (((char*)&prim->data) + table->soff) : 0;
#ifndef NO_COPY_ROUTING
        P_LEN(prim) = table->size + sizeof (T_PRIM_HEADER);
#endif /* NO_COPY_ROUTING */
#endif /* PALLOC_TRANSITION */
        JUMP (table->func) (P2D(prim));
      }
      else
      {
        primitive_not_supported (P2D(prim));
      }
      return PEI_OK;
    }

    /*
     * primitive is not a GSM primitive - forward it to the environment
     */
    if (opc & SYS_MASK)
      vsi_c_primitive (VSI_CALLER prim);
    else
    {
      PFREE (P2D(prim));
      return PEI_ERROR;
    }
  }
  return PEI_OK;
}


/*
+------------------------------------------------------------------------------
| Function    : pei_init
+------------------------------------------------------------------------------
| Description : This function is called by the frame. It is used to initialise
|               the entitiy.
|
| Parameters  : handle            - task handle
|
| Return      : PEI_OK            - entity initialised
|               PEI_ERROR         - entity not (yet) initialised
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_init (T_HANDLE handle)
{
  TRACE_FUNCTION ("pei_init");

  /*
   * Initialize task handle
   */
  LLC_handle = handle;

  /*
   * Open communication channels
   */
  if (hCommGMM < VSI_OK)
  {
#ifdef LL_2to1
    if ((hCommGMM = vsi_c_open (VSI_CALLER MM_NAME)) < VSI_OK)
#else
    if ((hCommGMM = vsi_c_open (VSI_CALLER GMM_NAME)) < VSI_OK)
#endif
      return PEI_ERROR;
  }
  if (hCommSNDCP < VSI_OK)
  {
    if ((hCommSNDCP = vsi_c_open (VSI_CALLER SNDCP_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
#ifdef LL_2to1
  if (hCommMM < VSI_OK)
  {
    if ((hCommMM = vsi_c_open (VSI_CALLER MM_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
#else
  if (hCommGSMS < VSI_OK)
  {
    if ((hCommGSMS = vsi_c_open (VSI_CALLER GSMS_NAME)) < VSI_OK)
      return PEI_ERROR;
  }
#endif
  if (hCommGRLC < VSI_OK)
  {
    if ((hCommGRLC = vsi_c_open (VSI_CALLER GRLC_NAME)) < VSI_OK)
      return PEI_ERROR;
  }


  /*
   * Initialize global pointer llc_data. This is required to access all
   * entity data.
   */
  llc_data = &llc_data_base;


  /*
   * Initialize entity data (call init function of every service)
   */
  llme_init();
  u_init();
  itx_init();
  irx_init();
  uitx_init();
  uirx_init();
  t200_init();
  llc_tx_init();
  rx_init();
#ifndef TI_PS_OP_CIPH_DRIVER
  llc_fbs_init();
#endif
  return (PEI_OK);
}

/*
+------------------------------------------------------------------------------
| Function    : pei_timeout
+------------------------------------------------------------------------------
| Description : This function is called by the frame when a timer has expired.
|
| Parameters  : index             - timer index
|
| Return      : PEI_OK            - timeout processed
|               PEI_ERROR         - timeout not processed
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_timeout (USHORT index)
{
  TRACE_FUNCTION ("pei_timeout");

  /*
   * Process timeout
   */
  switch (index)
  {
    case TIMER_T200_1:
      /*
       * T200 for SAPI 1 expired.
       */
      t200_timer_t200 (LL_SAPI_1);
      break;
    case TIMER_T200_3:
      /*
       * T200 for SAPI 3 expired.
       */
      t200_timer_t200 (LL_SAPI_3);
      break;
    case TIMER_T200_5:
      /*
       * T200 for SAPI 5 expired.
       */
      t200_timer_t200 (LL_SAPI_5);
      break;
    case TIMER_T200_7:
      /*
       * T200 for SAPI 7 expired.
       */
      t200_timer_t200 (LL_SAPI_7);
      break;
    case TIMER_T200_9:
      /*
       * T200 for SAPI 9 expired.
       */
      t200_timer_t200 (LL_SAPI_9);
      break;
    case TIMER_T200_11:
      /*
       * T200 for SAPI 11 expired.
       */
      t200_timer_t200 (LL_SAPI_11);
      break;
    case TIMER_T201_3:
      /*
       * T201 for SAPI 3 expired.
       */
      itx_timer_t201 (LL_SAPI_3);
      break;
    case TIMER_T201_5:
      /*
       * T201 for SAPI 5 expired.
       */
      itx_timer_t201 (LL_SAPI_5);
      break;
    case TIMER_T201_9:
      /*
       * T201 for SAPI 9 expired.
       */
      itx_timer_t201 (LL_SAPI_9);
      break;
    case TIMER_T201_11:
      /*
       * T201 for SAPI 11 expired.
       */
      itx_timer_t201 (LL_SAPI_11);
      break;
    default:
      TRACE_ERROR("Unknown Timeout");
      break;
  }

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
| Function    : pei_signal
+------------------------------------------------------------------------------
| Description : This function is called by the frame when a signal has been
|               received.
|
| Parameters  : opc               - signal operation code
|               *data             - pointer to primitive
|
| Return      : PEI_OK            - signal processed
|               PEI_ERROR         - signal not processed
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_signal (ULONG opc, void *data)
{
  TRACE_FUNCTION ("pei_signal");

  /*
   * Process signal
   */
  TRACE_ERROR("Unknown Signal OPC");

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
| Function    : pei_exit
+------------------------------------------------------------------------------
| Description : This function is called by the frame when the entity is
|               terminated. All open resources are freed.
|
| Parameters  : -
|
| Return      : PEI_OK            - exit sucessful
|               PEI_ERROR         - exit not sueccessful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_exit (void)
{
  TRACE_FUNCTION ("pei_exit");

  /*
   * Close communication channels
   */
  vsi_c_close (VSI_CALLER hCommGMM);
  hCommGMM = VSI_ERROR;

  vsi_c_close (VSI_CALLER hCommSNDCP);
  hCommSNDCP = VSI_ERROR;

#ifdef LL_2to1
  vsi_c_close (VSI_CALLER hCommMM);
  hCommMM = VSI_ERROR;
#else
  vsi_c_close (VSI_CALLER hCommGSMS);
  hCommGSMS = VSI_ERROR;
#endif

  vsi_c_close (VSI_CALLER hCommGRLC);
  hCommGRLC = VSI_ERROR;

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
| Function    : pei_run
+------------------------------------------------------------------------------
| Description : This function is called by the frame when entering the main
|               loop. This fucntion is only required in the active variant.
|
|               This function is not used.
|
| Parameters  : handle            - Communication handle
|
| Return      : PEI_OK            - sucessful
|               PEI_ERROR         - not successful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_run (T_HANDLE TaskHandle, T_HANDLE ComHandle )
{
/*
 * Does not compile!
 *
  T_QMSG Message;

  TRACE_FUNCTION ("pei_run");

  if (pei_init (TaskHandle) != PEI_OK)
    return PEI_ERROR;

  while (!exit_flag)
  {
    vsi_c_await (VSI_CALLER ComHandle, &Message);
    switch (Message.MsgType)
    {
      case MSG_PRIMITIVE:
        pei_primitive (Message.Msg.Primitive.Prim );
        break;
      case MSG_SIGNAL:
        pei_signal ( (USHORT)Message.Msg.Signal.SigOPC,
          Message.Msg.Signal.SigBuffer );
        break;
      case MSG_TIMEOUT:
        pei_timeout ( (USHORT)Message.Msg.Timer.Index );
        break;
      default:
        TRACE_ERROR("Unknown Message Type");
        break;
    }
  }

 *
 *
 */

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
| Function    : pei_config
+------------------------------------------------------------------------------
| Description : This function is called by the frame when a primitive is
|               received indicating dynamic configuration.
|
|               This function is not used in this entity.
|
| Parameters  : handle            - Communication handle
|
| Return      : PEI_OK            - sucessful
|               PEI_ERROR         - not successful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_config (char *inString)
{
  TRACE_FUNCTION ("pei_config");
  TRACE_FUNCTION (inString);

#ifdef _SIMULATION_
#ifndef NCONFIG
  /*
   * Parse for config keywords:
   *     SAPI XX KU YYYY
   *     SAPI XX KD YYYY
   *     SAPI XX MU YYYY
   *     SAPI XX MD YYYY
   *     SAPI XX N200 YYYY
   *     SAPI XX T200 YYYY
   *     SAPI XX N201_U YYYY
   *     SAPI XX N201_I YYYY
   * The configured values are currently stored in the requested_xid struct, which is
   * refilled completely after change into LLC assigned state and after LLC XID reset.
   */
  if (inString[0] == 'S' AND inString[1] == 'A' AND inString[2] == 'P' AND
      inString[3] == 'I' AND inString[4] == ' ')
  {
    UBYTE  sapi = 0;
    UBYTE  i = 5;

    /* get first sapi number */
    if (inString[i] >= '0' AND inString[i] <= '9')
    {
      sapi = (inString[i] - '0');
      i++;
    }

    /* get second sapi number, if available */
    if (inString[i] >= '0' AND inString[i] <= '9')
    {
      sapi = (sapi * 10) + (inString[i] - '0');
      i++;
    }

    /* skip next whitespace */
    i++;

    /* get XID value to change */
    if (inString[i] == 'K' AND inString[i+2] == ' ')
    {
      if (inString[i+1] == 'U')      /* KU */
      {
        llc_data->ffs_xid.ku[IMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.ku[IMAP(sapi)].value = (UBYTE)atoi(&inString[i+3]);
      }
      else                           /* KD */
      {
        llc_data->ffs_xid.kd[IMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.kd[IMAP(sapi)].value = (UBYTE)atoi(&inString[i+3]);
      }
    }
    else if (inString[i] == 'M' AND inString[i+2] == ' ')
    {
      if (inString[i+1] == 'U')      /* MU */
      {
        llc_data->ffs_xid.mu[IMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.mu[IMAP(sapi)].value = (UBYTE)atoi(&inString[i+3]);
      }
      else                           /* MD */
      {
        llc_data->ffs_xid.md[IMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.md[IMAP(sapi)].value = (UBYTE)atoi(&inString[i+3]);
      }
    }
    else if (inString[i+0] == 'N' AND inString[i+1] == '2' AND inString[i+2] == '0' AND
             inString[i+3] == '1' AND inString[i+4] == '_' AND inString[i+6] == ' '   )
    {
      if (inString[i+5] == 'I')      /* N201_I */
      {
        llc_data->ffs_xid.n201_i[IMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.n201_i[IMAP(sapi)].value = (USHORT)atoi(&inString[i+7]);
      }
      else if (inString[i+5] == 'U') /* N201_U */
      {
        llc_data->ffs_xid.n201_u[UIMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.n201_u[UIMAP(sapi)].value = (USHORT)atoi(&inString[i+7]);
      }
    }
    else if (inString[i+1] == '2' AND inString[i+2] == '0' AND inString[i+3] == '0' AND
             inString[i+4] == ' ')
    {
      if (inString[i+0] == 'N')      /* N200 */
      {
        llc_data->ffs_xid.n200[UIMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.n200[UIMAP(sapi)].value = (UBYTE)atoi(&inString[i+5]);
      }
      else if (inString[i+0] == 'T') /* T200 */
      {
        llc_data->ffs_xid.t200[UIMAP(sapi)].valid = TRUE;
        llc_data->ffs_xid.t200[UIMAP(sapi)].value = (USHORT)atoi(&inString[i+5]);
      }
    }
  } 

#endif

#else /*_SIMULATION_*/
/* CCI_INFO */ 
  if(inString[0] == 'C' AND 
     inString[1] == 'C' AND 
     inString[2] == 'I' AND
     inString[3] == '_' AND 
     inString[4] == 'I' AND
     inString[5] == 'N' AND
     inString[6] == 'F' AND
     inString[7] == 'O') {
    TRACE_EVENT("CCI Info Trace Enabled");
    llc_fbs_enable_cci_info_trace();
  } else if (inString[0] == 'D' &&
               inString[1] == 'E' &&
               inString[2] == 'L' &&
               inString[3] == 'A' &&
               inString[4] == 'Y') {

      USHORT millis = (USHORT)atoi(&inString[5]);
      llc_data->millis = millis;
      TRACE_1_OUT_PARA("Delay timer :%d milliseconds", llc_data->millis);
  } else {
    TRACE_EVENT("PEI ERROR: invalid config primitive");
  }
#endif/*_SIMULATION_*/

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
| Function    : pei_config
+------------------------------------------------------------------------------
| Description : This function is called by the frame in case sudden entity
|               specific data is requested (e.g. entity Version).
|
| Parameters  : out_monitor       - return the address of the data to be
|                                   monitoredCommunication handle
|
| Return      : PEI_OK            - sucessful (address in out_monitor is valid)
|               PEI_ERROR         - not successful
|
+------------------------------------------------------------------------------
*/
LOCAL SHORT pei_monitor (void ** out_monitor)
{
  TRACE_FUNCTION ("pei_monitor");

  /*
   * Version = "0.S" (S = Step).
   */
  llc_mon.version = "LLC 1.0";
  *out_monitor = &llc_mon;

  return PEI_OK;
}

/*
+------------------------------------------------------------------------------
| Function    : pei_create
+------------------------------------------------------------------------------
| Description :  This function is called by the frame when the process is
|                created.
|
| Parameters  : out_name          - Pointer to the buffer in which to locate
|                                   the name of this entity
|
| Return      : PEI_OK            - entity created successfuly
|               PEI_ERROR         - entity could not be created
|
+------------------------------------------------------------------------------
*/
GLOBAL SHORT pei_create (T_PEI_INFO **info)
{
static T_PEI_INFO pei_info =
              {
               "LLC",         /* name */
               {              /* pei-table */
                 pei_init,
                 pei_exit,
                 pei_primitive,
                 pei_timeout,
                 pei_signal,
                 pei_run,
                 pei_config,
                 pei_monitor
               },
               2560,          /* stack size */
               32,            /* queue entries */
               200,           /* priority (1->low, 255->high) */
               TIMER_NUM,     /* number of timers */
#ifdef _TARGET_
               PASSIVE_BODY|COPY_BY_REF|TRC_NO_SUSPEND|PRIM_NO_SUSPEND
#else
               PASSIVE_BODY|COPY_BY_REF
#endif
              };


  TRACE_FUNCTION ("pei_create");

  /*
   * Close Resources if open
   */
  if (first_access)
    first_access = FALSE;
  else
    pei_exit();

  /*
   * Export startup configuration data
   */
  *info = &pei_info;

  return PEI_OK;
}

/*==== END OF FILE ==========================================================*/