view src/g23m-gprs/llc/llc_f.c @ 503:0c5f61a73709

hybrid-gpf configuration created
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 25 Jun 2018 00:51:54 +0000
parents 219afcfc6250
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  
|  Modul   :  
+----------------------------------------------------------------------------- 
|  Copyright 2002 Texas Instruments Berlin, AG 
|                 All rights reserved. 
| 
|                 This file is confidential and a trade secret of Texas 
|                 Instruments Berlin, AG 
|                 The receipt of or possession of this file does not convey 
|                 any rights to reproduce or disclose its contents or to 
|                 manufacture, use, or sell anything it may describe, in 
|                 whole, or in part, without the specific written consent of 
|                 Texas Instruments Berlin, AG. 
+----------------------------------------------------------------------------- 
|  Purpose :  Contains global functions of Logical Link Control (LLC)
+----------------------------------------------------------------------------- 
*/ 

#define LLC_F_C

#define ENTITY_LLC

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

#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_par.h"    /* to get the default values of the LLC paramters */
#include "llc_uf.h"     /* to get the XID parameter definitions */


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

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

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

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


/*
+------------------------------------------------------------------------------
| Function    : llc_init_parameters
+------------------------------------------------------------------------------
| Description : This procedure initialises the LLC layer parameters with 
|               their default values as they are defined in LLC_PAR.H.
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_init_parameters (void)
{
  UBYTE             incarnation;

  TRACE_FUNCTION ("llc_init_parameters");

  llc_data->version                               = LLC_VERSION_ALL_SAPIS;

  llc_data->iov_ui                                = LLC_IOV_UI_ALL_SAPIS;
  llc_data->iov_i_base[IMAP(LL_SAPI_3)]           = LLC_IOV_I_SAPI_3;
  llc_data->iov_i_base[IMAP(LL_SAPI_5)]           = LLC_IOV_I_SAPI_5;
  llc_data->iov_i_base[IMAP(LL_SAPI_9)]           = LLC_IOV_I_SAPI_9;
  llc_data->iov_i_base[IMAP(LL_SAPI_11)]          = LLC_IOV_I_SAPI_11;

  /*
   * The LLC parameters for T200 are contained in T200 service data.
   */
  llc_data->t200_base[UIMAP(LL_SAPI_1)].length    = LLC_T200_SAPI_1;
  llc_data->t200_base[UIMAP(LL_SAPI_3)].length    = LLC_T200_SAPI_3;
  llc_data->t200_base[UIMAP(LL_SAPI_5)].length    = LLC_T200_SAPI_5;
  llc_data->t200_base[UIMAP(LL_SAPI_7)].length    = LLC_T200_SAPI_7;
  llc_data->t200_base[UIMAP(LL_SAPI_9)].length    = LLC_T200_SAPI_9;
  llc_data->t200_base[UIMAP(LL_SAPI_11)].length   = LLC_T200_SAPI_11;

  /*
   * The LLC parameters for T201 are contained in T201 service data.
   * T201 is being set to the same values as T200 for all SAPIs.
   * <R.LLC.LLC_PAR.A.015>
   */
  llc_data->n200_base[UIMAP(LL_SAPI_1)]           = LLC_N200_SAPI_1;
  llc_data->n200_base[UIMAP(LL_SAPI_3)]           = LLC_N200_SAPI_3;
  llc_data->n200_base[UIMAP(LL_SAPI_5)]           = LLC_N200_SAPI_5;
  llc_data->n200_base[UIMAP(LL_SAPI_7)]           = LLC_N200_SAPI_7;
  llc_data->n200_base[UIMAP(LL_SAPI_9)]           = LLC_N200_SAPI_9;
  llc_data->n200_base[UIMAP(LL_SAPI_11)]          = LLC_N200_SAPI_11;

  llc_data->n201_u_base[UIMAP(LL_SAPI_1)]         = LLC_N201_U_SAPI_1;
  llc_data->n201_u_base[UIMAP(LL_SAPI_3)]         = LLC_N201_U_SAPI_3;
  llc_data->n201_u_base[UIMAP(LL_SAPI_5)]         = LLC_N201_U_SAPI_5;
  llc_data->n201_u_base[UIMAP(LL_SAPI_7)]         = LLC_N201_U_SAPI_7;
  llc_data->n201_u_base[UIMAP(LL_SAPI_9)]         = LLC_N201_U_SAPI_9;
  llc_data->n201_u_base[UIMAP(LL_SAPI_11)]        = LLC_N201_U_SAPI_11;

  llc_data->n201_i_base[IMAP(LL_SAPI_3)]          = LLC_N201_I_SAPI_3;
  llc_data->n201_i_base[IMAP(LL_SAPI_5)]          = LLC_N201_I_SAPI_5;
  llc_data->n201_i_base[IMAP(LL_SAPI_9)]          = LLC_N201_I_SAPI_9;
  llc_data->n201_i_base[IMAP(LL_SAPI_11)]         = LLC_N201_I_SAPI_11;
      
  llc_data->md_base[IMAP(LL_SAPI_3)]              = LLC_MD_SAPI_3;
  llc_data->md_base[IMAP(LL_SAPI_5)]              = LLC_MD_SAPI_5;
  llc_data->md_base[IMAP(LL_SAPI_9)]              = LLC_MD_SAPI_9;
  llc_data->md_base[IMAP(LL_SAPI_11)]             = LLC_MD_SAPI_11;
      
  llc_data->mu_base[IMAP(LL_SAPI_3)]              = LLC_MU_SAPI_3;
  llc_data->mu_base[IMAP(LL_SAPI_5)]              = LLC_MU_SAPI_5;
  llc_data->mu_base[IMAP(LL_SAPI_9)]              = LLC_MU_SAPI_9;
  llc_data->mu_base[IMAP(LL_SAPI_11)]             = LLC_MU_SAPI_11;
      
  llc_data->kd_base[IMAP(LL_SAPI_3)]              = LLC_KD_SAPI_3;
  llc_data->kd_base[IMAP(LL_SAPI_5)]              = LLC_KD_SAPI_5;
  llc_data->kd_base[IMAP(LL_SAPI_9)]              = LLC_KD_SAPI_9;
  llc_data->kd_base[IMAP(LL_SAPI_11)]             = LLC_KD_SAPI_11;
      
  llc_data->ku_base[IMAP(LL_SAPI_3)]              = LLC_KU_SAPI_3;
  llc_data->ku_base[IMAP(LL_SAPI_5)]              = LLC_KU_SAPI_5;
  llc_data->ku_base[IMAP(LL_SAPI_9)]              = LLC_KU_SAPI_9;
  llc_data->ku_base[IMAP(LL_SAPI_11)]             = LLC_KU_SAPI_11;

  /*
   * Reset all OCs for unacknowledged transfer.
   */
  for (incarnation = 0; incarnation < MAX_SAPI_INC; incarnation++)
  {
    llc_data->sapi_base[incarnation].oc_ui_tx = 0L;
    llc_data->sapi_base[incarnation].oc_ui_rx = 0L;
  }

  return;
} /* llc_init_parameters() */


