view src/g23m-gprs/llc/llc_rxf.c @ 268:f2e52cab0a73

abb_inth.c: check all interrupt causes, not just one The original code used if - else if - else if etc constructs, thus the first detected interrupt was the only one handled. However, Iota ITSTATREG is a clear-on-read register, thus if we only handle the first detected interrupt and skip checking the others, then the other interrupts will be lost, if more than one interrupt happened to occur in one ABB interrupt handling cycle - a form of rare race condition. Change the code to check all interrupts that were read in this cycle.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 13 Jun 2021 18:17:53 +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 modul is part of the entity LLC and implements all 
|             procedures and functions as described in the 
|             SDL-documentation (RX-statemachine)
+----------------------------------------------------------------------------- 
*/ 

#ifndef LLC_RXF_C
#define LLC_RXF_C
#endif

#define ENTITY_LLC

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

#include <string.h>     /* to get memcpy() */

#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_rxf.h"    /* to get local defines */
#include "llc_rxp.h"    /* to get the function rx_cci_decipher_cnf */
#include "llc_f.h"      /* to get global functions, e.g. llc_generate_input */
#ifndef TI_PS_OP_CIPH_DRIVER 
#include "cci_fbsf.h"     /* to get functional interface */
#endif
/*==== CONST ================================================================*/

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

/*==== PRIVATE FUNCTIONS ====================================================*/
#ifndef CF_FAST_EXEC

GLOBAL UBYTE rx_get_desc_octet (T_desc_list *desc_list, 
                               USHORT offset, 
                               UBYTE *data_ptr);
#endif /* CF_FAST_EXEC */

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


/*
+------------------------------------------------------------------------------
| Function    : rx_init
+------------------------------------------------------------------------------
| Description : This procedure initialises all necessary variables of 
|               receive_pdu.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
#ifndef CF_FAST_EXEC

GLOBAL void rx_init (void)
{ 
  TRACE_FUNCTION( "rx_init" );
  
  /*
   * Initialise service RX with state TLLI_UNASSIGNED.
   */
  INIT_STATE (RX, RX_TLLI_UNASSIGNED);

  return;
} /* rx_init() */

#endif /* CF_FAST_EXEC */


/*
+------------------------------------------------------------------------------
| Function    : rx_analyse_ctrl_field
+------------------------------------------------------------------------------
| Description : This procedure analyses the received LLC frame control field 
|               and sets frame_type according to the frame type (U, UI, I/S). 
|               protected_mode is set according to the protected mode of the 
|               frame (U and I/S always protected, UI depends on setting of 
|               PM bit). The type of protected_mode is the same as in 
|               CCI_DECIPHER_REQ. ns is set to N(S) for I frames, N(U) for 
|               UI frames, or an undefined value for S and U frames. ciphering 
|               is set to TRUE if the frame is ciphered (U never, I/S always, 
|               UI depends on setting of E bit), otherwise to FALSE. frame_ok 
|               indicates if the frame contains enough octets to contain a 
|               known control field (i.e. all necessary information is 
|               accessible, not the control field is complete!).
|
| Parameters  : grlc_unitdata_ind  - a valid pointer to a GRLC_UNITDATA_IND 
|                                   primitive
|               frame_type        - a valid pointer to a T_PDU_TYPE variable
|               protected_mode    - a valid pointer to a UBYTE variable
|               sapi              - a valid pointer to a UBYTE variable
|               ns                - a valid pointer to a T_FRAME_NUM variable
|               ciphering         - a valid pointer to a BOOL variable
|               header_size       - a valid pointer to a USHORT variable
|               frame_ok          - a valid pointer to a BOOL variable
|
+------------------------------------------------------------------------------
*/

#ifndef CF_FAST_EXEC

