view src/g23m-gprs/llc/llc_rxf.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 (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 */