view src/g23m-fad/l2r/l2r_upf.c @ 633:f760e858098b

doc/Compiling: tangomdm target added
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 04 Jan 2020 20:12:07 +0000
parents 90eb61ecd093
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  CSD (8411)
|  Modul   :  L2r_upf.c
+----------------------------------------------------------------------------- 
|  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 Modul defines the procedures and functions for
|             the component L2R of the base station
+----------------------------------------------------------------------------- 
*/ 

#ifndef L2R_UPF_C
#define L2R_UPF_C
#endif

#define ENTITY_L2R

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

#include <string.h>
#include "typedefs.h"
#include "pconst.cdg"
#include "vsi.h"
#include "macdef.h"
#include "custom.h"
#include "gsm.h"
#include "cus_l2r.h"
#include "cnf_l2r.h"
#include "mon_l2r.h"
#include "prim.h"
#include "pei.h"
#include "tok.h"
#include "dti.h"      /* functionality of the dti library */

#include "cl_ribu.h"
#include "l2r.h"

/*==== CONST =======================================================*/

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

/*==== VAR EXPORT ==================================================*/

/*==== VAR LOCAL ===================================================*/

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

/*
+------------------------------------------------------------------------------
|  Function    : up_init
+------------------------------------------------------------------------------
|  Description : initialise the l2r data for the uplink process
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_init(T_UP *dup)
{
  register ULONG i;

  TRACE_FUNCTION ("up_init()");

  dup->FlowCtrlUsed      = FALSE;
  dup->DnFlow            = FL_INACTIVE;
  dup->UpFlow            = FL_INACTIVE;
  dup->ULFlow            = FL_INACTIVE;
  dup->LLFlow            = FL_INACTIVE;
  dup->MrgFlow           = FL_INACTIVE;
  dup->FlowThreshLo      = MAX_UPRIM_RIBU_SIZE/2;
  dup->FlowThreshHi      = 3*MAX_UPRIM_RIBU_SIZE/4;
  dup->DataSize          = 0;
  dup->FrameSize         = 0;
  dup->OldFrameSize      = 0;
  dup->FramesPerPrim     = L2R_FRAMES_PER_PRIM_MAX;
  dup->LastRcvdSa        = 0;
  dup->LastRcvdSb        = 0;
  dup->LastSentSa        = 0;
  dup->LastSentSb        = 0;
  dup->LastSentFlow      = FL_INACTIVE;
  dup->StoreDataActive   = FALSE;
  dup->UrgentMsg         = FALSE;
  dup->DiscardRemapData  = FALSE;
  dup->QRemapRead        = 0;
  dup->QRemapWrite       = 0;
  dup->BRemapAdr         = NULL;
  dup->BRemapLen         = 0;
  dup->BRemapSa          = 0;
  dup->BRemapSb          = 0;
  dup->BRemapLastState   = 0;
  dup->QRemapPrimDesc.prim = NULL;

  for (i = 0; i < MAX_UP_REMAP_QUEUE_SIZE; i++)
  {
    dup->QRemap[i] = NULL;
  }

  for (i = 0; i < UP_REMAP_BUFFER_SIZE; i++)
  {
    dup->BRemap[i] = 0;
  }

  dup->RiBu.idx.depth = MAX_UPRIM_RIBU_SIZE;

  INIT_STATE (UP_UL, IW_IDLE);
  INIT_STATE (UP_LL, ISW_IDLE);
  INIT_STATE (UP, UP_DISCONNECTED);
}

/*
+------------------------------------------------------------------------------
|  Function    : up_alloc_prim
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/


LOCAL void up_alloc_prim(void)
{
  T_UP *dup = &l2r_data->up;

  T_PRIM_INDEX          m;
  T_P_UPRIM_DESCRIPTOR  primDesc;
  USHORT                sduSize;

  TRACE_FUNCTION ("up_alloc_prim()");

  sduSize = dup->FramesPerPrim * dup->FrameSize * 8;
  primDesc = dup->RiBu.primDesc[dup->RiBu.alloc];
  {
    PALLOC_SDU(data_req, RLP_DATA_REQ, sduSize);
    primDesc->prim = data_req;
  }

#ifdef _SIMULATION_
  /* Clear SDU for test environment */
  {
  UBYTE *p;
  UBYTE *pend;
  p = &primDesc->prim->sdu.buf[0];
  pend = p + (sduSize >> 3);
  while (p < pend)
  {
    *p++ = 0;
  }
  }