/*
+------------------------------------------------------------------------------
| Function    : llc_get_ffs_data
+------------------------------------------------------------------------------
| Description : Load configured LLC parameter from FFS. In case of _SIMULATION_
|               only default values are returned.
|
| Parameters  : type             - XID Paramter type
|               sapi_array_index - Array index of Sapi !!!
|
+------------------------------------------------------------------------------
*/
LOCAL USHORT llc_get_ffs_data (UBYTE type, UBYTE sapi_array_index)
{

  switch (type)
  {
#ifdef _SIMULATION_

    case XID_T200:      
      if (llc_data->ffs_xid.t200[sapi_array_index].valid)
        return llc_data->ffs_xid.t200[sapi_array_index].value; /* 50 = 5 sec !!! */
      else
        return (USHORT)INT2XID(llc_data->t200_base[sapi_array_index].length);

    case XID_N200:      
      if (llc_data->ffs_xid.n200[sapi_array_index].valid)
        return (USHORT)llc_data->ffs_xid.n200[sapi_array_index].value;
      else
        return (USHORT)llc_data->n200_base[sapi_array_index];

    case XID_MD:        
      if (llc_data->ffs_xid.md[sapi_array_index].valid)
        return llc_data->ffs_xid.md[sapi_array_index].value;
      else
        return llc_data->md_base[sapi_array_index];

    case XID_MU:        
      if (llc_data->ffs_xid.mu[sapi_array_index].valid)
        return (USHORT)llc_data->ffs_xid.mu[sapi_array_index].value;
      else
        return (USHORT)llc_data->mu_base[sapi_array_index];

    case XID_N201_U:    
      if (llc_data->ffs_xid.n201_u[sapi_array_index].valid)
        return llc_data->ffs_xid.n201_u[sapi_array_index].value;
      else
        return llc_data->n201_u_base[sapi_array_index];

    case XID_N201_I:
      if (llc_data->ffs_xid.n201_i[sapi_array_index].valid)
        return llc_data->ffs_xid.n201_i[sapi_array_index].value;
      else
        return llc_data->n201_i_base[sapi_array_index];

    case XID_KD:        
      if (llc_data->ffs_xid.kd[sapi_array_index].valid)
        return (USHORT)llc_data->ffs_xid.kd[sapi_array_index].value;
      else
        return (USHORT)llc_data->kd_base[sapi_array_index];

    case XID_KU:        
      if (llc_data->ffs_xid.ku[sapi_array_index].valid)
        return (USHORT)llc_data->ffs_xid.ku[sapi_array_index].value;
      else
        return (USHORT)llc_data->ku_base[sapi_array_index];

#else

    case XID_T200:      return (USHORT)INT2XID(llc_data->t200_base[sapi_array_index].length);
    case XID_N200:      return (USHORT)llc_data->n200_base[sapi_array_index];
/*  case XID_N201_U:    return (USHORT)llc_data->n201_u_base[sapi_array_index]; */
/*  case XID_KD:        return (USHORT)llc_data->kd_base[sapi_array_index]; */
/*  case XID_KU:        return (USHORT)llc_data->ku_base[sapi_array_index]; */
    case XID_N201_I:    return (USHORT)N201_I_SUPPORTED;
    case XID_N201_U:    return (USHORT)N201_U_SUPPORTED;
    case XID_KD:        return (USHORT)KD_KD_SUPPORTED; 
    case XID_KU:        return (USHORT)KD_KU_SUPPORTED;
    case XID_MD:        return (USHORT)KD_MD_SUPPORTED;
    case XID_MU:        return (USHORT)KD_MU_SUPPORTED;


#endif /* _SIMULATION_ */

    default:
      TRACE_ERROR ("Illegal FFS parameter used");
      return 0;
  }

} /* llc_get_ffs_data */


/*
+------------------------------------------------------------------------------
| Function    : llc_init_requested_xid
+------------------------------------------------------------------------------
| Description : Fill in requested XID parameters into requested_xid structure
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_init_requested_xid (void)
{
  UBYTE  u_inc, i_inc, sapi;
  USHORT requested_value;

  for (u_inc = 0; u_inc < ACKNOWLEDGED_INC; u_inc++)
  {
    llc_data->requested_l3_xid_base[u_inc].valid  = FALSE;
  }

  for (sapi = LL_SAPI_1; sapi <= LL_SAPI_11; sapi += 2)
  {
    u_inc = UIMAP(sapi);

    llc_data->u_base[u_inc].requested_xid.version.valid = FALSE;
    llc_data->u_base[u_inc].requested_xid.iov_ui.valid  = FALSE;
    llc_data->u_base[u_inc].requested_xid.iov_i.valid   = FALSE;
    llc_data->u_base[u_inc].requested_xid.reset.valid   = FALSE;

    llc_data->u_base[u_inc].requested_xid.t200.valid    = FALSE;
    llc_data->u_base[u_inc].requested_xid.n200.valid    = FALSE;
    llc_data->u_base[u_inc].requested_xid.n201_u.valid  = FALSE;
    llc_data->u_base[u_inc].requested_xid.n201_i.valid  = FALSE;
    llc_data->u_base[u_inc].requested_xid.kd.valid      = FALSE;
    llc_data->u_base[u_inc].requested_xid.ku.valid      = FALSE;
    llc_data->u_base[u_inc].requested_xid.md.valid      = FALSE;
    llc_data->u_base[u_inc].requested_xid.mu.valid      = FALSE;

    requested_value = llc_get_ffs_data (XID_T200, u_inc);
    if (requested_value != (USHORT)INT2XID(llc_data->t200_base[u_inc].length))
    {
      llc_data->u_base[u_inc].requested_xid.t200.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.t200.value  = requested_value;
    }

    requested_value = llc_get_ffs_data (XID_N200, u_inc);
    if (requested_value != llc_data->n200_base[u_inc])
    {
      llc_data->u_base[u_inc].requested_xid.n200.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.n200.value  = (UBYTE)requested_value;
    }

    requested_value = llc_get_ffs_data (XID_N201_U, u_inc);
    if (requested_value != llc_data->n201_u_base[u_inc])
    {
      llc_data->u_base[u_inc].requested_xid.n201_u.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.n201_u.value  = requested_value;
    }

    if ((sapi != LL_SAPI_1) && (sapi != LL_SAPI_7))
    {
      i_inc = (UBYTE)IMAP(sapi);

      requested_value = llc_get_ffs_data (XID_N201_I, i_inc);
      if (requested_value != llc_data->n201_i_base[i_inc])
      {
        llc_data->u_base[u_inc].requested_xid.n201_i.valid  = TRUE;
        llc_data->u_base[u_inc].requested_xid.n201_i.value  = requested_value;
      }

      requested_value = llc_get_ffs_data (XID_MD, i_inc);
      if (requested_value != llc_data->md_base[i_inc])
      {
        llc_data->u_base[u_inc].requested_xid.md.valid  = TRUE;
        llc_data->u_base[u_inc].requested_xid.md.value  = requested_value;
      }

      requested_value = llc_get_ffs_data (XID_MU, i_inc);
      if (requested_value != llc_data->mu_base[i_inc])
      {
        llc_data->u_base[u_inc].requested_xid.mu.valid  = TRUE;
        llc_data->u_base[u_inc].requested_xid.mu.value  = requested_value;
      }

      requested_value = llc_get_ffs_data (XID_KD, i_inc);
      if (requested_value != llc_data->kd_base[i_inc])
      {
        llc_data->u_base[u_inc].requested_xid.kd.valid  = TRUE;
        llc_data->u_base[u_inc].requested_xid.kd.value  = (UBYTE)requested_value;
      }
  
      requested_value = llc_get_ffs_data (XID_KU, i_inc);
      if (requested_value != llc_data->ku_base[i_inc])
      {
        llc_data->u_base[u_inc].requested_xid.ku.valid  = TRUE;
        llc_data->u_base[u_inc].requested_xid.ku.value  = (UBYTE)requested_value;
      }
    }
  }
} /* llc_init_requested_xid() */


