view src/g23m-gprs/llc/llc_irxf.c @ 639:026c98f757a6

tpudrv12.h & targets/gtm900.h: our current support is for MGC2GSMT version only As it turns out, there exist two different Huawei-made hw platforms both bearing the marketing name GTM900-B: one is MG01GSMT, the other is MGC2GSMT. The two are NOT fw-compatible: aside from flash chip differences which should be handled by autodetection, the two hw platforms are already known to have different RFFEs with different control signals, and there may be other differences not yet known. Our current gtm900 build target is for MGC2GSMT only; we do not yet have a specimen of MG01GSMT on hand, hence no support for that version will be possible until and unless someone provides one.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 30 Jan 2020 18:19:01 +0000
parents 219afcfc6250
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() */