#endif

  primDesc->prim->sdu.o_buf = 0;
  primDesc->prim->sdu.l_buf = sduSize;
  primDesc->nFr             = dup->FramesPerPrim;
  primDesc->index           = 0;
  primDesc->offset          = 0;
  primDesc->off_status      = 0;
  primDesc->full            = FALSE;
  primDesc->sa              = FL_INACTIVE;
  primDesc->sb              = FL_INACTIVE;
  /*lint -e416 (Warning -- Likely creation of out-of-bounds) */
  for (m = 0; m != dup->FramesPerPrim; m++)
  {
    (*primDesc->adr)[m] = (T_P_L2R_FRAME)(primDesc->prim->sdu.buf +L2R_ENCODING_OFFSET + m * dup->FrameSize);    
  }
   /*lint +e416 (Warning -- Likely creation of out-of-bounds) */	
  dup->RiBu.alloc++;
  if (dup->RiBu.alloc EQ dup->RiBu.idx.depth)
  {
    dup->RiBu.alloc = 0;
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_send_prim_timeout
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/


GLOBAL void up_send_prim_timeout(void)
{
  T_UP *dup = &l2r_data->up;

  TRACE_FUNCTION ("up_send_prim_timeout()");

  if (!dup->RiBu.idx.filled)
  {
    if (dup->RiBu.alloc EQ dup->RiBu.idx.wi)
    {
      /* No primitive is there, so we allocate one */
      up_alloc_prim();
    }

    /*
    No primitive is ready, so we take one away from the relay entity
    */
    cl_ribu_write_index(&dup->RiBu.idx); /* point to next primitive */
  }
  up_send_current_prim();
}

/*
+------------------------------------------------------------------------------
|  Function    : up_next_remap_frame
+------------------------------------------------------------------------------
|  Description : Local function, which is used by up_copy_data_from_l2r 
|                to advance to the next frame in the primitive.
|                The variable primDesc of the calling function is updated.
|
|  Parameters  : primDesc -
|
|  Return      : 1 -
|                0 -
+------------------------------------------------------------------------------
*/

LOCAL UBYTE up_next_remap_frame(T_RPRIM_DESCRIPTOR *primDesc)
{
  T_UP *dup = &l2r_data->up;

  U8 i;
  T_P_RLP_REMAP_DATA_IND prim;

  /* next frame */
  primDesc->index++;

  if (primDesc->index >= primDesc->nFr)
  {
    /* primitive is completely read out */
    PFREE(dup->QRemap [dup->QRemapRead]);

    /* next primitive */
    (dup->QRemapRead)++;
    if (dup->QRemapRead >= MAX_UP_REMAP_QUEUE_SIZE)
    {
      dup->QRemapRead = 0;
    }
    if (dup->QRemapRead EQ dup->QRemapWrite)
    {
      /* no primitive is ready */
      primDesc->prim = NULL;
      return (1);
    }

    /* point to next primitive descriptor */

    prim            = dup->QRemap[dup->QRemapRead];
    primDesc->prim  = prim;
    primDesc->nFr   = prim->sdu.l_buf / (8 * prim->data_size + HT_LEN);
    primDesc->index = 0;

    for (i = 0; i < primDesc->nFr; i++)
    {
      primDesc->adr[i] = (T_P_L2R_FRAME)(prim->sdu.buf + (prim->sdu.o_buf>>3)
                          + HEADER_LEN + i * (prim->data_size + HT_LEN));
    }
  }
  primDesc->offset      = 0;
  primDesc->off_status  = 0;
  return (0);
}

/*
+------------------------------------------------------------------------------
|  Function    : up_copy_data_from_rq
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : buf         -
|                len         -
|                sa          -
|                sb          -
|                bytesCopied -
|
|  Return      : -
+------------------------------------------------------------------------------
*/

LOCAL void up_copy_data_from_rq(U8 *buf, U16 len, U8 *sa, U8 *sb, U16 *bytesCopied)
{
  T_UP *dup = &l2r_data->up;

  register ULONG          i;
  register T_P_UBYTE      pFrame;
  register T_P_UBYTE      pBuf;
  register T_P_UBYTE      pStat;
  T_P_RLP_REMAP_DATA_IND  prim;
  T_P_L2R_FRAME           frame;
  T_P_UBYTE               pEnd;
  USHORT                  bytesToCopy;
  USHORT                  blocklen;
  UBYTE                   statOct;
  UBYTE                   lastState;

  T_RPRIM_DESCRIPTOR *primDesc = &dup->QRemapPrimDesc;

#ifdef _SIMULATION_
  TRACE_FUNCTION ("up_copy_data_from_rq()");
#endif

  bytesToCopy = len;

  if (primDesc->prim EQ NULL)
  {
    prim                  = dup->QRemap[dup->QRemapRead];
    primDesc->prim        = prim;
    primDesc->nFr         = prim->sdu.l_buf / ((prim->data_size<<3) + HT_LEN);
    primDesc->index       = 0;
    primDesc->offset      = 0;
    primDesc->off_status  = 0;

    for (i = 0; i < primDesc->nFr; i++)
    {
      primDesc->adr[i] = (T_P_L2R_FRAME)(prim->sdu.buf + (prim->sdu.o_buf>>3)
                                         + HEADER_LEN + i * (prim->data_size + HT_LEN));
    }
  }

  /* skip empty primitive */
  if (primDesc->nFr EQ 0)
  {
    *bytesCopied = 0;
    return;
  }

  frame  = primDesc->adr[primDesc->index];  /* point to current l2r frame in primitive */
  pBuf   = buf;                             /* point to destination buffer */
  pFrame = &((*frame)[primDesc->offset]);
  pStat  = &((*frame)[primDesc->off_status]);

  if (pFrame EQ pStat)
  {
    /* current byte is status octet */
    dup->BRemapLastState = *pFrame & SO_STATUS_BITS_MASK;
  }

  lastState = dup->BRemapLastState;
  *sa = GET_SO_SA_BIT(lastState);
  *sb = GET_SO_SB_BIT(lastState);

  /************************************************************************************
   * loop until either
   *   -  no more data are available or
   *   -  status in L2R frame changes or
   *   -  buffer for data is full
   ************************************************************************************/
  
  for (;;)
  {
#ifdef _SIMULATION_
    TRACE_FUNCTION ("loop");
#endif

    blocklen = pStat - pFrame;
    if (blocklen EQ 0)
    {
      /*
       * current byte is status octet
       * (only in the first pass of the loop, there may be no status octet)
       */

      /*****************************
       *  evaluate status bits
       *****************************/
      statOct = *pFrame;
      if (lastState NEQ (statOct & SO_STATUS_BITS_MASK))
      {
        /*
         *  status has changed. We have to stop,
         *  since only one state can be transmitted to the upper layer
         */
        primDesc->offset = primDesc->off_status = pFrame - (T_P_UBYTE)frame;

        *bytesCopied = len - bytesToCopy;
        return;
      }

      pFrame++;

      /************************************
       *  evaluate addrss bits
       ************************************/
      statOct &= SO_ADR_MASK;

      switch (statOct)
      {
      case SO_BREAK_ACK:
      case SO_BREAK_REQ:
      case SO_END_EMPTY:
        /* no more data in this frame */
        if (up_next_remap_frame(primDesc) EQ 1)
        {
          /* no more data available */
          *bytesCopied = len - bytesToCopy; /* this much data could be copied */
          return;
        }

        frame  = primDesc->adr[primDesc->index];
        pFrame = (T_P_UBYTE)frame;
        pStat  = (T_P_UBYTE)frame;
        continue;  /* continue with next frame */

      case SO_END_FULL:
        pStat = &((*frame)[primDesc->prim->data_size]);
        blocklen = pStat - pFrame;
        break;

      case SO_TWO_OCTET:
          blocklen = *pFrame++ & SO_ADR_MASK_TWO_OCT;
          pStat = pFrame + blocklen;
          break;

      default:
          blocklen = statOct;
          pStat = pFrame + blocklen;
          break;
      }
    }

    if (bytesToCopy < blocklen)
    {
      /***************************************
       *  There is not enough space in the
       *  buffer to copy the complete block
       ***************************************/

      pEnd = pFrame + bytesToCopy;

      while (pFrame < pEnd)
      {
        *pBuf++ = *pFrame++;
      }

      if (pFrame EQ &((*frame)[primDesc->prim->data_size]))
      {
        /* end of frame reached */
        up_next_remap_frame(primDesc);
      }
      else
      {
        primDesc->offset   = pFrame - (T_P_UBYTE)frame;
        primDesc->off_status = pStat  - (T_P_UBYTE)frame;
      }
      *bytesCopied = len; /* this much data could be copied */
      return;
    }
    else
    {
      /***************************************
       *  Copy the complete block
       ***************************************/
      bytesToCopy -= blocklen;

      while (pFrame < pStat)
      {
        *pBuf++ = *pFrame++;
      }

      if (pFrame EQ  &((*frame)[primDesc->prim->data_size]))
      {
        /* end of frame reached */
        if (up_next_remap_frame(primDesc) EQ 1)
        {
          /* no more data available */
          *bytesCopied = len - bytesToCopy; /* this much data could be copied */
          return;
        }
        frame  = primDesc->adr[primDesc->index];
        pFrame = (T_P_UBYTE)frame;
        pStat  = (T_P_UBYTE)frame;
      }
      if (bytesToCopy EQ 0)
      {
        primDesc->offset   = pFrame - (T_P_UBYTE)frame;
        primDesc->off_status = pStat  - (T_P_UBYTE)frame;
        *bytesCopied = len; /* this much data could be copied */
        return;
      }
    }
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_fill_in_status_octet
+------------------------------------------------------------------------------
|  Description : local function, which is used patch a status octet into a
|                l2r frame. This function takes into acount the two octet
|                format for 14400.
|
|  Parameters  : pFrame   - points to begin of l2r frame
|                offstat1 - offset of status octet to be patched
|                offstat2 - offset of next status octet
|
|  Return      :
+------------------------------------------------------------------------------
*/

LOCAL BOOL up_fill_in_status_octet(T_P_UBYTE pFrame, UBYTE offstat1, UBYTE offstat2)
{
  register T_P_UBYTE pStat, pSrc, pDes, pEnd;
  register UBYTE dataLen;

  dataLen = offstat2 - offstat1 - 1;  /* number of data bytes between status octets */
  pStat = pFrame + offstat1;

  if (dataLen <= DATA_SIZE_SHORT-2)
  {
    *pStat |= dataLen;
    return (FALSE);
  }
  else
  {
    /* this is only possible with 14400 */
    if (offstat2 < DATA_SIZE_LONG - 1)
    {
      /* a two octet status must be inserted */
      pSrc = pFrame + offstat2;
      pDes = pSrc + 1;
      pEnd = pFrame + offstat1;
      while (pSrc > pEnd)
      {
        *--pDes = *--pSrc;
      }
      *pStat |= SO_TWO_OCTET;
      pStat++;
      *pStat = dataLen;
    }
    else
    {
      /* a additional status octet (FULL) must be inserted somewhere in the frame */
      pSrc = pFrame + offstat2;
      pDes = pSrc + 1;
      pEnd = pFrame + offstat1 + DATA_SIZE_SHORT - 1;
      while (pSrc > pEnd)
      {
        *--pDes = *--pSrc;
      }
      *pStat |= DATA_SIZE_SHORT - 2;
      *pEnd = *pStat & SO_STATUS_BITS_MASK | SO_END_FULL;
    }
    return (TRUE);
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_next_frame
+------------------------------------------------------------------------------
|  Description : local function, which is used by up_copy_data_into_l2r to
|                advance to the next frame in the primitive
|                The variables primDesc, frame and pFrame of the calling function
|                are updated. Moreover the return values of the calling function
|                primToSend and bytesCopied are set.
|
|
|  Parameters  : primDesc   -
|                primToSend -
|
|  Return      :
+------------------------------------------------------------------------------
*/

LOCAL UBYTE up_next_frame(T_P_UPRIM_DESCRIPTOR *primDesc, BOOL *primToSend)
{
  T_UP *dup = &l2r_data->up;

  TRACE_FUNCTION("up_next_frame()");

  (*primDesc)->index++;
  (*primDesc)->offset = 0;

  if ((*primDesc)->index >= dup->FramesPerPrim) /* primitive is filled */
  {
    (*primDesc)->full = TRUE;
    *primToSend = TRUE;

    cl_ribu_write_index(&dup->RiBu.idx); /* point to next primitive */

    if (dup->RiBu.alloc EQ dup->RiBu.idx.wi)
    {
      /* no primitive is ready */
      return (1);
    }

    /* point to next primitive descriptor */
    *primDesc = dup->RiBu.primDesc[dup->RiBu.idx.wi];
    (*primDesc)->index = 0;
  }
  return (0);
}

LOCAL void up_copy_data_into_l2r(U8 *buf, U16 len, U8 sa, U8 sb, U8 x, BOOL *primToSend, U16 *bytesCopied)
{
  T_UP *dup = &l2r_data->up;

  T_P_UPRIM_DESCRIPTOR primDesc;
  T_P_L2R_FRAME frame;
  register T_P_UBYTE pFrame;
  register T_P_UBYTE pEnd;
  register T_P_UBYTE pBuf;
  USHORT bytesToCopy;
  UBYTE frameCount;
  T_FLOW flow = FL_INVALID;

#ifdef _SIMULATION_
  TRACE_FUNCTION ("up_copy_data_into_l2r()");
#endif

  switch (x)
  {
  case DTI_FLOW_ON:
    flow = FL_INACTIVE;
    break;
  case DTI_FLOW_OFF:
    flow = FL_ACTIVE;
    break;
  }

#ifdef _TARGET_
  if (dup->DataSize > DATA_SIZE_SHORT)
  {
    frameCount = 1;
  }
  else
  {
    frameCount = 2;
  }
#else
  {
    frameCount = 100;
  }
#endif

  up_store_status(sa, sb, flow);

  if (dup->LastSentFlow EQ dup->MrgFlow)
  {
    *primToSend = FALSE;
  }
  else
  {
    *primToSend = TRUE;
  }

  /* don't copy into primitive if no primitive is ready */
  if (dup->RiBu.alloc EQ dup->RiBu.idx.wi)
  {
    *bytesCopied = 0;
    return;
  }

  bytesToCopy = len;

  primDesc = dup->RiBu.primDesc[dup->RiBu.idx.wi]; /* point to current primitive descriptor */
  frame = (*primDesc->adr)[primDesc->index];  /* point to current l2r frame in primitive */
  pFrame = &(*frame)[primDesc->offset];       /* point to current byte in frame */
  pBuf = buf;                                 /* point to source buffer */

  /************************************************************************************
   *
   * Handle a partly filled frame, which has been written in the primitive previously
   *
   ************************************************************************************/

  if (primDesc->offset NEQ 0)
  {
    /* partly filled frame is there */

    if (bytesToCopy EQ 0)
    /* no data to copy -> quit without writing status octet */
    {
      *bytesCopied = len;
      return;
    }

    if (sa NEQ primDesc->sa OR sb NEQ primDesc->sb)
    {
      /*
       * status has changed since last time
       */

      /* finish previous status octet */
      if (
          up_fill_in_status_octet(
            (T_P_UBYTE)frame,
            primDesc->off_status,
            primDesc->offset
            )
         )
      {
        /* frame data has been moved to make space for two octet status */
        pFrame++;
        (primDesc->offset)++;

        if (primDesc->offset >= (dup->DataSize))
        {
          /* frame is just filled by patching the previous status frame */

          if (up_next_frame(&primDesc, primToSend) EQ 1)
          {
            /* no more space for data */
            *bytesCopied = 0; /* no data could be copied */
            return;
          }
          frameCount--;
#ifdef _TARGET_
          if (frameCount EQ 0)
          {
            *bytesCopied = 0;  /* no data could be copied */
            return;
          }
#endif
          frame = (*primDesc->adr)[primDesc->index];  /* point to current l2r frame in primitive */
          pFrame = &(*frame)[primDesc->offset];       /* point to current byte in frame */
        }
      }

      if (primDesc->offset NEQ 0)
      {
        /* write status octet */
        *pFrame++ = (U8)(sa << SO_SA_BIT | sb << SO_SB_BIT | 0 << SO_X_BIT);    /* x is set to 0 (inactive) by default */

        /* store current status for next call */
        primDesc->sa = sa;
        primDesc->sb = sb;

        primDesc->off_status = primDesc->offset;
        (primDesc->offset)++;
        if (primDesc->offset >= (dup->DataSize))
        {
          /* frame is just filled by the status octet */

          /* finish previous status octet */
          (*frame)[primDesc->off_status] |= SO_END_EMPTY;

          if (up_next_frame(&primDesc, primToSend) EQ 1)
          {
            /* no more space for data */
            *bytesCopied = 0;         /* no data could be copied */
            return;
          }
          frameCount--;
          if (frameCount EQ 0)
          {
            *bytesCopied = 0;         /* no data could be copied */
            return;
          }
          frame = (*primDesc->adr)[primDesc->index];  /* point to current l2r frame in primitive */
          pFrame = &(*frame)[primDesc->offset];       /* point to current byte in frame */
        }
      }
    }

    /*
     * write data into partly filled frame
     */
    if (primDesc->offset NEQ 0)
    {
      if (bytesToCopy >= dup->DataSize - primDesc->offset)
      {
        /* enough data to fill frame completly */

        for (pEnd = pFrame + dup->DataSize - primDesc->offset; pFrame < pEnd; )
        {
          *pFrame++ = *pBuf++;
        }

        /* finish previous status octet */
        (*frame)[primDesc->off_status] |= SO_END_FULL;

        bytesToCopy -= dup->DataSize - primDesc->offset;

        if (up_next_frame(&primDesc, primToSend) EQ 1)
        {
          /* no more space for data */
          *bytesCopied = len - bytesToCopy; /* this much data could be copied */
          return;
        }
        frameCount--;
        if (frameCount EQ 0)
        {
          *bytesCopied = len - bytesToCopy; /* this much data could be copied */
          return;
        }
        frame = (*primDesc->adr)[primDesc->index];  /* point to current l2r frame in primitive */
        pFrame = &(*frame)[primDesc->offset];       /* point to current byte in frame */

      }
      else
      {
        /* not enough data to fill frame completly */

        for (pEnd = pFrame + bytesToCopy; pFrame < pEnd; )
        {
          *pFrame++ = *pBuf++;
        }
        primDesc->offset += bytesToCopy;
        *bytesCopied = len;
        return; /* Nothing else to do */
      }
    }
  }

  /************************************************************************************
   *
   * Handle consecutive frames, which are filled starting with byte 0
   *
   ************************************************************************************/

  while (bytesToCopy > 0)
  {
    if (bytesToCopy >= dup->DataSize - 1)
    {
      /*
       * There are enough data to fill a frame completely
       */
      *pFrame++ = sa << SO_SA_BIT | sb << SO_SB_BIT | 0 << SO_X_BIT | SO_END_FULL;    /* x is set to 0 (inactive) by default */

      /* store current status for next call */
      primDesc->sa = sa;
      primDesc->sb = sb;

      for (pEnd = pFrame + dup->DataSize - 1; pFrame < pEnd; )
      {
        *pFrame++ = *pBuf++;
      }
      bytesToCopy -= dup->DataSize - 1;

      /* advance to next frame */
      if (up_next_frame(&primDesc, primToSend) EQ 1)
      {
        /* running out of primitives */
        *bytesCopied = len - bytesToCopy; /* this much data could be copied */
        return;
      }
      frameCount--;
      if (frameCount EQ 0)
      {
        *bytesCopied = len - bytesToCopy; /* this much data could be copied */
        return;
      }
      frame = (*primDesc->adr)[primDesc->index];  /* point to current l2r frame in primitive */
      pFrame = &(*frame)[primDesc->offset];       /* point to current byte in frame */
    }
    else
    {
      /*
       * There are some data, but not enough to fill a frame completely
       */
      *pFrame++ = (U8)(sa << SO_SA_BIT | sb << SO_SB_BIT | 0 << SO_X_BIT);    /* x is set to 0 (inactive) by default */

      /* store current status for next call */
      primDesc->sa = sa;
      primDesc->sb = sb;

      for (pEnd = pFrame + bytesToCopy; pFrame < pEnd; )
      {
        *pFrame++ = *pBuf++;
      }
      primDesc->off_status = 0;
      primDesc->offset = bytesToCopy + 1;
      bytesToCopy = 0;
    }
  }
  *bytesCopied = len;
}

/*
+------------------------------------------------------------------------------
|  Function    : up_copy_remap_data
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

LOCAL void up_copy_remap_data(void)
{
  T_UP *dup = &l2r_data->up;

  USHORT bytesCopied;
  BOOL   dummy;

#ifdef _SIMULATION_
  TRACE_FUNCTION ("up_copy_remap_data()");
#endif

  do
  {
    if (dup->BRemapLen EQ 0)
    {
      dup->BRemapAdr = dup->BRemap;
      up_copy_data_from_rq
        (
        dup->BRemapAdr,
        UP_REMAP_BUFFER_SIZE,
        &dup->BRemapSa,
        &dup->BRemapSb,
        &dup->BRemapLen
        );
      if (dup->BRemapLen EQ 0)
      {
        up_check_flow();
        return;
      }
    }
    up_copy_data_into_l2r
      (
      dup->BRemapAdr,
      dup->BRemapLen,
      dup->BRemapSa,
      dup->BRemapSb,
      DTI_FLOW_OFF,
      &dummy,
      &bytesCopied
      );
    dup->BRemapAdr += bytesCopied;
    dup->BRemapLen -= bytesCopied;

  } while (bytesCopied NEQ 0);
}

/*
+------------------------------------------------------------------------------
|  Function    : up_check_alloc
+------------------------------------------------------------------------------
|  Description : This procedure allocates new primitives until there are
|                UP_RIBU_PREALLOC primitives ready.
|
|                I.e.: alloc = write + UP_RIBU_PREALLOC
|
|                It may be less than this, if otherwise the alloc pointer
|                would reach the read pointer.
|
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_check_alloc(void)
{
  T_UP *dup = &l2r_data->up;

  S8 i_from, i_to;

  TRACE_FUNCTION ("up_check_alloc()");

  i_to = dup->RiBu.idx.ri - dup->RiBu.idx.wi - 1;

  if (i_to < 0)
  {
    i_to = i_to + dup->RiBu.idx.depth;
  }

  if (i_to > UP_RIBU_PREALLOC)
  {
    i_to = UP_RIBU_PREALLOC;
  }

  i_from = dup->RiBu.alloc - dup->RiBu.idx.wi;

  if (i_from < 0)
  {
    i_from = i_from + dup->RiBu.idx.depth;
  }

  if (i_from >= i_to)
  {
    return;
  }

  do
  {
    up_alloc_prim();
    i_from++;
  } while (i_from < i_to);

  if (dup->QRemapRead NEQ dup->QRemapWrite)
  {
    up_copy_remap_data();
    return;
  }

  switch (GET_STATE (UP_UL))
  {
  case IW_WAIT:
    break;

  case IW_IDLE:
    if (dup->StoreDataActive EQ FALSE AND dup->Prim NEQ NULL)
    {
      up_store_data();
    }
    break;
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_check_flow
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_check_flow(void)
{
  T_UP *dup = &l2r_data->up;
  
  U8 primCount;

  TRACE_FUNCTION ("up_check_flow()");

  if (!dup->FlowCtrlUsed)
  {
    return;
  }

  primCount = dup->RiBu.idx.filled;

  switch (dup->UpFlow)
  {
  case FL_ACTIVE:
    if (primCount < dup->FlowThreshLo)
    {
      dup->UpFlow = FL_INACTIVE;
    }
    else
    {
      return;
    }
    break;

  case FL_INACTIVE:
    if (primCount >= dup->FlowThreshHi)
    {
      dup->UpFlow = FL_ACTIVE;
    }
    else
    {
      return;
    }
    break;
  }
  sig_up_dn_flow(dup->UpFlow);
}

/*
+------------------------------------------------------------------------------
|  Function    : up_store_data
+------------------------------------------------------------------------------
|  Description : This procedure copies data from a dti_data_req primitive 
|                into the uplink ring buffer.
|
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_store_data(void)
{
  T_UP *dup = &l2r_data->up;

  T_P_UBYTE adr;
  USHORT len;
  BOOL primToSend;
  USHORT bytesCopied;
  USHORT i, j;
  T_desc2 *desc;

  TRACE_FUNCTION ("up_store_data()");

  dup->StoreDataActive = TRUE;

  do
  {
    desc = (T_desc2*)dup->Prim->desc_list2.first;
    if (desc)
    {
      len = desc->len;
      adr = desc->buffer;
    }
    else
    {
      len = 0;
      adr = NULL;
    }
    
    up_copy_data_into_l2r (adr,
      len,
      dup->Prim->parameters.st_lines.st_line_sa,
      dup->Prim->parameters.st_lines.st_line_sb,
      dup->Prim->parameters.st_lines.st_flow,
      &primToSend,
      &bytesCopied);

    dup->Prim->desc_list2.list_len  -= bytesCopied;
 
    if (desc)
    {
      if ((bytesCopied EQ len))
      {
        /*
         * Block has been copied successfully
         */
        dup->Prim->desc_list2.first = desc->next;
        MFREE (desc);
      }
      else if (bytesCopied > 0)
      {
       /*
        * remaining data must be copied to begin of block
        */
        /*lint -e{661} (Warning -- Likely creation of out-of-bounds) */        
        for (i=0, j = bytesCopied; j < len; i++, j++)
        {
	  desc->buffer[i] = desc->buffer[j];
	 }
        desc->len -= bytesCopied;
      }
    }
    up_check_alloc();

  } while (dup->Prim->desc_list2.first NEQ 0 AND dup->RiBu.alloc NEQ dup->RiBu.idx.wi);

  if (dup->Prim->desc_list2.first EQ 0)
  {
    PFREE (dup->Prim);
    dup->Prim = NULL;
    up_send_ready();
  }
  dup->StoreDataActive = FALSE;
}

/*
+------------------------------------------------------------------------------
|  Function    : up_send_ready
+------------------------------------------------------------------------------
|  Description : only used in test environment
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_send_ready(void)
{
  TRACE_FUNCTION ("up_send_ready()");

  if (GET_STATE (UP_UL) EQ IW_IDLE)
  {
    if (l2r_data->up.DtiConnected EQ FALSE)
    {
      TRACE_EVENT("DTI not connected, but up_send_ready() called -> break");
      return; /* jk: DTI_READY_IND primitive cannot be sent when DTI disconnected (!) */
    }

    dti_start (
      l2r_hDTI,
      L2R_DTI_UP_DEF_INSTANCE,
      L2R_DTI_UP_INTERFACE,
      L2R_DTI_UP_CHANNEL
      );        
    SET_STATE (UP_UL, IW_WAIT);
  }
}