GLOBAL void rx_analyse_ctrl_field (T_GRLC_UNITDATA_IND *grlc_unitdata_ind,
                                   T_PDU_TYPE         *frame_type,
                                   UBYTE              *protected_mode,
                                   UBYTE              *sapi,
                                   T_FRAME_NUM        *ns,
                                   BOOL               *ciphering,
                                   USHORT             *header_size,
                                   BOOL               *frame_ok)
{
  UBYTE             first_octet;
  UBYTE             sec_octet;
  UBYTE             command_octet;
  UBYTE             sack_k_octet;


  TRACE_FUNCTION( "rx_analyse_ctrl_field" );

  /*
   * Check if the frame contains enough octets to access the first octet of
   * the control field to find out the type of the frame.
   */
  if (grlc_unitdata_ind->desc_list.list_len < CTRL_MIN_OCTETS)
  {
    *frame_ok = FALSE;
    return;
  }

  /*
   * Assume initially that the frame is ok.
   */
  *frame_ok = TRUE;
  
  /*
   * Set first_octet to the value of the first frame octet (offset 0), which 
   * is the address field.
   */
  rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 0, &first_octet);

  *sapi = first_octet & 0x0F;

  /*
   * Set sec_octet to the value of the second frame octet (offset 1), which 
   * is the first octet of the control field.
   */
  rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 1, &sec_octet);


  /*
   * Determine frame_type, along with ns, protected_mode, and ciphering,
   * depending on the frame type.
   */
  if ((sec_octet & I_FRAME_MASK) EQ I_FRAME_ID)
  {
    /*
     * I frame, protected, ciphered, at least 4 octets required to access all
     * requested information (1 Address, 3 Control).
     */
    *frame_type     = I_FRAME;
    *protected_mode = CCI_PM_PROTECTED;
    *ciphering      = TRUE;

    /*
     * Check if the frame contains enough octets to access the complete
     * I frame control field.
     */
    if (grlc_unitdata_ind->desc_list.list_len < I_CTRL_MIN_OCTETS)
    {
      *frame_ok = FALSE;
      return;
    }

    /*
     * Determine the header_size
     */
    *header_size = I_CTRL_MIN_OCTETS;

    /*
     * Add bytes in case of SACK-Bitmap (add K+1). Therefore get at first 
     * the command octet (offset 3). In case of an SACK, get next the k 
     * octet (offset 4) and add the additional size.
     */
    rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 3, &command_octet);
    
    if ( (command_octet & 0x03) == I_FRAME_SACK )
    {
      *header_size += 1; /* k octet */

      if (grlc_unitdata_ind->desc_list.list_len < *header_size)
      {
        *frame_ok = FALSE;
        return;
      }

      rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 4, &sack_k_octet);

      *header_size += (sack_k_octet & 0x1F); /* bitmap size */

      if (grlc_unitdata_ind->desc_list.list_len < *header_size)
      {
        *frame_ok = FALSE;
        return;
      }
    }

    /*
     * Extract N(S) and store it in ns.
     */
    *ns = (((T_FRAME_NUM)
      rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 1, NULL)) & 0x1F) << 4;
    *ns |= (((T_FRAME_NUM)
      rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL)) >> 4);
  }
  else if ((sec_octet & S_FRAME_MASK) EQ S_FRAME_ID)
  {
    /*
     * S frame, protected, not ciphered. No N(S) present, only N(R).
     */
    *frame_type     = S_FRAME;
    *protected_mode = CCI_PM_PROTECTED;
    *ciphering      = FALSE;

    *header_size    = S_CTRL_MIN_OCTETS;
    
    /* not necessary to add bytes in case of SACK - value is not used */
  }
  else if ((sec_octet & UI_FRAME_MASK) EQ UI_FRAME_ID)
  {
    /*
     * UI frame, at least 3 octets required to access all requested 
     * information (1 Address, 2 Control).
     */
    *frame_type     = UI_FRAME;

    /*
     * Check if the frame contains enough octets to access the complete
     * UI frame control field.
     */
    if (grlc_unitdata_ind->desc_list.list_len < UI_CTRL_MIN_OCTETS)
    {
      *frame_ok = FALSE;
      return;
    }

    /*
     * Extract protected mode setting of frame (PM bit).
     */
    if (rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL) & 0x01)
    {
      *protected_mode = CCI_PM_PROTECTED;
    }
    else
    {
      *protected_mode = CCI_PM_UNPROTECTED;
    }

    /*
     * Extract ciphering setting of frame (E bit).
     */
    if (rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL) & 0x02)
    {
      *ciphering = TRUE;
    }
    else
    {
      *ciphering = FALSE;
    }

    *header_size = UI_CTRL_MIN_OCTETS;

    /*
     * Extract N(U) and store it in ns.
     */
    *ns = (((T_FRAME_NUM)
      rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 1, NULL)) & 0x07) << 6;
    *ns |= (((T_FRAME_NUM)
      rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL)) >> 2);
  }
  else if ((sec_octet & U_FRAME_MASK) EQ U_FRAME_ID)
  {
    /*
     * U frame, protected, not ciphered. No N(S) present.
     */
    *frame_type     = U_FRAME;
    *protected_mode = CCI_PM_PROTECTED;
    *ciphering      = FALSE;

    *header_size    = U_CTRL_MIN_OCTETS;
  }

  return;
} /* rx_analyse_ctrl_field() */

#endif /* CF_FAST_EXEC */