/*
+------------------------------------------------------------------------------
| Function    : llc_init_requested_xid_sapi
+------------------------------------------------------------------------------
| Description : Fill in requested XID parameters into requested_xid structure
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_init_requested_xid_sapi (T_SAPI sapi)
{
  UBYTE  u_inc, i_inc;
  USHORT requested_value;

  u_inc = UIMAP(sapi);

  llc_data->u_base[u_inc].requested_xid.t200.valid    = FALSE;
  llc_data->u_base[u_inc].requested_xid.n200.valid    = FALSE;
  llc_data->u_base[u_inc].requested_xid.n201_u.valid  = FALSE;
  llc_data->u_base[u_inc].requested_xid.n201_i.valid  = FALSE;
  llc_data->u_base[u_inc].requested_xid.kd.valid      = FALSE;
  llc_data->u_base[u_inc].requested_xid.ku.valid      = FALSE;
  llc_data->u_base[u_inc].requested_xid.md.valid      = FALSE;
  llc_data->u_base[u_inc].requested_xid.mu.valid      = FALSE;

  requested_value = llc_get_ffs_data (XID_T200, u_inc);
  if (requested_value != (USHORT)INT2XID(llc_data->t200_base[u_inc].length))
  {
    llc_data->u_base[u_inc].requested_xid.t200.valid  = TRUE;
    llc_data->u_base[u_inc].requested_xid.t200.value  = requested_value;
  }

  requested_value = llc_get_ffs_data (XID_N200, u_inc);
  if (requested_value != llc_data->n200_base[u_inc])
  {
    llc_data->u_base[u_inc].requested_xid.n200.valid  = TRUE;
    llc_data->u_base[u_inc].requested_xid.n200.value  = (UBYTE)requested_value;
  }

  requested_value = llc_get_ffs_data (XID_N201_U, u_inc);
  if (requested_value != llc_data->n201_u_base[u_inc])
  {
    llc_data->u_base[u_inc].requested_xid.n201_u.valid  = TRUE;
    llc_data->u_base[u_inc].requested_xid.n201_u.value  = requested_value;
  }

  if ((sapi != LL_SAPI_1) && (sapi != LL_SAPI_7))
  {
    i_inc = (UBYTE)IMAP(sapi);

    requested_value = llc_get_ffs_data (XID_N201_I, i_inc);
    if (requested_value != llc_data->n201_i_base[i_inc])
    {
      llc_data->u_base[u_inc].requested_xid.n201_i.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.n201_i.value  = requested_value;
    }

    requested_value = llc_get_ffs_data (XID_MD, i_inc);
    if (requested_value != llc_data->md_base[i_inc])
    {
      llc_data->u_base[u_inc].requested_xid.md.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.md.value  = requested_value;
    }

    requested_value = llc_get_ffs_data (XID_MU, i_inc);
    if (requested_value != llc_data->mu_base[i_inc])
    {
      llc_data->u_base[u_inc].requested_xid.mu.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.mu.value  = requested_value;
    }

    requested_value = llc_get_ffs_data (XID_KD, i_inc);
    if (requested_value != llc_data->kd_base[i_inc])
    {
      llc_data->u_base[u_inc].requested_xid.kd.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.kd.value  = (UBYTE)requested_value;
    }
  
    requested_value = llc_get_ffs_data (XID_KU, i_inc);
    if (requested_value != llc_data->ku_base[i_inc])
    {
      llc_data->u_base[u_inc].requested_xid.ku.valid  = TRUE;
      llc_data->u_base[u_inc].requested_xid.ku.value  = (UBYTE)requested_value;
    }
  }
} /* llc_init_requested_xid_sapi() */



/*
+------------------------------------------------------------------------------
| Function    : llc_generate_input
+------------------------------------------------------------------------------
| Description : This procedure calculates the frame-dependent input for 
|               UI_FRAMES / I_FRAMES. S_FRAMES are treated like I_FRAMES. 
|               Parameter direction must be one of 
|               CCI_DIRECTION_UPLINK/DOWNLINK. The input is generated 
|               according to section A.2.1 in GSM 04.64, and written to 
|               paramete input. This procedure is called from services 
|               TX and RX.
|
| Parameters  : sapi          - valid SAPI number
|               frame_type    - indicates acknowledged/unacknowledged frames
|               lfn           - LLC frame number
|               direction     - CCI_DIRECTION_UPLINK/CCI_DIRECTION_DOWNLINK
|               cipher_input  - generated ciphering input
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_generate_input (UBYTE sapi,
                                T_PDU_TYPE frame_type,
                                T_FRAME_NUM lfn,
                                ULONG *cipher_input,
                                ULONG oc)
{
  ULONG             sx;


  TRACE_FUNCTION ("llc_generate_input");

  /*
   * According to 04.64, Annex A.2.1:
   *   SX = 2exp27 * SAPI + 2exp31
   */
  sx = 134217728 * sapi + 2147483648;

  if ((frame_type EQ I_FRAME) OR (frame_type EQ S_FRAME))
  {
    /*
     * According to 04.64, Annex A.2.1:
     *   Input = ((IOV-I + LFN + OC) modulo 2exp32
     */
    *cipher_input = (ULONG)(*(llc_data->iov_i) + lfn + oc);
  }
  else /* unacknowledged mode */
  {
    /*
     * According to 04.64, Annex A.2.1:
     *   Input = ((IOV-UI XOR SX) + LFN + OC) modulo 2exp32
     */
    *cipher_input = (ULONG)((llc_data->iov_ui ^ sx) + lfn + oc);
  }

  return;
} /* llc_generate_input() */


