view src/g23m-gprs/llc/llc_irxf.c @ 16:5ba4316fa42c

targets: initial import from Magnetite (pruned)
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 15 Jul 2018 21:40:06 +0000
parents d393cd9bb723
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 modul is part of the entity LLC and implements all 
|             procedures and functions as described in the 
|             SDL-documentation (IRX-statemachine)
+----------------------------------------------------------------------------- 
*/ 

#ifndef LLC_IRXF_C
#define LLC_IRXF_C
#endif

#define ENTITY_LLC

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

#include <string.h>

#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 "llc.h"        /* to get the global entity definitions */

#include "llc_itxs.h"   /* to get ITX signal definitions */
#include "llc_irxf.h"   /* to get IRX local functions */


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

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

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

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



/*
+------------------------------------------------------------------------------
| Function    : irx_init
+------------------------------------------------------------------------------
| Description : This procedure initialises all necessary variables of
|               i_frames_rx for all SAPIs.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_init (void)
{
  UBYTE inc;

  TRACE_FUNCTION( "irx_init" );

  /*
   * Initialise all 4 SAPIs
   */
  SWITCH_SERVICE (llc, irx, 0);
  INIT_STATE (IRX_0, IRX_TLLI_UNASSIGNED);

  SWITCH_SERVICE (llc, irx, 1);
  INIT_STATE (IRX_1, IRX_TLLI_UNASSIGNED);

  SWITCH_SERVICE (llc, irx, 2);
  INIT_STATE (IRX_2, IRX_TLLI_UNASSIGNED);

  SWITCH_SERVICE (llc, irx, 3);
  INIT_STATE (IRX_3, IRX_TLLI_UNASSIGNED);

  /*
   * Initialise the IRX data structure
   */
  for (inc = 0; inc < IRX_NUM_INC; inc++)
  {
    SWITCH_SERVICE (llc, irx, inc);

    /*
     * Free old used resources (in case of an LLC restart):
     * memory, stored primitives, running timer.
     */
    irx_queue_clean ();

    llc_data->irx->ll_send_ready = FALSE;
    llc_data->irx->last_ns = NS_EQUAL_VR;

    llc_data->irx->queue = NULL;
  }
  
  return;
} /* irx_init() */


/*
+------------------------------------------------------------------------------
| Function    : irx_init_sapi
+------------------------------------------------------------------------------
| Description : This procedure initialises all necessary variables of
|               i_frames_rx for the given SAPI.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_init_sapi (void)
{ 
  TRACE_FUNCTION( "irx_init_sapi" );

  llc_data->sapi->va = 0;
  llc_data->sapi->vs = 0;
  llc_data->sapi->vr = 0;

  llc_data->irx->vf      = 0;
  llc_data->irx->last_ns = NS_EQUAL_VR;
  llc_data->irx->B_rx    = 0;

  return;
} /* irx_init_sapi() */


/*
+------------------------------------------------------------------------------
| Function    : irx_init_abm
+------------------------------------------------------------------------------
| Description : This procedure initialises all necessary variables of
|               i_frames_rx for the given SAPI when switching into ABM.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_init_abm (void)
{ 
  TRACE_FUNCTION( "irx_init_abm" );
  
  llc_data->sapi->va = 0;
  llc_data->sapi->vs = 0;
  llc_data->sapi->vr = 0;

  llc_data->irx->vf      = 0;
  llc_data->irx->last_ns = NS_EQUAL_VR;

  /*
   * Reset OCs for acknowledged transfer.
   */
  llc_data->sapi->oc_i_tx = 0L;
  llc_data->sapi->oc_i_rx = 0L;

  return;
} /* irx_init_abm() */