/*
+------------------------------------------------------------------------------
| Function    : rx_decipher_req
+------------------------------------------------------------------------------
| Description : Handles the function rx_decipher_req. This functions sets the
|               ciphering parameters and calls the deciphering driver function.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/

#ifndef CF_FAST_EXEC
GLOBAL void rx_decipher_req (T_CCI_DECIPHER_REQ *decipher_req)
{ 
  T_CIPH_init_cipher_req_parms  init_cipher_req_parms;
  T_CIPH_cipher_req_parms       cipher_req_parms;
  T_CIPH_in_data_list           in_data_list;
  T_CIPH_out_data               out_data;
  T_CIPH_ck                     ck;
  U16                           i;
  U8                            status;
  U16                           cnt = 0;

  TRACE_FUNCTION( "rx_decipher_req" );

#ifdef LLC_TRACE_CIPHERING
  TRACE_EVENT("DOWNLINK CIPHERED DATA");
  llc_trace_desc_list(&decipher_req->desc_list);
#endif

  /*
   * Copy pointer to desc's from CIPHER_REQ to the in_data_list
   * The in_data array in allocated dynamically in this func.
   */
  llc_copy_dl_data_to_list(decipher_req, &in_data_list);
  /*
   * Set ciphering parameters 
   */
  cipher_req_parms.gprs_parameters.pm = decipher_req->pm;
  cipher_req_parms.gprs_parameters.header_size = decipher_req->header_size;
  cipher_req_parms.gprs_parameters.ciphering_input = 
                                             decipher_req->ciphering_input;
  cipher_req_parms.gprs_parameters.threshold = 0;
  init_cipher_req_parms.direction = CIPH_DOWNLINK_DIR;
  init_cipher_req_parms.algo      = decipher_req->ciphering_algorithm;
  init_cipher_req_parms.ptr_ck = & ck;
  /*
   * Copy ciphering key
   */
  for (i = 0; i < 8;i++) {
    init_cipher_req_parms.ptr_ck->ck_element[i] = decipher_req->kc.key[i];
  }

  {
    /* Use GRLC_DATA_REQ instead of CCI_CIPHER_CNF to avoid PPASS in LLC*/
    PALLOC_SDU (ll_unitdata_ind, LL_UNITDATA_IND,
           (USHORT)((decipher_req->desc_list.list_len*8) - FCS_SIZE_BITS));
  
    ll_unitdata_ind->sdu.o_buf = 0;
    ll_unitdata_ind->sdu.l_buf = 0;
    out_data.buf = 
         (U32)(&ll_unitdata_ind->sdu.buf[ll_unitdata_ind->sdu.o_buf >> 3]);
    /*
     * Initialize ciphering driver and decipher data
     */
#ifdef TI_PS_OP_CIPH_DRIVER
    ciph_init_cipher_req (&init_cipher_req_parms, NULL);
    ciph_cipher_req (&cipher_req_parms, &in_data_list, &out_data, &status);
#else
    ciph_init_cipher_req_sim (&init_cipher_req_parms, NULL);
    ciph_cipher_req_sim (&cipher_req_parms, &in_data_list, &out_data, &status);
#endif
    /*
     * "Send" DECIPHER_CNF to LLC
     */
    ll_unitdata_ind->sdu.l_buf  = out_data.len * 8;
    ll_unitdata_ind->tlli       = decipher_req->reference1;
    ll_unitdata_ind->cipher     = (UBYTE)decipher_req->reference2;
    if (status == CIPH_CIPH_PASS){
      ll_unitdata_ind->sapi = CCI_FCS_PASSED;
    } else {
      ll_unitdata_ind->sapi = CCI_FCS_FAILED;
    }

#ifdef LLC_TRACE_CIPHERING
    TRACE_EVENT("DOWNLINK DECIPHERED DATA");
    llc_trace_sdu(&ll_unitdata_ind->sdu);
#endif
      rx_cci_decipher_cnf(ll_unitdata_ind);
  }
  /*
   * Free in_data array from in_data_list allocated in llc_copy_dl_data_to_list
   */
  if(in_data_list.ptr_in_data != NULL){
    MFREE(in_data_list.ptr_in_data);
    in_data_list.ptr_in_data = NULL;
  }
  /*
   * Free descriptors from the deipher_req->desc_list
   */
  if (decipher_req != NULL)
  {
    T_desc *desc = (T_desc *)decipher_req->desc_list.first;
    T_desc *next_desc;
    while (desc NEQ NULL){
      next_desc = (T_desc *)desc->next;
      MFREE (desc);
      desc = next_desc;
      /* increase freed partitions counter */
      cnt++;
    }       
    MFREE(decipher_req);
    decipher_req = NULL;
  }
  llc_data->fbs.cci_freed_partition +=cnt;

  /* trace number of freed partitions */
  if(llc_data->fbs.cci_info_trace){
    TRACE_EVENT_P2("INFO CCI: freed partitions %ld, total=%ld",
                        cnt,llc_data->fbs.cci_freed_partition);
  }

} /* rx_decipher_req */

#endif