LOCAL void clear_primDesc(T_P_UPRIM_DESCRIPTOR primDesc)
{
  primDesc->prim        = NULL;

  primDesc->nFr         = 0;
  primDesc->index       = 0;
  primDesc->offset      = 0;
  primDesc->off_status  = 0;
  primDesc->full        = FALSE;
  primDesc->sa          = FL_INACTIVE;
  primDesc->sb          = FL_INACTIVE;
}

/*
+------------------------------------------------------------------------------
|  Function    : up_free_prim
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : primDesc
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

LOCAL void up_free_prim(T_P_UPRIM_DESCRIPTOR primDesc)
{
  TRACE_FUNCTION ("up_free_prim()");

  if (primDesc->prim NEQ NULL)
  {
    PFREE (primDesc->prim);
    clear_primDesc(primDesc);
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_deinit_ribu
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_deinit_ribu(void)
{
  T_UP *dup = &l2r_data->up;

  T_PRIM_DESC_RIBU_INDEX n;

  T_P_UPRIM_DESCRIPTOR primDesc;

  TRACE_FUNCTION ("up_deinit_ribu()");

  for (n = 0; n < dup->RiBu.idx.depth; n++)
  {
    primDesc = dup->RiBu.primDesc[n];
    if (primDesc->prim NEQ NULL)
    {
      up_free_prim(primDesc);
    }
  }
  if (GET_STATE (UP_LL) EQ ISW_SEND)
  {
    SET_STATE (UP_LL, ISW_IDLE);
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_send_status
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : sa   -
|                sb   -
|                flow -
|                adr  -
|
|  Return      : -
+------------------------------------------------------------------------------
*/