/*
+------------------------------------------------------------------------------
| Function    : llc_build_crc24
+------------------------------------------------------------------------------
| Description : This procedure builds a CRC24 checksum according to RFC2440,
|               which is needed by LLC to build and check the Frame Check
|               Sequence (FCS) which is included in each frame.
|
| Parameters  : octets  - a valid pointer to the frame contents
|               len     - number of valid octets
|
+------------------------------------------------------------------------------
*/

/* 
 * Precomputed table for polynomial : 0x00ad85dd (high term in LSB)
 * 24-bit masks are packed in 16-bits words
 *
 */
 
const USHORT a_fcs24_tab_rev_packed[384] = {
  0x0000, 0x7600, 0xd6a7, 0x4557, 0x21f6, 0x20e2, 0x8115, 0x63b7, 0x6126, 0xc442, 0x3441, 0x9763, 
  0x0991, 0xe734, 0xe2ae, 0x4cc6, 0xb0c2, 0x14eb, 0x8884, 0xf283, 0x552f, 0xcdd3, 0xa575, 0xa36a, 
  0x1322, 0x5468, 0xbeb4, 0x5675, 0x039e, 0x48f1, 0x9237, 0x41df, 0x0935, 0xd760, 0x1629, 0xff70, 
  0x1ab3, 0xc55c, 0x8abd, 0x5fe4, 0x92aa, 0x7cf8, 0x9ba6, 0xd0eb, 0x3d3c, 0xdef1, 0x871d, 0xcb79, 
  0x2644, 0x32d0, 0x0681, 0x6313, 0x6526, 0xf0c4, 0xa751, 0x2767, 0xb100, 0xe206, 0x7091, 0x4745, 
  0x2fd5, 0xa3e4, 0x3288, 0x6a82, 0xf412, 0xc4cd, 0xaec0, 0xb653, 0x8509, 0xeb97, 0xe1a5, 0x734c, 
  0x3566, 0x10b8, 0x6e92, 0x7031, 0x474e, 0x98d7, 0xb473, 0x050f, 0xd913, 0xf124, 0x52f9, 0x2f56, 
  0x3cf7, 0x818c, 0x5a9b, 0x79a0, 0xd67a, 0xacde, 0xbde2, 0x943b, 0xed1a, 0xf8b5, 0xc3cd, 0x1b5f, 
  0x4733, 0x45fb, 0x2de0, 0x0264, 0x120d, 0xdba5, 0xc626, 0x504c, 0x9a61, 0x8371, 0x07ba, 0x6c24, 
  0x4ea2, 0xd4cf, 0x19e9, 0x0bf5, 0x8339, 0xefac, 0xcfb7, 0xc178, 0xae68, 0x8ae0, 0x968e, 0x582d, 
  0x5411, 0x6793, 0x45f3, 0x1146, 0x3065, 0xb3b6, 0xd504, 0x7224, 0xf272, 0x9053, 0x25d2, 0x0437, 
  0x5d80, 0xf6a7, 0x71fa, 0x18d7, 0xa151, 0x87bf, 0xdc95, 0xe310, 0xc67b, 0x99c2, 0xb4e6, 0x303e, 
  0x6177, 0x012b, 0xfdc6, 0x2420, 0x56dd, 0x0b83, 0xe062, 0x149c, 0x4a47, 0xa535, 0x436a, 0xbc02, 
  0x68e6, 0x901f, 0xc9cf, 0x2db1, 0xc7e9, 0x3f8a, 0xe9f3, 0x85a8, 0x7e4e, 0xaca4, 0xd25e, 0x880b, 
  0x7255, 0x2343, 0x95d5, 0x3702, 0x74b5, 0x6390, 0xf340, 0x36f4, 0x2254, 0xb617, 0x6102, 0xd411, 
  0x7bc4, 0xb277, 0xa1dc, 0x3e93, 0xe581, 0x5799, 0xfad1, 0xa7c0, 0x165d, 0xbf86, 0xf036, 0xe018, 
  0x85dd, 0xabad, 0x7b22, 0xc08a, 0xfc5b, 0x8d67, 0x04c8, 0xbe1a, 0xcca3, 0x419f, 0xe9ec, 0x3ae6, 
  0x8c4c, 0x3a99, 0x4f2b, 0xc91b, 0x6d6f, 0xb96e, 0x0d59, 0x2f2e, 0xf8aa, 0x480e, 0x78d8, 0x0eef, 
  0x96ff, 0x89c5, 0x1331, 0xd3a8, 0xde33, 0xe574, 0x17ea, 0x9c72, 0xa4b0, 0x52bd, 0xcb84, 0x52f5, 
  0x9f6e, 0x18f1, 0x2738, 0xda39, 0x4f07, 0xd17d, 0x1e7b, 0x0d46, 0x90b9, 0x5b2c, 0x5ab0, 0x66fc, 
  0xa399, 0xef7d, 0xab04, 0xe6ce, 0xb88b, 0x5d41, 0x228c, 0xfaca, 0x1c85, 0x67db, 0xad3c, 0xeac0, 
  0xaa08, 0x7e49, 0x9f0d, 0xef5f, 0x29bf, 0x6948, 0x2b1d, 0x6bfe, 0x288c, 0x6e4a, 0x3c08, 0xdec9, 
  0xb0bb, 0xcd15, 0xc317, 0xf5ec, 0x9ae3, 0x3552, 0x31ae, 0xd8a2, 0x7496, 0x74f9, 0x8f54, 0x82d3, 
  0xb92a, 0x5c21, 0xf71e, 0xfc7d, 0x0bd7, 0x015b, 0x383f, 0x4996, 0x409f, 0x7d68, 0x1e60, 0xb6da, 
  0xc2ee, 0x9856, 0x8065, 0x87b9, 0xcfa0, 0x7620, 0x43fb, 0x8de1, 0x37e4, 0x06ac, 0xda17, 0xc1a1, 
  0xcb7f, 0x0962, 0xb46c, 0x8e28, 0x5e94, 0x4229, 0x4a6a, 0x1cd5, 0x03ed, 0x0f3d, 0x4b23, 0xf5a8, 
  0xd1cc, 0xba3e, 0xe876, 0x949b, 0xedc8, 0x1e33, 0x50d9, 0xaf89, 0x5ff7, 0x158e, 0xf87f, 0xa9b2, 
  0xd85d, 0x2b0a, 0xdc7f, 0x9d0a, 0x7cfc, 0x2a3a, 0x5948, 0x3ebd, 0x6bfe, 0x1c1f, 0x694b, 0x9dbb, 
  0xe4aa, 0xdc86, 0x5043, 0xa1fd, 0x8b70, 0xa606, 0x65bf, 0xc931, 0xe7c2, 0x20e8, 0x9ec7, 0x1187, 
  0xed3b, 0x4db2, 0x644a, 0xa86c, 0x1a44, 0x920f, 0x6c2e, 0x5805, 0xd3cb, 0x2979, 0x0ff3, 0x258e, 
  0xf788, 0xfeee, 0x3850, 0xb2df, 0xa918, 0xce15, 0x769d, 0xeb59, 0x8fd1, 0x33ca, 0xbcaf, 0x7994, 
  0xfe19, 0x6fda, 0x0c59, 0xbb4e, 0x382c, 0xfa1c, 0x7f0c, 0x7a6d, 0xbbd8, 0x3a5b, 0x2d9b, 0x4d9d
};