/*
+------------------------------------------------------------------------------
| Function    : rx_send_decipher_req
+------------------------------------------------------------------------------
| Description : This procedure allocates the CCI_DECIPHER_REQ primitive, fills
|               in all necessary parameters and sends the primitive to CCI.
|
| Parameters  : grlc_unitdata_ind  - a valid pointer to a GRLC_UNITDATA_IND 
|                                   primitive
|               frame_type        - indicates (un)acknowledged operation mode
|               protected_mode    - pm setting for CCI_DECIPHER_REQ
|               ns                - N(S)/N(U) for deciphering, otherwise
|                                   undefined value
|               header_size       - size of header bytes (not ciphered bytes)
|               ciphering         - indicates deciphering (TRUE/FALSE)
|
+------------------------------------------------------------------------------
*/
#ifndef CF_FAST_EXEC

GLOBAL void rx_send_decipher_req (T_GRLC_UNITDATA_IND *grlc_unitdata_ind,
                                  T_PDU_TYPE  frame_type,
                                  UBYTE       protected_mode,
                                  T_FRAME_NUM ns,
                                  USHORT      header_size,
                                  BOOL        ciphering)
{
  ULONG oc;
  
  TRACE_FUNCTION ("rx_send_decipher_req");

  {
    /*
     * No need to PPASS GRLC_xDATA_IND, because desc_list contains a pointer 
     * which can be simply copied.
     */
    T_CCI_DECIPHER_REQ *cci_decipher_req;
    MALLOC(cci_decipher_req, sizeof(T_CCI_DECIPHER_REQ));

    /*
     * Requires rx_analyse_ctrl_field() to set a correct CCI value.
     */
    cci_decipher_req->pm = protected_mode;

    if (ciphering EQ TRUE)
    {
      cci_decipher_req->reference2 = LL_CIPHER_ON; /* re-use of reference is ok */
      cci_decipher_req->ciphering_algorithm = llc_data->ciphering_algorithm;
      memcpy (&cci_decipher_req->kc, &llc_data->kc, sizeof(T_kc));

      /*
       * Calculate the OC which is valid for this ns. This could be the
       * current OC or OC + 1 in case of an modulo overflow of ns
       */
      switch (frame_type)
      {
        case I_FRAME:
        case S_FRAME:
          if (ns >= llc_data->sapi->vr)
            oc = llc_data->sapi->oc_i_rx;
          else
            oc = llc_data->sapi->oc_i_rx + (MAX_SEQUENCE_NUMBER+1);
          break;

        default:
          if (ns >= llc_data->sapi->vur)
            oc = llc_data->sapi->oc_ui_rx;
          else
            oc = llc_data->sapi->oc_ui_rx + (MAX_SEQUENCE_NUMBER+1);
          break;
      }

      llc_generate_input (llc_data->current_sapi, frame_type, ns, 
        &cci_decipher_req->ciphering_input, oc);

      cci_decipher_req->direction = CCI_DIRECTION_DOWNLINK;
    }
    else /* ciphering EQ FALSE */
    {
      cci_decipher_req->reference2 = LL_CIPHER_OFF;
      cci_decipher_req->ciphering_algorithm = CCI_CIPHER_NO_ALGORITHM;
    }

    cci_decipher_req->header_size = header_size;

    /*
     * TLLI must be stored somewhere
     */
    cci_decipher_req->reference1 = grlc_unitdata_ind->tlli;

    cci_decipher_req->desc_list = grlc_unitdata_ind->desc_list;

    /* TRACE_EVENT ("CCI thread not available, using functional interface"); */

    rx_decipher_req(cci_decipher_req);
  }

  return;
} /* rx_send_decipher_req() */

#endif /* CF_FAST_EXEC */

/*
+------------------------------------------------------------------------------
| Function    : rx_interpret_frame
+------------------------------------------------------------------------------
| Description : This procedure analyses the LLC header and checksum of the 
|               given frame (U, UI, or I) and sets sapi, pdu_type, command, 
|               cr_bit, pf_bit, nr, and ns accordingly. The FCS field is not 
|               included in frame, it has already been stripped off. frame_ok 
|               is set to TRUE if the frame fulfils the following requirements:
|               known PDU type, valid length, valid PD bit, valid SAPI. 
|               frame_rej indicates a frame rejection condition if any bit of 
|               W4-W1 is set. If a frame is rejected, frame_rej_ctrl_length 
|               indicates the length of the control field. If this length 
|               could not be determined, frame_rej_ctrl_length is set to the 
|               number of control field octets of the frame.
|
| Parameters  : frame     - contains the frame to be analysed, must be 
|                           a valid pointer
|               sapi      - will be set to the SAPI of the frame, must be 
|                           a valid pointer
|               pdu_type  - will be set to the PDU type of the frame, must be 
|                           a valid pointer
|               command   - will be set to the command (I/S, U) of the frame 
|                           (if available), must be a valid pointer
|               cr_bit    - will be set to the C/R bit of the frame, must be 
|                           a valid pointer
|               pf_bit    - will be set to the P/F bit of the frame (if 
|                           available), must be a valid pointer
|               nr        - will be set to N(R) / N(U) of the frame (if 
|                           available), must be a valid pointer
|               ns        - will be set to N(S) of the frame (if available), 
|                           must be a valid pointer
|               frame_ok  - TRUE if the frame is ok, else FALSE, must be 
|                           a valid pointer
|               frame_rej - indicates a frame rejection condition, must be 
|                           a valid pointer
|               frame_rej_ctrl_length - number of octets in the rejected 
|                           control field, must be a valid pointer
|
+------------------------------------------------------------------------------
*/