/*
+------------------------------------------------------------------------------
| Function    : irx_queue_store
+------------------------------------------------------------------------------
| Description : This procedure stores the primitive into the per ns sorted 
|               queue. If the queue is full, is_busy set to TRUE will be 
|               returnd.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_queue_store (T_LL_UNITDATA_IND *ll_unitdata_ind,
                             T_FRAME_NUM       ns,
                             BOOL              *is_busy)
{
  T_IRX_QUEUE** entry = &(llc_data->irx->queue);
  T_IRX_QUEUE*  next;
  T_FRAME_NUM   n     = llc_data->irx->vf;
  ULONG         M     = *(llc_data->md) * 16;

  TRACE_FUNCTION( "irx_queue_store" );

  /*
   * First skip already for L3 ready waiting entries
   */
  while (*entry && (*entry)->ns == n)
  {
    n++;
    n %= (MAX_SEQUENCE_NUMBER+1);

    if ((*entry)->ns == ns)
    {
      /*
       * Ignore duplicate frame
       */
      *is_busy = FALSE;

      TRACE_0_INFO( "Unexpected duplicate frame number" );
      PFREE (ll_unitdata_ind);

      return;
    }

    entry = &((*entry)->next);
  }

  /*
   * Now find the inserting position (queue sorted per ns, 
   * 'lower' frame numbers stored first)
   */
  while (*entry)
  {
    if ((*entry)->ns == ns)
    {
      /*
       * Ignore duplicate frame
       */
      *is_busy = FALSE;

      TRACE_0_INFO( "Duplicate frame number received" );
      PFREE (ll_unitdata_ind);

      return;
    }
    
    /*
     * n <= ns <= (*entry)->ns - 1
     */
    if (FRAME_NUM_VALID( n, ns, (*entry)->ns - 1))
    {
      /*
       * Found a nice place in between
       */
      break;
    }

    entry = &((*entry)->next);
  }

  /*
   * Position found - save ptr to next entry
   */
  next = *entry;

  /*
   * Allocate memory
   */
  MALLOC( *entry, sizeof(T_IRX_QUEUE) );

  if( *entry )
  {
    /*
     * Memory successful allocated. Fill in struct entries.
     */
    (*entry)->next   = next;
    (*entry)->ns     = ns;
    (*entry)->frame  = ll_unitdata_ind;

    /*
     * Increase amount of stored Information in rx queue
     */
    llc_data->irx->B_rx += BYTELEN(ll_unitdata_ind->sdu.l_buf);

    /*
     * Determine 'own receiver busy' condition
     */
    if ((M == 0) || (*(llc_data->n201_i) <= M - llc_data->irx->B_rx))
    {
      *is_busy = FALSE;
    }
    else
    {
      *is_busy = TRUE;
    }
  }
  else
  {
    /*
     * Out of memory
     */
    *is_busy = TRUE;
    TRACE_ERROR( "Out of memory in irx_queue_store()" );
    PFREE (ll_unitdata_ind);
  }

} /* irx_queue_store() */


/* 
+------------------------------------------------------------------------------
| Function    : irx_update_vr
+------------------------------------------------------------------------------
| Description : This procedure increments V(R) if the next following frames
|               are already stored in the queue.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_update_vr (void)
{
  T_IRX_QUEUE* entry = llc_data->irx->queue;

  TRACE_FUNCTION( "irx_update_vr" );

  /*
   * Search the queue from the beginning to entry V(R)
   */
  while (entry)
  {
    if (entry->ns == llc_data->sapi->vr)
    {
      /*
       * Increment V(R) and continue searching
       */
      llc_data->sapi->vr++;

      if (llc_data->sapi->vr > MAX_SEQUENCE_NUMBER)
      {
        llc_data->sapi->oc_i_rx += (MAX_SEQUENCE_NUMBER+1);
        llc_data->sapi->vr       = 0;
      }
    }
    
    entry = entry->next;
  }

} /* irx_update_vr() */


/* 
+------------------------------------------------------------------------------
| Function    : irx_get_last_queued_ns
+------------------------------------------------------------------------------
| Description : This procedure returns the frame number of the last queue entry
|
| Parameters  : *found - set to TRUE if ns found, FALSE otherwise
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_get_last_queued_ns (BOOL *found, T_FRAME_NUM *num)
{
  T_IRX_QUEUE* entry = llc_data->irx->queue;

  TRACE_FUNCTION( "irx_get_last_queued_ns" );

  /*
   * Search the queue from the beginning to last entry
   */
  while (entry AND entry->next)
    entry = entry->next;

  if (entry)
  {
    *found = TRUE;
    *num   = entry->ns;
  }
  else
  {
    *found = FALSE;
    *num   = 0;
  } 

} /* irx_get_last_queued_ns() */


/*
+------------------------------------------------------------------------------
| Function    : irx_queue_retrieve
+------------------------------------------------------------------------------
| Description : If the next frame in sequence is stored in the queue then this 
|               procedure will return the primitive pointer, sets found to 
|               TRUE and removes the frame from the queue.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_queue_retrieve (T_LL_UNITDATA_IND **ll_unitdata_ind, BOOL *found)
{
  T_IRX_QUEUE** entry = &(llc_data->irx->queue);

  TRACE_FUNCTION( "irx_queue_retrieve" );

  /*
   * Take the first queue entry, if this is the next one (queue is sorted per ns)
   */
  if ((*entry != NULL) && ((*entry)->ns == llc_data->irx->vf))
  {
    /*
     * Store pointer to the entry
     */
    T_IRX_QUEUE* current = *entry;

    /*
     * Remove entry from the queue (make second to first)
     */
    *entry = current->next;

    *ll_unitdata_ind = current->frame;
  
    /*
     * Decrease amount of stored Information in rx queue
     */
    if (llc_data->irx->B_rx >= (ULONG)(BYTELEN((*ll_unitdata_ind)->sdu.l_buf)))
    {
      llc_data->irx->B_rx -= BYTELEN((*ll_unitdata_ind)->sdu.l_buf);
    }
    else
    {
      llc_data->irx->B_rx = 0;
      TRACE_0_INFO("Unexpected SDU lenght handled");
    }

    /*
     * Free retrieved entry
     */
    MFREE (current);

    *found = TRUE;

    /*
     * Increment V(f) (= Next frame number to forward to L3)
     */
    llc_data->irx->vf += 1;
    llc_data->irx->vf %= (MAX_SEQUENCE_NUMBER+1);

    return;
  }

  /*
   * Set default return values
   */
  *ll_unitdata_ind = NULL;
  *found           = FALSE;
}