/******************************************************************************
 *
 *  FUNCTION NAME: f_crc24_tab
 *
 *  The function compute the 24-bit FCS and is using table for it 
 *
 *  ARGUMENT LIST:
 *
 *  Argument        Type      IO  Description
 *  -------------   --------  --  ---------------------------------------------
 *  data_p          T_BYTE*   I  Pointer on the input buffer
 *  d_length          T_UINT16  I  Number of bytes to process
 *  d_l_crc_init    T_UINT16  I  Init value of the CRC
 *
 * RETURN VALUE:    
 *  Argument        Type      Description
 *  -------------   --------  ---------------------------------------------
 *  d_l_crc         T_UINT32  24-bit FCS stored in a 32-bit word
 *
 *****************************************************************************/
GLOBAL ULONG llc_build_crc24 (UBYTE *d_data_p, 
                              USHORT d_length)
{
  /*  GLOBAL VARIABLES:
   *
   *  Variables       Type      IO  Description
   *  -------------   --------  --  -------------------------------------------
   *  none
   */

  /*  LOCAL VARIABLES:
   *
   *  Variables       Type      Description
   *  -------------   -------   ----------------------------------------------
   *  d_l_crc         T_UINT32  24-bit FCS stored in a 32-bit word
   *  d_l_tab_res     T_UINT32  FCS mask extracted from the table
   *  d_l_tab_index   T_UINT32  Index of the FCS mask into the table
   *  d_byte_msb      T_UINT16  Intermediate variable used to know the position
   *                            FCSof the 24-bit FCS into two 16-bits words
   */
  ULONG  d_l_crc, d_l_tab_res, d_l_tab_index;
  USHORT d_byte_msb;

  d_l_crc = 0x00ffffff;
  
  do 
  {
    /* Most efficient C implementation with 32-bits tables but require more d_data rom */
    /* d_l_crc = (d_l_crc >> 8) ^ a_fcs24_tab_rev[(d_l_crc ^ *d_data_p++) & 0xff];     */

    /* Details very close to the ASM implementation */
    d_l_tab_index = (d_l_crc ^ *d_data_p++) & 0x0ff;
    d_l_crc >>= 8;

    if(d_l_tab_index & 0x01)
      d_byte_msb = 1;
    else
      d_byte_msb = 0;

    d_l_tab_index *= 3;

    d_l_tab_index >>= 1;

    d_l_crc &= 0x0000ffff;  /* Replace the code in comment below */

    d_l_tab_res = a_fcs24_tab_rev_packed[d_l_tab_index];
    d_l_tab_res |= (a_fcs24_tab_rev_packed[d_l_tab_index+1] << 16);

    if(d_byte_msb == 1)
      d_l_tab_res >>= 8;

    d_l_crc ^= d_l_tab_res;
  } 
  while (--d_length);

  d_l_crc = (~d_l_crc) & 0x00ffffff;

  return (d_l_crc);
} /* llc_build_crc24() */