/*#if defined(CF_FAST_EXEC) || defined(_SIMULATION_) || \
   !defined(REL99)     || defined(LL_2to1) */

GLOBAL void rx_interpret_frame (T_sdu *frame,
                                UBYTE *sapi, 
                                T_PDU_TYPE *pdu_type, 
                                T_COMMAND *command, 
                                T_BIT *cr_bit, 
                                T_BIT *pf_bit, 
                                T_FRAME_NUM *nr, 
                                T_FRAME_NUM *ns, 
                                BOOL *frame_ok, 
                                UBYTE *frame_rej,
                                USHORT *frame_rej_ctrl_length,
                                UBYTE  cipher)
{
  USHORT            min_length = 0;     /* minimum required frame length  */
  BOOL              check_length;       /* check/ignore length at the end */

  TRACE_FUNCTION( "rx_interpret_frame" );

  /*
   * Preset variables with suspected success, control field length is not
   * yet known, so assume complete frame length.
   */
  *frame_ok               = TRUE;
  *frame_rej              = FRAME_NOT_REJ;
  *frame_rej_ctrl_length  = frame->l_buf/8 - 1;

  /*
   * Frame length is selectively activated, depending on frame type, command,
   * etc. Ignore frame length per default.
   */
  check_length            = FALSE;


  /*
   * Check if the frame contains enough octets to access the first octet of
   * the control field to find out the type of the frame.
   */
  if (frame->l_buf/8 < CTRL_MIN_OCTETS)
  {
    *frame_ok = FALSE;
    return;
  }


  /*
   * Check if PD bit is set to 0. If it is not, the frame is invalid.
   * <R.LLC.XCEPTION.A.001>
   */
  if ((frame->buf[frame->o_buf/8] & 0x80) != 0x00)
  {
    *frame_ok = FALSE;
    return;
  }


  /*
   * Extract C/R bit.
   */
  *cr_bit = (frame->buf[frame->o_buf/8] & 0x40) >> 6;


  /*
   * Extract SAPI and check if the frame contains a reserved SAPI.
   * <R.LLC.XCEPTION.A.001>
   */
  *sapi = frame->buf[frame->o_buf/8] & 0x0F;
  switch (*sapi) /* !!!!! constants or function/macro to determine invalid sapis */
  {
    case 0:
    case 2:
    case 4:
    case 6:
    case 8:
    case 10:
    case 12:
    case 13:
    case 14:
    case 15:
    {
      *frame_ok = FALSE;
      return;
    }
  }


  /*
   * Determine the PDU type of the frame, along with the minimum length
   * required for this PDU type to constitute a complete frame. 
   * Additionally, extract available variables for each PDU type.
   */
  if ((frame->buf[(frame->o_buf/8)+1] & I_FRAME_MASK) EQ I_FRAME_ID)
  {
    /*
     * I frame, at least 5 octets (1 Address, 3 Control, 1(++) 
     * Information, no FCS)
     */
    *pdu_type               = I_FRAME;
    min_length              = I_FRAME_MIN_OCTETS_WITHOUT_FCS;
    *frame_rej_ctrl_length  = I_CTRL_OCTETS;

    /*
     * Extract A bit (stored in P/F bit)
     */
    *pf_bit = (frame->buf[(frame->o_buf/8)+1] & 0x40) >> 6;

    /*
     * Check if the frame contains enough octets to access the complete
     * I frame control field.
     */
    if (frame->l_buf/8 < I_CTRL_MIN_OCTETS)
    {
      *frame_ok = FALSE;
      return;
    }

    /*
     * Extract N(S), N(R)
     */
    *ns = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+1] & 0x1F) << 4;
    *ns |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] >> 4);

    *nr = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] & 0x07) << 6;
    *nr |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+3] >> 2);

    /*
     * Determine the command of the I frame (S1 & S2 bits)
     */
    switch (frame->buf[(frame->o_buf/8)+3] & 0x03)
    {
      case I_FRAME_RR:
        TRACE_4_PARA("I-RR s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns);
        *command = I_RR;
        break;

      case I_FRAME_ACK:
        TRACE_4_PARA("I-ACK s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns);
        *command = I_ACK;
        break;

      case I_FRAME_RNR:
        TRACE_4_PARA("I-RNR s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns);
        *command = I_RNR;
        break;

      case I_FRAME_SACK:
        TRACE_4_PARA("I-SACK s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns);
        *command = I_SACK;

        /*
         * Check if the frame contains enough octets to access the complete
         * I frame SACK control field. K is at octet I_CTRL_MIN_OCTETS + 1
         * and there must be at least K+1 octets within the bitmap. Therefore
         * the frame must contain a minimum of I_CTRL_MIN_OCTETS+2.
         */
        if (frame->l_buf/8 < I_CTRL_MIN_OCTETS+2)
        {
          *frame_ok = FALSE;
          return;
        }

        /*
         * min_length is being modified to be more accurate for SACK frames,
         * according to the Bitmap Length Indicator K in 
         * frame->buf[(frame->o_buf/8)+4].
         */
        min_length += (frame->buf[(frame->o_buf/8)+4] & 0x1F) + 1;

        check_length = TRUE;
        break;

      default:
        /*
         * Frame rejection condition due to receipt of a command or
         * response field that is undefined or not implemented (set
         * W3 bit to 1).
         * <R.LLC.XCEPTION.A.002>
         */
        *frame_ok = FALSE;
        *frame_rej = FRAME_REJ_W3;
        return;
    }
  }
  else if ((frame->buf[(frame->o_buf/8)+1] & S_FRAME_MASK) EQ S_FRAME_ID)
  {
    /*
     * S frame, fixed 3 octets (1 Address, 2 Control, 0 Information, no FCS)
     */
    *pdu_type               = S_FRAME;
    min_length              = S_FRAME_MIN_OCTETS_WITHOUT_FCS;
    *frame_rej_ctrl_length  = S_CTRL_OCTETS;

    /*
     * Extract A bit (stored in P/F bit)
     */
    *pf_bit = (frame->buf[(frame->o_buf/8)+1] & 0x20) >> 5;

    /*
     * Check if the frame contains enough octets to access the complete
     * S frame control field.
     */
    if (frame->l_buf/8 < S_CTRL_MIN_OCTETS)
    {
      *frame_ok               = FALSE;
      *frame_rej              = FRAME_REJ_W1;
      *frame_rej_ctrl_length  = frame->l_buf/8 - 1;
      return;
    }

    /*
     * Extract N(R)
     */
    *nr = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+1] & 0x07) << 6;
    *nr |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] >> 2);

    /*
     * Determine the command of the S frame (S1 & S2 bits)
     */
    switch (frame->buf[(frame->o_buf/8)+2] & 0x03)
    {
      case I_FRAME_RR:
        TRACE_2_PARA("S-RR s:%d nr:%d", *sapi, *nr);
        *command = I_RR;
        check_length = TRUE;
        break;

      case I_FRAME_ACK:
        TRACE_2_PARA("S-ACK s:%d nr:%d", *sapi, *nr);
        *command = I_ACK;
        check_length = TRUE;
        break;

      case I_FRAME_RNR:
        TRACE_2_PARA("S-RNR s:%d nr:%d", *sapi, *nr);
        *command = I_RNR;
        check_length = TRUE;
        break;

      case I_FRAME_SACK:
        TRACE_2_PARA("S-SACK s:%d nr:%d", *sapi, *nr);
        *command = I_SACK;
        /*
         * min_length is being modified to be more accurate for SACK frames.
         * The S frame SACK format adds a number of up to 32 octets to the
         * control field.
         */
        min_length += S_FRAME_SACK_MIN_CTRL_OCTETS;

        check_length = FALSE;
        break;

      default:
        /*
         * Frame rejection condition due to receipt of a command or
         * response field that is undefined or not implemented (set
         * W3 bit to 1).
         * <R.LLC.XCEPTION.A.002>
         */
        *frame_ok = FALSE;
        *frame_rej = FRAME_REJ_W3;
        return;
    }
  }
  else if ((frame->buf[(frame->o_buf/8)+1] & UI_FRAME_MASK) EQ UI_FRAME_ID)
  {
    /*
     * UI frame, at least 3 octets (1 Address, 2 Control, 0(++)
     * Information, no FCS)
     */
    *pdu_type               = UI_FRAME;
    min_length              = UI_FRAME_MIN_OCTETS_WITHOUT_FCS;
    *frame_rej_ctrl_length  = UI_CTRL_OCTETS;

    /*
     * Check if the frame contains enough octets to access the complete
     * UI frame control field.
     */
    if (frame->l_buf/8 < UI_CTRL_MIN_OCTETS)
    {
      *frame_ok = FALSE;
      return;
    }

    /*
     * Extract N(U) (is stored in N(R))
     */
    *nr = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+1] & 0x07) << 6;
    *nr |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] >> 2);

    TRACE_4_PARA("UI s:%d len:%d nr:%d c:%d", *sapi, BYTELEN(frame->l_buf), *nr, cipher);
  }
  else if ((frame->buf[(frame->o_buf/8)+1] & U_FRAME_MASK) EQ U_FRAME_ID)
  {
    /*
     * U frame, at least 2 octets (1 Address, 1 Control, 0(++)
     * Information, no FCS)
     */
    *pdu_type               = U_FRAME;
    min_length              = U_FRAME_MIN_OCTETS_WITHOUT_FCS;
    *frame_rej_ctrl_length  = U_CTRL_OCTETS;

    /*
     * Extract P/F bit
     */
    *pf_bit = (frame->buf[(frame->o_buf/8)+1] & 0x10) >> 4;

    /*
     * Check if the frame contains enough octets to access the complete
     * U frame control field.
     * NOTE:
     * This check could be omitted, because the U frame control field size
     * is only one octet.
     */
    if (frame->l_buf/8 < U_CTRL_MIN_OCTETS)
    {
      *frame_ok = FALSE;
      *frame_rej = FRAME_REJ_W1;
      return;
    }

    /*
     * Determine the command of the U frame (M4, M3, M2, M1).
     * Adjust the minimum length of the frame, if possible.
     */
    switch (frame->buf[(frame->o_buf/8)+1] & 0x0F)
    {
      case U_FRAME_DM:
        TRACE_1_PARA("DM s:%d", *sapi);
        *command = U_DM;
        /*
         * No information field is permitted.
         */
        check_length = TRUE;
        break;
      case U_FRAME_DISC:
        TRACE_1_PARA("DISC s:%d", *sapi);
        *command = U_DISC;
        /*
         * No information field is permitted.
         */
        check_length = TRUE;
        break;
      case U_FRAME_UA:
        TRACE_2_PARA("UA s:%d len:%d", *sapi, BYTELEN(frame->l_buf));
        *command = U_UA;
        /*
         * No knowledge if an information field is permitted, because
         * this is only the case when UA is the response to SABM.
         */
        break;
      case U_FRAME_SABM:
        TRACE_2_PARA("SABM s:%d len:%d", *sapi, BYTELEN(frame->l_buf));
        *command = U_SABM;
        /*
         * An information field is permitted, containing XID 
         * parameters. Therefore the size of the information field
         * is not (yet) known.
         */
        break;
      case U_FRAME_FRMR:
        TRACE_1_PARA("FRMR s:%d", *sapi);
        *command = U_FRMR;
        /*
         * FRMR response contains an information field of 10 octets.
         * <R.LLC.XCEPTION.A.008>
         */
        min_length += U_FRAME_FRMR_INFO_OCTETS;
        check_length = TRUE;
        break;
      case U_FRAME_XID:
        if( *cr_bit == SGSN_COMMAND )
        {
          TRACE_2_PARA("XID REQ s:%d len:%d", *sapi, BYTELEN(frame->l_buf));
        }
        else
        {
          TRACE_2_PARA("XID RSP s:%d len:%d", *sapi, BYTELEN(frame->l_buf));
        }
        *command = U_XID;
        /*
         * An information field is required, containing XID
         * parameters. Therefore the size of the information field
         * is not (yet) known.
         */
        break;
      default:
        TRACE_0_INFO("Not supported U frame received");
        /*
         * Frame rejection condition due to receipt of a command or
         * response field that is undefined or not implemented (set
         * W3 bit to 1).
         * <R.LLC.XCEPTION.A.002>
         */
        *frame_ok = FALSE;
        *frame_rej = FRAME_REJ_W3;
        return;
    }
  }


  /*
   * Determine if it is requested to check the frame length exactly
   * (check_length EQ TRUE), or just to check the minimum frame length
   * (check_length EQ FALSE).
   */
  if (check_length AND (frame->l_buf/8 NEQ min_length))
  {
    /*
     * Actual length of frame doesn't correspond with computed length.
     */
    *frame_ok = FALSE;

    /*
     * Check if frame rejection condition occured: S or U frame with
     * incorrect length (= W1 + W3 bits). frame_rej_ctrl_length has already 
     * been set above to the correct control field length.
     * <R.LLC.XCEPTION.A.002>, <R.LLC.XCEPTION.A.010>
     */
    if ((*pdu_type EQ S_FRAME) OR (*pdu_type EQ U_FRAME))
    {
      *frame_rej = FRAME_REJ_W1 | FRAME_REJ_W3;
    }
    return;
  }
  else if (frame->l_buf/8 < min_length)
  {
    /*
     * Frame doesn't contain enough octets to include the address field, 
     * control field, information field, and FCS field necessary to constitute
     * a complete frame according to the PDU type.
     * <R.LLC.XCEPTION.A.001>
     */
    *frame_ok = FALSE;
    return;
  }

  return;
} /* rx_interpret_frame() */