GLOBAL void               up_send_status
                          (
                             T_BIT  sa,
                             T_BIT  sb,
                             T_FLOW flow,
                             UBYTE adr
                          )
{
  T_UP *dup = &l2r_data->up;

  T_P_UPRIM_DESCRIPTOR  primDesc;
  T_P_L2R_FRAME    frame;
  T_BIT x = 0;

  TRACE_FUNCTION ("up_send_status()");

  primDesc = dup->RiBu.primDesc[dup->RiBu.idx.wi];
  frame = (*primDesc->adr)[0];

  switch (flow)
  {
  case FL_ACTIVE:
    x = 1;
    break;
  case FL_INACTIVE:
    x = 0;
    break;
  }
  (*frame)[0] = sa << SO_SA_BIT | sb << SO_SB_BIT | x << SO_X_BIT | adr;

  /* jk:030501 - TEST CASE 251 -
  new funktion parameter 'adr' was introduced
  in order to cancel the older function 'up_send_break(...)'
  */
  primDesc->sa = sa; /* the status has to be saved in order to be correctly stored later */
  primDesc->sb = sb; /* on into "dup->LastSentSa/Sb" [up_send current_prim()] */

  primDesc->index = 1;
  primDesc->offset = 0;

  cl_ribu_write_index(&dup->RiBu.idx); /* point to next primitive */
}