/*
+------------------------------------------------------------------------------
| Function    : llc_xid_value_acceptable
+------------------------------------------------------------------------------
| Description : This procedure checks if the value of the XID parameter for
|               the given SAPI is acceptable, or not. The return value is TRUE
|               for an accepted value, and FALSE for an unaccepted value.
|
| Parameters  : sapi          - SAPI number
|               xid_parameter - XID parameter (defined in llc_uf.h)
|               xid_value     - value of XID parameter (actually UBYTE/USHORT/
|                               ULONG, depending on the parameter)
|
+------------------------------------------------------------------------------
*/
GLOBAL BOOL llc_xid_value_acceptable (UBYTE sapi,
                                      UBYTE xid_parameter,
                                      ULONG xid_value)
{
  BOOL              rc;
  UBYTE             u_inc, i_inc;


  TRACE_FUNCTION ("llc_xid_value_acceptable");

  /*
   * Preset rc in case of unknown XID parameters or unknown SAPI values.
   */
  rc  = FALSE;
  u_inc = UIMAP(sapi);
  i_inc = IMAP(sapi);

  /*
   * Accept only possible (valid) values for now (defined in llc_uf.h).
   */
  switch (xid_parameter)
  {
    case XID_VERSION:
      if (xid_value EQ LLC_VERSION_ALL_SAPIS)
      {
        rc = TRUE;
        TRACE_EVENT ("Version value accepted");
      }
      else
      {
        TRACE_EVENT ("Version value NOT accepted");
      }
      break;

    case XID_IOV_UI:
      /*
       * Must not be checked, ignored.
       */
      TRACE_EVENT ("IOV-UI values are not checked.");
      break;

    case XID_IOV_I:
      /*
       * Must not be checked, ignored.
       */
      TRACE_EVENT ("IOV-I values are not checked.");
      break;

    case XID_T200:
      switch (sapi)
      {
        case LL_SAPI_1:
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_7:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value >= (llc_data->u_base[u_inc].requested_xid.t200.valid ?
                llc_get_ffs_data(XID_T200, u_inc): XID_T200_MIN))  
               AND (xid_value <= XID_T200_MAX) )
          {
            rc = TRUE;
            TRACE_EVENT ("T200 value accepted");
          }
          else
          {
            TRACE_EVENT ("T200 value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for T200");
          break;
      }
      break;

    case XID_N200:
      switch (sapi)
      {
        case LL_SAPI_1:
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_7:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value >= (llc_data->u_base[u_inc].requested_xid.n200.valid ?
                llc_get_ffs_data(XID_N200, u_inc): XID_N200_MIN))  
               AND (xid_value <= XID_N200_MAX) )
          {
            rc = TRUE;
            TRACE_EVENT ("N200 value accepted");
          }
          else
          {
            TRACE_EVENT ("N200 value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for N200");
          break;
      }
      break;

    case XID_N201_U:
      switch (sapi)
      {
        case LL_SAPI_1:
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_7:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value >= (USHORT)XID_N201_U_MIN) AND 
             (xid_value <= (llc_data->u_base[u_inc].requested_xid.n201_u.valid ?
              llc_get_ffs_data(XID_N201_U, u_inc): XID_N201_U_MAX)) )
          {
            rc = TRUE;
            TRACE_EVENT ("N201-U value accepted");
          }
          else
          {
            TRACE_EVENT ("N201-U value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for N201-U");
          break;
      }
      break;

    case XID_N201_I:
      switch (sapi)
      {
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value >= XID_N201_I_MIN) AND 
               (xid_value <= (llc_data->u_base[u_inc].requested_xid.n201_i.valid ?
                llc_get_ffs_data(XID_N201_I, i_inc): XID_N201_I_MAX)) )
          {
            rc = TRUE;
            TRACE_EVENT ("N201-I value accepted");
          }
          else
          {
            TRACE_EVENT ("N201-I value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for N201-I");
          break;
      }
      break;

    case XID_MD:
      switch (sapi)
      {
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value == XID_MD_OFF) OR ((xid_value >= XID_MD_MIN) AND 
               (xid_value <= (llc_data->u_base[u_inc].requested_xid.md.valid ?
              llc_get_ffs_data(XID_MD, i_inc): XID_MD_MAX)) ))
          {
            rc = TRUE;
            TRACE_EVENT ("mD value accepted");
          }
          else
          {
            TRACE_EVENT ("mD value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for mD");
          break;
      }
      break;

    case XID_MU:
      switch (sapi)
      {
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value == XID_MU_OFF) OR ((xid_value >= XID_MU_MIN) AND 
               (xid_value <= (llc_data->u_base[u_inc].requested_xid.mu.valid ?
                llc_get_ffs_data(XID_MU, i_inc): XID_MU_MAX)) ))
          {
            rc = TRUE;
            TRACE_EVENT ("mU value accepted");
          }
          else
          {
            TRACE_EVENT ("mU value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for mU");
          break;
      }
      break;

    case XID_KD:
      switch (sapi)
      {
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value >= XID_KD_MIN) AND 
               (xid_value <= (llc_data->u_base[u_inc].requested_xid.kd.valid ?
                llc_get_ffs_data(XID_KD, i_inc): XID_KD_MAX)) )
          {
            rc = TRUE;
            TRACE_EVENT ("kD value accepted");
          }
          else
          {
            TRACE_EVENT ("kD value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for kD");
          break;
      }
      break;

    case XID_KU:
      switch (sapi)
      {
        case LL_SAPI_3:
        case LL_SAPI_5:
        case LL_SAPI_9:
        case LL_SAPI_11:
          if ( (xid_value >= XID_KU_MIN) AND
               (xid_value <= (llc_data->u_base[u_inc].requested_xid.ku.valid ?
                llc_get_ffs_data(XID_KU, i_inc): XID_KU_MAX)) )
          {
            rc = TRUE;
            TRACE_EVENT ("kU value accepted");
          }
          else
          {
            TRACE_EVENT ("kU value NOT accepted");
          }
          break;
        default:
          TRACE_ERROR ("invalid SAPI value for kU");
          break;
      }
      break;

    case XID_LAYER_3:
      /*
       * Must not be checked, ignored.
       */
      TRACE_EVENT ("Layer-3 values are not checked.");
      break;

    case XID_RESET:
      /*
       * Must not be checked, ignored.
       */
      TRACE_EVENT ("Reset is not checked.");
      break;

    default:
      TRACE_ERROR ("unknown XID parameter");
      break;
  }

  return rc;
} /* llc_xid_value_acceptable() */


/*
+------------------------------------------------------------------------------
| Function    : llc_palloc_desc
+------------------------------------------------------------------------------
| Description : This function allocates a descriptor of type T_DESC3
|
| Parameters  : len -  length of descriptor
|             : offset offset of descriptor
|
| Return      : poniter to T_DESC3
|
+------------------------------------------------------------------------------
*/
#ifdef LL_DESC
GLOBAL T_desc3* llc_palloc_desc( U16 len, U16 offset)
{
        T_desc3 *desc3;
        U8 *buffer;

        TRACE_FUNCTION ("llc_palloc_desc");

        MALLOC (desc3, (USHORT)(sizeof(T_desc3)));
        
        MALLOC (buffer, len + offset);

        desc3->next = NULL;
        desc3->offset = offset;
        desc3->len = len;
        desc3->buffer = (ULONG)buffer;

        return desc3;
}

/*
+------------------------------------------------------------------------------
| Function    : llc_cl_desc3_free
+------------------------------------------------------------------------------
| Description : Frees the descriptor connected to the desc3 descriptor. This 
|               free will when applicable cause the frame to decrease a 
|               connected counter attached to the allocation or really free
|               the memory in case the counter is zero.
|
| Parameters  : T_desc3* pointer to desc3 descriptor.
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_cl_desc3_free(T_desc3* p_desc3)
{
  T_desc3* help;
  while(p_desc3)
  {
    help = (T_desc3*)p_desc3->next;
    MFREE(p_desc3->buffer);
    MFREE(p_desc3);
    p_desc3 = help;
  }
}
#endif /* LL_DESC */

#ifndef TI_PS_OP_CIPH_DRIVER
/*
+------------------------------------------------------------------------------
| Function    : llc_fbs_init
+------------------------------------------------------------------------------
| Description : The function fbs_init() initializes service 
|               variablaes
|
| Parameters  : void
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_fbs_init ( void )
{ 
  TRACE_FUNCTION( "llc_fbs_init" );
  llc_data->fbs.initialized = FALSE;
}
#endif
/*
+------------------------------------------------------------------------------
| Function    : llc_copy_ul_data_to_list
+------------------------------------------------------------------------------
| Description : The function copy_data_to_list copies the length and the pointer 
|               of the linked descriptor list to the in_data_list.
|               It is used in uplink direction
|
| Parameters  : 
|
+------------------------------------------------------------------------------
*/

GLOBAL void llc_copy_ul_data_to_list
( 
 T_CCI_CIPHER_DESC_REQ *cipher_req,
 T_CIPH_in_data_list *in_data_list
)
{ 
  
  T_desc3*           cipher_desc;
  U16                cnt = 0;
  T_CIPH_in_data*    in_data_array;


  /*
   * Count the number of descriptors in the incoming desc_list
   */
  cipher_desc = (T_desc3*)cipher_req->desc_list3.first;
  while (cipher_desc){
    cipher_desc = (T_desc3*)cipher_desc->next;  
    cnt ++;
  }
  /*
   * Allocate an array for T_CIPH_in_data_list 
   */
  MALLOC(in_data_array,(sizeof(T_CIPH_in_data) * cnt));
  in_data_list->ptr_in_data = in_data_array;

  /*
   * Copy buf and len parameters from descriptors to the in_data's
   */
  cipher_desc = (T_desc3*)cipher_req->desc_list3.first;
  cnt = 0;
  while (cipher_desc)
  {
    in_data_list->ptr_in_data[cnt].buf = 
      (U32)&((U8*)cipher_desc->buffer)[cipher_desc->offset];
    in_data_list->ptr_in_data[cnt].len = cipher_desc->len;

    cipher_desc = (T_desc3*)cipher_desc->next;  
    cnt++;
  }
  in_data_list->c_in_data = cnt;  
  
} /* llc_copy_ul_data_to_list */