/* #endif */ /* CF_FAST_EXEC || _SIMULATION_ */


/*
+------------------------------------------------------------------------------
| Function    : rx_strip_llc_header
+------------------------------------------------------------------------------
| Description : This procedure strips the LLC header field off of the sdu 
|               so that the sdu contains only the remaining L3-PDU.
|
| Parameters  : sdu       - contains the SDU (frame), must be a valid pointer
|               pdu_type  - contains the PDU type of the frame, must be 
|                           a valid PDU type
|               command   - contains the command (I/S, U) of the frame, must be
|                           a valid command for the given PDU type
|
+------------------------------------------------------------------------------
*/

#ifndef CF_FAST_EXEC

GLOBAL void rx_strip_llc_header (T_sdu *sdu, 
                                 T_PDU_TYPE pdu_type,
                                 T_COMMAND command)
{ 
  UBYTE header_len = 0;


  TRACE_FUNCTION ("rx_strip_llc_header");
  
  switch (pdu_type)
  {
    case I_FRAME:
      /*
       * I frame 4 octets. Leave SACK Bitmap in PDU, will be stripped later!
       * (1 Address, 3Control).
       */
      header_len = I_CTRL_MIN_OCTETS;
      break;
    case S_FRAME:
      /*
       * S frame 3 octets. Leave SACK Bitmap in PDU, will be stripped later!
       * (1 Address, 2(+32 max) Control).
       */
       header_len = S_CTRL_MIN_OCTETS;
      break;
    case UI_FRAME:
      /*
       * UI frame, 3 octets (1 Address, 2 Control).
       */
      header_len = UI_CTRL_MIN_OCTETS;
      break;
    case U_FRAME:
      /*
       * U frame, 2 octets (1 Address, 1 Control).
       */
      header_len = U_CTRL_MIN_OCTETS;
      break;
    default:
      TRACE_ERROR ("Unknown PDU type");
      break;
  }

  /*
   * Adjust the beginning of the PDU using the determined header_len.
   */
  sdu->o_buf += header_len * 8;

  /*
   * Adjust the length of the PDU by the size of the header field.
   */
  sdu->l_buf -= header_len * 8;

  return;
} /* rx_strip_llc_header() */