/*
+------------------------------------------------------------------------------
|  Function    : up_set_flow_in_prim
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : prim -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

LOCAL void up_set_flow_in_prim(T_P_RLP_DATA_REQ prim)
{
  T_UP *dup = &l2r_data->up;

  if (dup->MrgFlow EQ FL_INACTIVE)
  {
    TRACE_FUNCTION ("up_set_flow_in_prim(INACTIVE)");
  }
  else
  {
    T_PRIM_INDEX frames;
    T_PRIM_INDEX ind;
    UBYTE off;
    register UBYTE statOct;
    T_P_L2R_FRAME frame;

    TRACE_FUNCTION ("up_set_flow_in_prim(ACTIVE)");

    frames = prim->sdu.l_buf / (8 * dup->FrameSize);
    frame = (T_P_L2R_FRAME) (prim->sdu.buf + (prim->sdu.o_buf / 8) + HEADER_LEN);
    off = 0;
    ind = 0;

    while (ind < frames)
    {
      statOct = (*frame)[off];
      SET_BIT(statOct, SO_X_BIT);
      (*frame)[off] = statOct;

      switch (statOct & SO_ADR_MASK)
      {
      case SO_BREAK_REQ:
      case SO_BREAK_ACK:
      case SO_END_EMPTY:
      case SO_END_FULL:
        ind++;
        frame = (T_P_L2R_FRAME)((UBYTE*)frame + dup->FrameSize);
        off = 0;
        break;

      case SO_TWO_OCTET:
        off++;
        off += ((*frame)[off] & SO_ADR_MASK_TWO_OCT) + 1;
        if (off >= dup->DataSize)
        {
          ind++;
          frame = (T_P_L2R_FRAME)((UBYTE*)frame + dup->FrameSize);
          off = 0;
        }
        break;

      default:
        off += (statOct & SO_ADR_MASK) + 1;
        if (off >= dup->DataSize)
        {
          ind++;
          frame = (T_P_L2R_FRAME)((UBYTE*)frame + dup->FrameSize);
          off = 0;
        }
        break;
      }
    }
  }
  dup->LastSentFlow = dup->MrgFlow;
}

/*
+------------------------------------------------------------------------------
|  Function    : up_send_current_prim
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_send_current_prim(void)
{
  T_UP *dup = &l2r_data->up;
  T_P_UPRIM_DESCRIPTOR primDesc = dup->RiBu.primDesc[dup->RiBu.idx.ri];

  TRACE_FUNCTION ("up_send_current_prim()");

  if (!primDesc->full AND primDesc->index < primDesc->nFr)
  {
    TRACE_FUNCTION ("prim is not full");
    if (primDesc->offset > 0)
    {
      if (up_fill_in_status_octet((T_P_UBYTE)(*primDesc->adr)[primDesc->index], primDesc->off_status, primDesc->offset))
      {
        primDesc->offset++;
        if (primDesc->offset >= dup->DataSize)
        {
          primDesc->offset = 0;
          primDesc->index++;
        }
      }
    }

    if (primDesc->offset > 0 OR primDesc->index EQ 0)
    {
      /*
       * Add status octet, if there is a partly filled frame
       * or if status has changed since last write
       */
      (*(*primDesc->adr)[primDesc->index])[primDesc->offset] =
        dup->LastRcvdSa << SO_SA_BIT |
        dup->LastRcvdSb << SO_SB_BIT |
                      0 << SO_X_BIT  |
        SO_END_EMPTY;
      primDesc->sa = dup->LastRcvdSa;
      primDesc->sb = dup->LastRcvdSb;
      primDesc->index++;
    }
    primDesc->prim->sdu.l_buf = primDesc->index * dup->FrameSize * 8;
  }

  dup->LastSentSa = primDesc->sa;
  dup->LastSentSb = primDesc->sb;
  up_set_flow_in_prim(primDesc->prim);

  PSENDX (RLP, primDesc->prim);

  clear_primDesc(primDesc);     /* this primitive doesn't belong to us any longer */

  cl_ribu_read_index(&dup->RiBu.idx); /* point to next primitive */

  dup->UrgentMsg = FALSE;
  up_check_alloc();
  up_check_flow();
}