/*
+------------------------------------------------------------------------------
| Function    : irx_queue_clean 
+------------------------------------------------------------------------------
| Description : This procedure removes all entries from the IRX queue
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_queue_clean (void)
{
  T_IRX_QUEUE** entry = &(llc_data->irx->queue);
  
  TRACE_FUNCTION( "irx_queue_clean" );

  while (*entry)
  {
    /*
     * get pointer to next (=first) entry
     */
    T_IRX_QUEUE* current = *entry;

    /*
     * Free frame, if one is attached to the entry
     */
    if (current->frame != NULL)
    {
      PFREE (current->frame);
    }

    /*
     * remove next entry from the entry (make second to first)
     */
    *entry = current->next;
    
    /*
     * free the removed entry
     */
    MFREE (current);
  }

  /*
   * Adjust amount of stored Information in rx queue
   */
  llc_data->irx->B_rx = 0;

} /* irx_queue_clean() */



/*
+------------------------------------------------------------------------------
| Function    : irx_build_sack_bitmap
+------------------------------------------------------------------------------
| Description : This procedure builds a SACK bitmap, depending on current
|               status
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_build_sack_bitmap ( T_SACK_BITMAP *bitmap )
{
  T_IRX_QUEUE* entry    = llc_data->irx->queue;
  int          start_ns = (llc_data->sapi->vr + 1) % (MAX_SEQUENCE_NUMBER+1);
  T_FRAME_NUM  num      = llc_data->irx->vf;
  USHORT       d;


  TRACE_FUNCTION( "irx_build_sack_bitmap" );

  /*
   * Init bitmap to 0 (= frame not successfully received)
   */  
  memset (bitmap, 0, sizeof(T_SACK_BITMAP));

  /*
   * Skip already for L3 ready waiting entries
   */
  while (entry && entry->ns == num)
  {
    num++;
    num %= (MAX_SEQUENCE_NUMBER+1);

    entry = entry->next;
  }


  /*
   * Check the queue for successful received entries
   */
  while (entry)
  {
    /*
     * If frame is successful received, mark it in bitmap
     */
    d = FRAME_NUM_DISTANCE(start_ns, entry->ns);

    if (d < (S_FRAME_SACK_MAX_CTRL_OCTETS * 8))
    {
      bitmap->data[d>>3] |= 0x80>>(d%8);
    }

    entry = entry->next;
  }
} /* irx_build_sack_bitmap () */


/*
+------------------------------------------------------------------------------
| Function    : irx_ack_all_to
+------------------------------------------------------------------------------
| Description : This procedure handles the acknowledgement of transmitted I- 
|               frames from va to n (which is equal to nr -1)
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_ack_all_to (T_FRAME_NUM n)
{
  TRACE_FUNCTION( "irx_ack_all_to" );

  n %= (MAX_SEQUENCE_NUMBER+1);
   
  if (FRAME_WIN_VALID(n, llc_data->sapi->va, *(llc_data->ku)))
  {
    while (llc_data->sapi->va != (n+1)%(MAX_SEQUENCE_NUMBER+1))
    {
      sig_irx_itx_ack_ind (TRUE, llc_data->sapi->va);

      llc_data->sapi->va++;
      llc_data->sapi->va %= (MAX_SEQUENCE_NUMBER+1);
    } 

    /*
     * Sending L3 Data confirmations
     */
    sig_irx_itx_cnf_l3data_req ();
  }

} /* irx_ack_all_to () */


