diff src/g23m-gprs/llc/llc_rxf.c @ 1:d393cd9bb723

src/g23m-*: initial import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 15 Jul 2018 04:40:46 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/g23m-gprs/llc/llc_rxf.c	Sun Jul 15 04:40:46 2018 +0000
@@ -0,0 +1,1205 @@
+/* 
++----------------------------------------------------------------------------- 
+|  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 */