/*
+------------------------------------------------------------------------------
| Function    : llc_copy_dl_data_to_list
+------------------------------------------------------------------------------
| Description : The function copy_data_to_list copies the length and the pointer 
|               of the linked descriptor list to the in_data_list.
|               It is used in downlink direction
|
| Parameters  :
|
+------------------------------------------------------------------------------
*/

GLOBAL void llc_copy_dl_data_to_list
( 
 T_CCI_DECIPHER_REQ *decipher_req,
 T_CIPH_in_data_list *in_data_list
)
{ 
  
  T_desc* decipher_desc;
  T_CIPH_in_data*   in_data_array;
  USHORT   cnt = 0;

  /*
   * Count the number of descriptors in the incoming desc_list
   */
  decipher_desc = (T_desc*)decipher_req->desc_list.first;
  while (decipher_desc){
    decipher_desc = (T_desc*)decipher_desc->next;  
    cnt ++;  
  }
  
  /*
   * Allocate an array of for T_CIPH_in_data 
   */
  MALLOC(in_data_array, (sizeof(T_CIPH_in_data) * cnt));
  in_data_list->ptr_in_data = in_data_array;

  /*
   * Copy buf and len parameters from descriptors to the in_data's
   */
  decipher_desc = (T_desc*)decipher_req->desc_list.first;
  cnt = 0;
  while (decipher_desc)
  {
    in_data_list->ptr_in_data[cnt].buf = (U32)decipher_desc->buffer;
    in_data_list->ptr_in_data[cnt].len = decipher_desc->len;

    decipher_desc = (T_desc*)decipher_desc->next;  
    cnt++;
  }
  in_data_list->c_in_data = cnt;  
  
} /* llc_copy_dl_data_to_list */

/*
+------------------------------------------------------------------------------
| Function    : llc_fbs_enable_cci_info_trace
+------------------------------------------------------------------------------
| Description : The llc_fbs_enable_cci_info_trace sets the variable 
|               llc_data->fbs.cci_info_trace to TRUE 
|
| Parameters  : void
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_fbs_enable_cci_info_trace ( void )
{ 
  TRACE_FUNCTION( "llc_fbs_enable_cci_info_trace" );
  llc_data->fbs.cci_info_trace = TRUE;
}

#ifdef LLC_TRACE_CIPHERING
/*
+------------------------------------------------------------------------------
| Function    : llc_trace_desc_list3_content
+------------------------------------------------------------------------------
| Description : traces content of a desc3 descriptor list
|
| Parameters  : desc_list3 descriptor list
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_trace_desc_list3_content(T_desc_list3 desc_list3)
{
  U16 current_pos = 0;  
  U16 dif;  
  U16 data_len = 0;     /* The length of the data to be traced including data offset */
  U8* p_data = NULL;    /* Pointer to byte data element */
  T_desc3* p_desc3 = (T_desc3*)desc_list3.first; /* Pointer to the actual desc3 descriptor element */

  while(p_desc3 != NULL)
  {
    current_pos = p_desc3->offset;
    dif=0;
    p_data = (U8*)p_desc3->buffer;
    data_len = current_pos + p_desc3->len;
    while(current_pos < data_len)
    {
      if (current_pos +8 <= data_len)
      {
        TRACE_EVENT_P8
          ("%02x %02x %02x %02x %02x %02x %02x %02x",
            p_data[current_pos],
            p_data[current_pos+1],
            p_data[current_pos+2],
            p_data[current_pos+3],
            p_data[current_pos+4],
            p_data[current_pos+5],
            p_data[current_pos+6],
            p_data[current_pos+7]
          );
        current_pos += 8;
      }
      else
      {
        dif = data_len - current_pos;
        switch(dif)
        {
          case 1:
            TRACE_EVENT_P1
            ("%02x",
              p_data[current_pos]
            );
            current_pos += 1;
            break;
          case 2:
            TRACE_EVENT_P2
            ("%02x %02x",
              p_data[current_pos],
              p_data[current_pos+1]
            );
            current_pos += 2;
            break;
          case 3:
            TRACE_EVENT_P3
            ("%02x %02x %02x",
              p_data[current_pos],
              p_data[current_pos+1],
              p_data[current_pos+2]
            );
            current_pos += 3;
            break;
          case 4:
            TRACE_EVENT_P4
            ("%02x %02x %02x %02x",
              p_data[current_pos],
              p_data[current_pos+1],
              p_data[current_pos+2],
              p_data[current_pos+3]
            );
            current_pos += 4;
            break;
          case 5:
            TRACE_EVENT_P5
            ("%02x %02x %02x %02x %02x",
              p_data[current_pos],
              p_data[current_pos+1],
              p_data[current_pos+2],
              p_data[current_pos+3],
              p_data[current_pos+4]
            );
            current_pos += 5;
            break;
          case 6:
            TRACE_EVENT_P6
            ("%02x %02x %02x %02x %02x %02x",
              p_data[current_pos],
              p_data[current_pos+1],
              p_data[current_pos+2],
              p_data[current_pos+3],
              p_data[current_pos+4],
              p_data[current_pos+5]
            );
            current_pos += 6;
            break;
          case 7:
            TRACE_EVENT_P7
            ("%02x %02x %02x %02x %02x %02x %02x",
              p_data[current_pos],
              p_data[current_pos+1],
              p_data[current_pos+2],
              p_data[current_pos+3],
              p_data[current_pos+4],
              p_data[current_pos+5],
              p_data[current_pos+6]
            );
            current_pos += 7;
            break;
        }  

      }
    }

    p_desc3 = (T_desc3*)p_desc3->next;
  }
}