/*
+------------------------------------------------------------------------------
| Function    : irx_handle_sack
+------------------------------------------------------------------------------
| Description : This procedure handles the acknowledge bits of a SACK 
|               supervisory frame and strips the SACK bitmap from the SDU
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_handle_sack (T_FRAME_NUM       nr, 
                             T_LL_UNITDATA_IND *ll_unitdata_ind,
                             T_PDU_TYPE        frame_type)
{
  int            i, n, bytes;
  T_FRAME_NUM    rn = 0;
  T_SACK_BITMAP  *bm;
  
  TRACE_FUNCTION( "irx_handle_sack" );

  /*
   * Extract SACK bitmap and number of Ptr to bits from sdu:
   */
  if (frame_type == I_FRAME)
  {
    bm    = (T_SACK_BITMAP*)&(ll_unitdata_ind->sdu.buf[ 
               (ll_unitdata_ind->sdu.o_buf >> 3) + 1]);
    bytes = ll_unitdata_ind->sdu.buf[ 
               (ll_unitdata_ind->sdu.o_buf >> 3)] & 0xF1;
  }
  else
  {
    bm    = (T_SACK_BITMAP*)&(ll_unitdata_ind->sdu.buf[ 
               (ll_unitdata_ind->sdu.o_buf >> 3)]);
    bytes = ll_unitdata_ind->sdu.l_buf >> 3;
  }

  /*
   * Little Protection
   */
  if ( bytes > S_FRAME_SACK_MAX_CTRL_OCTETS )
  {
    bytes = S_FRAME_SACK_MAX_CTRL_OCTETS;
    TRACE_ERROR ("Illegal SACK bitmap size received");
  }

  /*
   * For each bit, which is set, send an acknowledge to ITX
   */
  for (i = 0; i < bytes; i++)
  {
    for (n = 7; n >= 0; n--)
    {
      /*
       * first increment rn (must begin with 1)
       */
      rn++;

      /*
       * check if bit is set from MSB to LSB
       */
      if ( bm->data[i] & (1 << n) )
      {
        /*
         * set frame acknowledged
         */
        sig_irx_itx_ack_ind ( TRUE, (T_FRAME_NUM)(nr + rn));
      }
      else
      {
        /*
         * mark frame for retransmission
         */
        sig_irx_itx_ack_ind ( FALSE, (T_FRAME_NUM)(nr + rn));
      }
    }
  }

  /*
   * send L3 data confirmation
   */
  sig_irx_itx_cnf_l3data_req ();

  /*
   * Strip SACK bitmap from I frame SDUs to get clean L3 PDU
   */
  if (frame_type == I_FRAME)
  {
    ll_unitdata_ind->sdu.o_buf += bytes << 3;
    ll_unitdata_ind->sdu.l_buf -= bytes << 3;
  }

} /* irx_handle_sack () */


/*
+------------------------------------------------------------------------------
| Function    : irx_send_ack
+------------------------------------------------------------------------------
| Description : This procedure handles the detection of sequence errors and
|               the sending of appropriate acknowledgements
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_send_ack (T_BIT a_bit)
{
  TRACE_FUNCTION( "irx_send_ack" );

  /*
   * Send S frame depending on last N(S) / V(R) condition
   */
  switch (llc_data->irx->last_ns)
  {
    case NS_EQUAL_VR:
      /*
       * Send an RR only, if requested
       */
      if (a_bit)
      {
        sig_irx_itx_send_rr_req (ABIT_NO_REQ);
      }
      else
      {
        /*
         * Trigger ITX to check queue and send frames
         * This is done inside the other signals, too.
         */
        sig_irx_itx_trigger_ind ();
      }
      break;
    
    case NS_EQUAL_VR_PLUS_1:
      /*
       * Sequence error - one missing
       */
      sig_irx_itx_send_ack_req (ABIT_NO_REQ);
      break;
    
    case NS_NO_SEQUENCE_ERROR:
      /*
       * Send an SACK only, if requested
       */
      if (a_bit)
      {
        T_SACK_BITMAP bitmap;

        irx_build_sack_bitmap( &bitmap );
        sig_irx_itx_send_sack_req (ABIT_NO_REQ, &bitmap);
      }
      else
      {
        /*
         * Trigger ITX to check queue and send frames
         * This is done inside the other signals, too.
         */
        sig_irx_itx_trigger_ind ();
      }
      break;

    default:
      /*
       * Sequence error - one or more missing
       */
      {
        T_SACK_BITMAP bitmap;

        irx_build_sack_bitmap( &bitmap );
        sig_irx_itx_send_sack_req (ABIT_NO_REQ, &bitmap);
      }
      break;
  }

} /* irx_send_ack () */


/*
+------------------------------------------------------------------------------
| Function    : irx_send_rnr
+------------------------------------------------------------------------------
| Description : This procedure sends an RNR to ITX
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void irx_send_rnr ()
{
  TRACE_FUNCTION( "irx_send_rnr" );

  sig_irx_itx_send_rnr_req (ABIT_NO_REQ);

} /* irx_send_rnr() */