#endif /* CF_FAST_EXEC */

/*
+------------------------------------------------------------------------------
| Function    : rx_get_desc_octet
+------------------------------------------------------------------------------
| Description : This procedure maps the given linear offset to a descriptor
|               list and returns the value of the octet at this offset. If
|               offset is too large for descriptor list, 0x00 is returned.
|               Each descriptor in descriptor list is evaluated until the 
|               requested offset is reached. The resulting octet value is 
|               written to the referenced parameter data_ptr (if not set to 
|               NULL), and is additionally returned.
|
| Parameters  : desc_list - a valid pointer to a valid descriptor list
|               offset    - linear octet offset, beginning with 0
|               data_ptr  - a valid pointer to be set to the resulting octet
|                           value, or NULL
|
+------------------------------------------------------------------------------
*/

#ifndef CF_FAST_EXEC

GLOBAL UBYTE rx_get_desc_octet (T_desc_list *desc_list, 
                               USHORT offset, 
                               UBYTE *data_ptr)
{
  T_desc            *desc;
  UBYTE             *desc_data;         /* local pointer to data octet */

  /*
   * Check if offset is contained in descriptor list. Return 0x00 if not.
   */
  if (offset >= desc_list->list_len)
  {
    TRACE_ERROR ("offset too large for descriptor list");
    return 0x00;
  }

  /*
   * Search requested data with given linear offset in descriptor list.
   * Empty descriptors are skipped. Check for each descriptor if requested
   * offset is in descriptor, until it is found.
   */
  desc = (T_desc *)desc_list->first;
  desc_data = NULL;
  do
  {
    if (offset < desc->len)
    {
      /*
       * Requested data is in current descriptor. Set desc_data to point to 
       * requested octet with remaining offset (has been decremented by
       * already passed descriptor payload).
       */
      /*lint -e662 Possible creation of out-of-bounds pointer */
      desc_data = &(desc->buffer[offset]);
    }
    else
    {
      /*
       * Requested data is not in current descriptor. Remember data payload
       * of current descriptor as already passed.
       */
      offset -= desc->len;
    }

    desc = (T_desc *)desc->next;
  }
  while ((desc NEQ NULL) AND (desc_data EQ NULL));

  
  if (desc_data EQ NULL)
  {
    TRACE_ERROR ("descriptor list ended before offset found");

    if (data_ptr NEQ NULL)
    {
      *data_ptr = 0;
    }

    return 0;
  }
  else
  {
    /*
     * Store value of found octet in data_ptr, if pointer is valid.
     */
    if (data_ptr NEQ NULL)
    {
      /*lint -e661 Possible access of out-of-bounds pointer */
      *data_ptr = *desc_data;
    }

    return *desc_data;
  }
} /* rx_get_desc_octet() */

#endif /* CF_FAST_EXEC */