/*
+------------------------------------------------------------------------------
|  Function    : up_send_prim_cond
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_send_prim_cond(void)
{
  TRACE_FUNCTION ("up_send_prim_cond()");

  switch (GET_STATE (UP_LL))
  {
  case ISW_WAIT:
    TIMERSTOP (TIMER_TUP_SND);
    up_send_current_prim();
    SET_STATE (UP_LL, ISW_IDLE);
    break;

  default:
    l2r_data->up.UrgentMsg = TRUE;
    up_check_flow();
    up_check_alloc();
    SET_STATE (UP_LL, ISW_SEND);
    break;
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_init_ribu
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/


GLOBAL void up_init_ribu(void)
{
  T_UP *dup = &l2r_data->up;

  T_PRIM_DESC_RIBU_INDEX n;
  T_PRIM_INDEX m;

  TRACE_FUNCTION ("up_init_ribu()");

  dup->RiBu.alloc = 0;

  cl_ribu_init(&dup->RiBu.idx, dup->RiBu.idx.depth);

  for (n = 0; n < dup->RiBu.idx.depth; n++)
  {
    dup->RiBu.primDesc[n] = &(dup->PrimDesc[n]);

    dup->PrimDesc[n].nFr = 0;
    dup->PrimDesc[n].adr = (T_P_ADR_VECTOR)&(dup->AdrVec[n]);
    dup->PrimDesc[n].index = 0;
    dup->PrimDesc[n].offset = 0;
    dup->PrimDesc[n].off_status = 0;
    dup->PrimDesc[n].full = FALSE;
    dup->PrimDesc[n].sa   = FL_INACTIVE;
    dup->PrimDesc[n].sb   = FL_INACTIVE;
    dup->PrimDesc[n].prim = NULL;

    for(m = 0;m < L2R_FRAMES_PER_PRIM_MAX; m++)
    {
      dup->AdrVec[n][m] = NULL;
    }

    if (dup->RiBu.alloc < UP_RIBU_PREALLOC)
    {
      up_alloc_prim();
    }
  }
}

/*
+------------------------------------------------------------------------------
|  Function    : up_store_status
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : sa
|                sb
|                flow
|
|  Return      : -
+------------------------------------------------------------------------------
*/