/*
+------------------------------------------------------------------------------
| Function    : llc_trace_sdu
+------------------------------------------------------------------------------
| Description : traces content of one sdu
|
| Parameters  : pointer to sdu
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_trace_sdu(T_sdu* sdu)
{

  USHORT pos = sdu->o_buf >> 3;
  USHORT  frame_len = (sdu->l_buf + 7) / 8;


  TRACE_FUNCTION("llc_trace_sdu");

  while(pos < (frame_len + (sdu->o_buf >> 3)))
  {
    if (pos + 8 <= (frame_len + (sdu->o_buf >> 3))) {
      TRACE_EVENT_P8
        ("%02x %02x %02x %02x %02x %02x %02x %02x",
          sdu->buf[pos],
          sdu->buf[pos + 1],
          sdu->buf[pos + 2],
          sdu->buf[pos + 3],
          sdu->buf[pos + 4],
          sdu->buf[pos + 5],
          sdu->buf[pos + 6],
          sdu->buf[pos + 7]
        );
      pos += 8;
    } else if (pos + 7 <= (frame_len + (sdu->o_buf >> 3))){
      TRACE_EVENT_P7
        ("%02x %02x %02x %02x %02x %02x %02x",
          sdu->buf[pos],
          sdu->buf[pos + 1],
          sdu->buf[pos + 2],
          sdu->buf[pos + 3],
          sdu->buf[pos + 4],
          sdu->buf[pos + 5],
          sdu->buf[pos + 6]
        );
      pos += 7;
    } else if (pos + 6 <= (frame_len + (sdu->o_buf >> 3))){
      TRACE_EVENT_P6
        ("%02x %02x %02x %02x %02x %02x",
          sdu->buf[pos],
          sdu->buf[pos + 1],
          sdu->buf[pos + 2],
          sdu->buf[pos + 3],
          sdu->buf[pos + 4],
          sdu->buf[pos + 5]
        );
      pos += 6;
    } else if (pos + 5 <= (frame_len + (sdu->o_buf >> 3))){
      TRACE_EVENT_P5
        ("%02x %02x %02x %02x %02x",
          sdu->buf[pos],
          sdu->buf[pos + 1],
          sdu->buf[pos + 2],
          sdu->buf[pos + 3],
          sdu->buf[pos + 4]
        );
      pos += 5;
    } else if (pos + 4 <= (frame_len + (sdu->o_buf >> 3))){
      TRACE_EVENT_P4
        ("%02x %02x %02x %02x",
          sdu->buf[pos],
          sdu->buf[pos + 1],
          sdu->buf[pos + 2],
          sdu->buf[pos + 3]
        );
      pos += 4;
    } else if (pos + 3 <= (frame_len + (sdu->o_buf >> 3))){
      TRACE_EVENT_P3
        ("%02x %02x %02x",
          sdu->buf[pos],
          sdu->buf[pos + 1],
          sdu->buf[pos + 2]
        );
      pos += 3;
    } else if (pos + 2 <= (frame_len + (sdu->o_buf >> 3))){
      TRACE_EVENT_P2
        ("%02x %02x",
          sdu->buf[pos],
          sdu->buf[pos + 1]
        );
      pos += 2;
    } else if (pos + 1 <= (frame_len + (sdu->o_buf >> 3))){
      TRACE_EVENT_P1
        ("%02x",
          sdu->buf[pos]
        );
      pos++;
    }

  }
}

/*
+------------------------------------------------------------------------------
| Function    : llc_trace_desc_list
+------------------------------------------------------------------------------
| Description : traces content of one desc_list
|
| Parameters  : pointer to desc_list
|
+------------------------------------------------------------------------------
*/
GLOBAL void llc_trace_desc_list(T_desc_list* desc_list)
{
  USHORT  frame_len = desc_list->list_len;
  T_desc* desc = (T_desc*)desc_list->first;
  USHORT  list_pos = 0;
  USHORT  desc_pos = 0;

  TRACE_FUNCTION("llc_trace_desc_list");

  while(list_pos < frame_len)
  {
    if (desc != NULL) {
      if (desc_pos >= desc->len) {
        desc_pos = 0;
        desc = (T_desc*)desc->next;
      }
    }
    if (desc == NULL) {
      return;
    }
    if (desc_pos + 8 <= desc->len) {
      TRACE_EVENT_P8 ("%02x %02x %02x %02x %02x %02x %02x %02x ",
                      desc->buffer[desc_pos],
                      desc->buffer[desc_pos + 1],
                      desc->buffer[desc_pos + 2],
                      desc->buffer[desc_pos + 3],
                      desc->buffer[desc_pos + 4],
                      desc->buffer[desc_pos + 5],
                      desc->buffer[desc_pos + 6],
                      desc->buffer[desc_pos + 7]
                     );
      list_pos+= 8;
      desc_pos+= 8;
    } else if (desc_pos + 7 <= desc->len) {
      TRACE_EVENT_P7 ("%02x %02x %02x %02x %02x %02x %02x ",
                      desc->buffer[desc_pos],
                      desc->buffer[desc_pos + 1],
                      desc->buffer[desc_pos + 2],
                      desc->buffer[desc_pos + 3],
                      desc->buffer[desc_pos + 4],
                      desc->buffer[desc_pos + 5],
                      desc->buffer[desc_pos + 6]
                     );
      list_pos+= 7;
      desc_pos+= 7;
    } else if (desc_pos + 6 <= desc->len) {
      TRACE_EVENT_P6 ("%02x %02x %02x %02x %02x %02x ",
                      desc->buffer[desc_pos],
                      desc->buffer[desc_pos + 1],
                      desc->buffer[desc_pos + 2],
                      desc->buffer[desc_pos + 3],
                      desc->buffer[desc_pos + 4],
                      desc->buffer[desc_pos + 5]
                     );
      list_pos+= 6;
      desc_pos+= 6;
    } else if (desc_pos + 5 <= desc->len) {
      TRACE_EVENT_P5 ("%02x %02x %02x %02x %02x ",
                      desc->buffer[desc_pos],
                      desc->buffer[desc_pos + 1],
                      desc->buffer[desc_pos + 2],
                      desc->buffer[desc_pos + 3],
                      desc->buffer[desc_pos + 4]
                     );
      list_pos+= 5;
      desc_pos+= 5;
    } else if (desc_pos + 4 <= desc->len) {
      TRACE_EVENT_P4 ("%02x %02x %02x %02x ",
                      desc->buffer[desc_pos],
                      desc->buffer[desc_pos + 1],
                      desc->buffer[desc_pos + 2],
                      desc->buffer[desc_pos + 3]
                     );
      list_pos+= 4;
      desc_pos+= 4;
    } else if (desc_pos + 3 <= desc->len) {
      TRACE_EVENT_P3 ("%02x %02x %02x ",
                      desc->buffer[desc_pos],
                      desc->buffer[desc_pos + 1],
                      desc->buffer[desc_pos + 2]
                     );
      list_pos+= 3;
      desc_pos+= 3;
    } else if (desc_pos + 2 <= desc->len) {
      TRACE_EVENT_P2 ("%02x %02x ",
                      desc->buffer[desc_pos],
                      desc->buffer[desc_pos + 1]
                     );
      list_pos+= 2;
      desc_pos+= 2;
    } else if (desc_pos + 1 <= desc->len) {
      TRACE_EVENT_P1 ("%02x ",
                      desc->buffer[desc_pos]
                     );
      list_pos++;
      desc_pos++;
    }
  } /* while(list_pos < frame_len) */
}
#endif /*LLC_TRACE_CIPHERING */