GLOBAL void               up_store_status
                          (
                             T_BIT  sa,
                             T_BIT  sb,
                             T_FLOW flow
                          )
{
  T_UP *dup = &l2r_data->up;

#ifdef _SIMULATION_
  TRACE_FUNCTION ("up_store_status()");
#endif

  dup->LastRcvdSa = sa;
  dup->LastRcvdSb = sb;

  if (dup->FlowCtrlUsed EQ FALSE OR flow EQ dup->ULFlow)
  {
    return;
  }

  dup->ULFlow = flow;

  up_merge_flow();
  sig_up_dn_ul_flow(dup->ULFlow);
}

/*
+------------------------------------------------------------------------------
|  Function    : up_send_empty_frame
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : sa   -
|                sb   -
|                flow -
|
|  Return      : -
+------------------------------------------------------------------------------
*/


GLOBAL void up_send_empty_frame(T_BIT sa, T_BIT sb, T_FLOW flow)
{
  T_UP *dup = &l2r_data->up;

  T_BIT x = 0;
  USHORT sduSize = dup->FrameSize << 3;

  PALLOC_SDU(data_req, RLP_DATA_REQ, sduSize);

  TRACE_FUNCTION ("up_send_empty_frame()");

#ifdef _SIMULATION_
  /* Clear SDU for test environment */
  {
  UBYTE *p;
  UBYTE *pend;
  p = &data_req->sdu.buf[0];
  pend = p + dup->FrameSize;
  while (p < pend)
  {
    *p++ = 0;
  }
  }
#endif

  data_req->sdu.o_buf = 0;
  data_req->sdu.l_buf = sduSize;

  switch (flow)
  {
  case FL_ACTIVE:
    x = 1;
    break;
  case FL_INACTIVE:
    x = 0;
    break;
  }
  /*lint -e416 (Warning -- Likely creation of out-of-bounds pointer) */
  /*lint -e415 (Warning -- Likely access of out-of-bounds pointer) */
  data_req->sdu.buf[L2R_ENCODING_OFFSET] = sa << SO_SA_BIT | sb << SO_SB_BIT | x << SO_X_BIT | SO_END_EMPTY;
  /*lint +e416 (Warning -- Likely creation of out-of-bounds pointer) */
  /*lint +e415 (Warning -- Likely access of out-of-bounds pointer) */  

  dup->LastSentFlow = flow;
  dup->LastSentSa   = sa;
  dup->LastSentSb   = sb;

  PSENDX (RLP, data_req);
}

/*
+------------------------------------------------------------------------------
|  Function    : up_merge_flow
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/


GLOBAL void up_merge_flow(void)
{
  T_UP *dup = &l2r_data->up;

#ifdef _SIMULATION_
  TRACE_FUNCTION ("up_merge_flow()");
#endif

  if (dup->FlowCtrlUsed AND (dup->DnFlow EQ FL_ACTIVE OR dup->ULFlow EQ FL_ACTIVE))
  {
    /* flow control active */
    dup->MrgFlow = FL_ACTIVE;
  }
  else
  {
    /* flow control inactive */
    dup->MrgFlow = FL_INACTIVE;
  }

}

/*
+------------------------------------------------------------------------------
|  Function    : up_rq_init
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : -
+------------------------------------------------------------------------------
*/

GLOBAL void up_rq_init(void)
{
#ifdef _SIMULATION_
  TRACE_FUNCTION ("up_rq_init()");
#endif

  l2r_data->up.QRemapWrite = 0;
  l2r_data->up.QRemapRead  = 0;
}

/*
+------------------------------------------------------------------------------
|  Function    : up_some_data_to_send
+------------------------------------------------------------------------------
|  Description :
|
|  Parameters  : -
|
|
|  Return      : TRUE  -
|                FALSE -
+------------------------------------------------------------------------------
*/

GLOBAL BOOL up_some_data_to_send(void)
{
  T_UP *dup = &l2r_data->up;

#ifdef _SIMULATION_
  TRACE_EVENT ("check if any data to send");
#endif

  if (
       dup->RiBu.idx.filled OR
      (dup->RiBu.primDesc[dup->RiBu.idx.ri]->index > 0) OR
      (dup->RiBu.primDesc[dup->RiBu.idx.ri]->offset > 0) OR
      (dup->LastRcvdSa NEQ dup->LastSentSa) OR
      (dup->LastRcvdSb NEQ dup->LastSentSb)
    )
  {
    return (TRUE);
  }
  return (FALSE);
}