diff src/g23m-gsm/dl/dl_state.c @ 1:fa8dc04885d8

src/g23m-*: import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 16 Oct 2020 06:25:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/g23m-gsm/dl/dl_state.c	Fri Oct 16 06:25:50 2020 +0000
@@ -0,0 +1,2765 @@
+/*
++-----------------------------------------------------------------------------
+|  Project :  GSM-PS
+|  Modul   :  DL_STATE
++-----------------------------------------------------------------------------
+|  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 defines the state machine of the component DL
+|             (replaces the old channel dependent implementation)
++-----------------------------------------------------------------------------
+*/
+
+#ifndef DL_STATE_C
+#define DL_STATE_C
+
+#define ENTITY_DL
+#define NEW_REJ_ACK /* Acknowledgement by valid reject frame
+                   * in accordance with 3GPP 04.06, 5.5.3.1
+                   * "On receipt of a valid I frame or supervisory frame"
+                   */
+
+/*==== INCLUDES ===================================================*/
+
+#include "typedefs.h"
+#include <string.h>
+#include "vsi.h"
+#include "pconst.cdg"
+#include "custom.h"
+#include "gsm.h"
+#include "mon_dl.h"
+#include "prim.h"
+#include "pei.h"
+#include "tok.h"
+#include "ccdapi.h"
+#include "dl.h"
+#include "dl_em.h"
+#include "dl_trc.h"
+
+/*==== TYPEDEFS ===================================================*/
+typedef struct
+{
+  UBYTE channel;
+  UBYTE sapi;
+  UBYTE state;
+  UBYTE T200_Stop;
+  UBYTE T200_Start;
+  UBYTE pf_bit_flag;
+  UBYTE dl_data_ind;
+  UBYTE mdl_error_ind;
+  T_CCH*  pcch;
+} T_CCH_INTERN;
+#define T_CCH_INTERN_INIT {0,0,0,0,0,0,0}
+
+/*==== EXPORT =====================================================*/
+/*==== PRIVAT =====================================================*/
+static  int frame_validation (UBYTE channel_type, UBYTE* frame);
+
+static  int downlink_idle (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  int downlink_contention_resolution (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  int downlink_mfe (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  int downlink_timer_recovery (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  int downlink_awaiting_release (T_CCH_INTERN* pcch_i, UBYTE *frame);
+
+static  void downlink_mfe_information (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void downlink_mfe_supervisory (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void downlink_mfe_sabm (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void downlink_mfe_dm (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void downlink_mfe_tr_unnumbered (T_CCH_INTERN* pcch_i, UBYTE *frame, UBYTE state);
+static  void downlink_i_frame (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void downlink_tr_supervisory (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void downlink_tr_information (T_CCH_INTERN* pcch_i, UBYTE *frame);
+
+static  void invoke_retransmission (T_CCH_INTERN* pcch_i, UBYTE frame_nr);
+static  void enquiry_response (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void mdl_error_ind (UBYTE cause, UBYTE channel_type, UBYTE sapi);
+static  void nr_error_recovery (T_CCH_INTERN* pcch_i, UBYTE *frame);
+static  void concatenate (UBYTE ch_type, UBYTE sapi, UBYTE *frame);
+static  void free_sending_buffer (UBYTE ch_type, UBYTE sapi);
+
+static  void repeat_sabm (UBYTE channel, UBYTE sapi);
+static  void delayed_release_ind(UBYTE channel);
+
+static  int uplink_idle (UBYTE channel, UBYTE sapi);
+static  int uplink_awaiting_establishment (UBYTE channel, UBYTE sapi, UBYTE no_signalling_mode);
+static  int uplink_mfe (UBYTE channel, UBYTE sapi, UBYTE no_signalling_mode);
+static  int uplink_timer_recovery (UBYTE channel, UBYTE sapi, UBYTE no_signalling_mode);
+
+static  void T200_expiry (UBYTE channel, UBYTE sapi);
+
+/*==== VARIABLES ==================================================*/
+
+/*Removed the const from the definition,rework for issue 25370*/
+static  UBYTE l2_empty_frame [25] = {
+        0x00, 0x00, /* the first two dummy bytes only for SACCH L1 header! */
+        /* here begins the normal empty frame (SDCCH, FACCH) */
+        0x01, /* address field: SAPI 0 */
+        0x03, /* control field: UI frame */
+        0x01, /* length field: length = 0 */
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b };
+#if 0
+static  UBYTE l2_invalid_frame [25] = {
+        0x00, 0x00, /* the first two dummy bytes only for SACCH L1 header! */
+        /* here begins the normal empty frame (SDCCH, FACCH) */
+        0x1D, /* address field: SAPI 7 (unallocated SAPI; no action shall be taken on such frames) */
+        0x03, /* control field: UI frame */
+        0x01, /* length field: length = 0 */
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+        0x2b, 0x2b, 0x2b, 0x2b, 0x2b };
+static  UBYTE l2_invalid_frame_0 [25] = {
+        0x00, 0x00, /* the first two dummy bytes only for SACCH L1 header! */
+        /* here begins the normal empty frame (SDCCH, FACCH) */
+        0x00, /* address field: SAPI 0 */
+        0x00, /* control field: UI frame */
+        0x00, /* length field: length = 0 */
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00 };
+#endif /* 0|1 */
+static  T_CCH_INTERN  cch_i;
+
+#if defined(CHECK_PCCHI)
+#define CHECK_PCCH_I()  if (check_pcch_i(pcch_i, __LINE__)()return;
+#define CHECK_PCCH_Ir() if (check_pcch_i(pcch_i, __LINE__)()return -1;
+#else
+#define CHECK_PCCH_I()
+#define CHECK_PCCH_Ir()
+#endif  /* CHECK_PCCHI */
+
+/*==== FUNCTIONS ==================================================*/
+int dl_downlink (UBYTE error_flag, UBYTE channel_type, UBYTE* frame, ULONG fn)
+{
+  UBYTE     channel_state = STATE_INVALID;
+  UBYTE     channel = NOT_PRESENT_8BIT;
+  UBYTE     frame_sapi = PS_SAPI_0; /* to calm lint, will be new set for valid frames */
+  static UBYTE  invalid = 0;
+  int       ret = -2;
+  int       cause;
+  int       l2_offset;
+
+   GET_INSTANCE_DATA;
+
+  TRACE_EVENT_WIN_P1 ("downlink(): dcch0_ch_type:%s",
+    CH_TYPE_NAME[dl_data->dcch0_ch_type]);
+  TRACE_EVENT_WIN_P6 ("DL: DCCH0=%s,%s vr=%u vs=%u va=%u T200=%u",
+    CH_TYPE_NAME[dl_data->cch[C_DCCH0].ch_type],
+    STATE_DCCH0_NAME[dl_data->state[C_DCCH0]],
+    dl_data->cch[C_DCCH0].vr, dl_data->cch[C_DCCH0].vs, dl_data->cch[C_DCCH0].va,
+    dl_data->cch[C_DCCH0].T200_counter);
+  TRACE_EVENT_WIN_P6 ("DL: DCCH3=%s,%s vr=%u vs=%u va=%u T200=%u",
+    CH_TYPE_NAME[dl_data->cch[C_DCCH3].ch_type],
+    STATE_DCCH3_NAME[dl_data->state[C_DCCH3]],
+    dl_data->cch[C_DCCH3].vr, dl_data->cch[C_DCCH3].vs, dl_data->cch[C_DCCH3].va,
+    dl_data->cch[C_DCCH3].T200_counter);
+
+  if (channel_type EQ L2_CHANNEL_SACCH)
+  {
+    l2_offset = 2; /* with layer 1 header */
+  }
+  else
+  {
+    l2_offset = 0; /* without layer 1 header */
+  }
+
+#define RR_SHORT_PD_HANDLING
+#if defined(RR_SHORT_PD_HANDLING)
+  /* Handling of unacknowledged UI frames with format type Bter */
+#if defined(RR_SHORT_PD_DETECT_KNOWN_MSG_ONLY) /* detection of known messages only */
+  cause = 0;
+  switch (frame[l2_offset])
+  {
+    case RR_SHORT_PD_SI10:
+    case RR_SHORT_PD_MEAS_INFO:
+      if (channel_type EQ L2_CHANNEL_SACCH)
+        cause = 1;
+      break;
+    case RR_SHORT_PD_NOTI_FACCH:
+      if ((channel_type EQ L2_CHANNEL_FACCH_F) OR
+          (channel_type EQ L2_CHANNEL_FACCH_H))
+          cause = 1;
+      break;
+    case RR_SHORT_PD_UPLINK_FREE:
+      cause = 1;
+      break;
+    default:
+      break;
+  }
+  if (cause)
+  {
+    drr_dl_short_unitdata_ind (channel_type, error_flag, frame,
+      &frame[l2_offset],
+      (channel_type EQ L2_CHANNEL_SACCH) ? DL_N201_SACCH_Bter : DL_N201_DCCH_Bter, fn);
+    return 0;
+  }
+#else /* detection of all possible messages with short L2 header and format Bter */
+  TRACE_EVENT_WIN_P4 ("detection of format Bter: %02x&%02x=%02x ?= %02x",
+    frame[l2_offset], BTER_FORMAT_MASK, GET_BTER_FORMAT (&frame[l2_offset]), SHORT_L2_HEADER_TYPE_1);
+  if ((GET_BTER_FORMAT (&frame[l2_offset]) EQ SHORT_L2_HEADER_TYPE_1))
+  {
+    drr_dl_short_unitdata_ind (channel_type, error_flag, frame,
+      &frame[l2_offset],
+      (UBYTE)((channel_type EQ L2_CHANNEL_SACCH) ? DL_N201_SACCH_Bter : DL_N201_DCCH_Bter), fn);
+    return 0;
+  }
+#endif /* kind of Bter detection */
+#endif /* RR_SHORT_PD_HANDLING */
+
+  /* check frame */
+
+  if (error_flag EQ VALID_BLOCK)
+  {
+    frame_sapi = GET_SAPI (frame+l2_offset);
+    if ((frame_sapi NEQ PS_SAPI_0) AND (frame_sapi NEQ PS_SAPI_3))
+    {
+      TRACE_EVENT_WIN_P1 ("downlink() returns -1 (wrong SAPI=%u)", frame_sapi);
+      return -1;
+    }
+
+    cause = frame_validation (channel_type, frame+l2_offset);
+    if (cause >= 0)
+    {
+      TRACE_FUNCTION ("frame validation failed!");
+      if (invalid EQ 0)
+      {
+        TRACE_ERROR ("invalid frame");
+        invalid = 1; /* only one message per succession */
+      }
+      mdl_error_ind ((UBYTE)cause, channel_type, frame_sapi);
+      return 0;/* ETSI GSM 04.06 Annex G.2 - G.4 */
+    }
+    else
+      invalid = 0;
+
+#if defined(DL_2TO1) || defined(_SIMULATION_)
+    /* Handling of unacknowledged UI frames on SACCH with SAPI=0 (not format type Bter) */
+    if ((channel_type EQ L2_CHANNEL_SACCH) AND
+        (frame_sapi EQ PS_SAPI_0) AND
+        (GET_FORMAT_TYPE(frame+l2_offset) EQ U_FORMAT) AND
+        (GET_U_TYPE(frame+l2_offset) EQ UI_FRAME) )
+    {
+      drr_dl_unitdata_ind (error_flag, frame, frame+l2_offset+3,
+                           (UBYTE)(GET_LENGTH_INDICATOR (frame+l2_offset)), fn);
+      return 0;
+    }
+#endif /* DL_2TO1 || _SIMULATION_ */
+  }
+#if defined(DL_2TO1)
+  else if (channel_type EQ L2_CHANNEL_SACCH)
+  { /*
+     * Indicate invalid SACCH frame for decrement of radio link timeout counter.
+     * The invalid frame possible contains invalid headers, use length 0.
+     */
+    drr_dl_unitdata_ind (error_flag, frame, frame+l2_offset+3, 0, fn);
+  }
+#endif /* DL_2TO1 */
+
+  if (error_flag NEQ VALID_BLOCK)
+  {
+    TRACE_EVENT_WIN ("invalid frame->stop download handling");
+    return 0; /* no further handling */
+  }
+
+  memset (&cch_i, 0, sizeof (T_CCH_INTERN));
+  switch (channel_type)
+  {
+  case L2_CHANNEL_SDCCH:
+    if (frame_sapi EQ PS_SAPI_3)
+    {
+      channel = C_DCCH3;
+      break;
+    }
+    /*lint -fallthrough*/
+  case L2_CHANNEL_FACCH_F:
+  case L2_CHANNEL_FACCH_H:
+    if (frame_sapi EQ PS_SAPI_0)
+      channel = C_DCCH0;
+    break;
+  case L2_CHANNEL_SACCH:
+    if (frame_sapi EQ PS_SAPI_3)
+    { /* SACCH with SAPI=3 only supported with associated TCH (FACCH) */
+      if ((dl_data->cch[C_DCCH0].ch_type EQ L2_CHANNEL_FACCH_H) OR
+          (dl_data->cch[C_DCCH0].ch_type EQ L2_CHANNEL_FACCH_F))
+        channel = C_DCCH3;
+    }
+    else
+    {
+      TRACE_EVENT_WIN ("No handling of SACCH with SAPI=0 here!");
+      /*
+       * The SACCH with SAPI=0 is handled some lines before for DL_2TO1 and
+       * simulation. Handling of frames other than UI frames is not supported.
+       */
+    }
+    break;
+  default:
+    break;
+  }/* endswitch channel_type */
+
+  if (channel EQ NOT_PRESENT_8BIT)
+  {
+    TRACE_EVENT_WIN ("downlink() returns -3");
+    return -3;
+  }
+  else
+  {
+
+#if defined(DELAYED_SABM)
+    /* ignore downlinked frames before delayed SABM was sent */
+    if ((channel EQ C_DCCH0) AND
+        (dl_data->dcch0_sabm_flag NEQ NOT_PRESENT_8BIT))
+    {
+      DL_OFFLINE_TRACE (TRACE_DL_EVENT, channel, channel_type, "DL:pend.SABM->ignore");
+      return 0;
+    }
+#endif  /* DELAYED_SABM */
+
+    cch_i.channel = channel;
+    cch_i.sapi = frame_sapi;
+#if defined(_SIMULATION_)
+    if (channel_type EQ L2_CHANNEL_SACCH)
+    {
+      TRACE_EVENT_WIN_P1 ("SACCH: set SAPI=%u set during downlink", frame_sapi);
+    }
+#endif /* _SIMULATION_ */
+
+    cch_i.state = channel_state = dl_data->state[channel];
+    cch_i.pcch = &dl_data->cch[channel];
+    /*
+     * The channel type pcch->ch_type is overwritten as input to the downlink
+     * sub functions. In case of DCCH0 this is temporary only and the channel
+     * type have to be re-assigned with the value of dl_data->dcch0_ch_type.
+     */
+    cch_i.pcch->ch_type = channel_type;
+    if (cch_i.pcch->vtx NEQ EMPTY_CMD)
+    { /* save bit for the case of unsolicited frames */
+      cch_i.pf_bit_flag = cch_i.pcch->f_bit;
+    }
+    TRACE_EVENT_WIN_P4 ("downlink()  in:%s SAPI=%u st=%u vtx=%s",
+      CH_TYPE_NAME[channel_type], frame_sapi, channel_state,
+      VTX_NAME[cch_i.pcch->vtx]);
+    TRACE_EVENT_WIN_P9 ("vr=%u vs=%u va=%u rc=%u contres=%u reje=%u ackp=%u %c=%u",
+      cch_i.pcch->vr, cch_i.pcch->vs, cch_i.pcch->va, cch_i.pcch->rc,
+      cch_i.pcch->contention_resolution, cch_i.pcch->reject_exception,
+      cch_i.pcch->acknowledge_pending,
+      cch_i.pcch->time_flag ? 'T' : 't', cch_i.pcch->T200_counter);
+  }
+
+  switch (channel_state)
+  {
+    case STATE_DISABLED:
+    case STATE_IDLE_DL:
+    ret = downlink_idle (&cch_i, frame+l2_offset);
+    break;
+  case STATE_CONTENTION_RESOLUTION:
+    ret = downlink_contention_resolution (&cch_i, frame+l2_offset);
+    break;
+  case STATE_MULTIPLE_FRAME_ESTABLISHED:
+    ret = downlink_mfe (&cch_i, frame+l2_offset);
+    break;
+    case STATE_TIMER_RECOVERY:
+    ret = downlink_timer_recovery (&cch_i, frame+l2_offset);
+    break;
+  case STATE_AWAITING_RELEASE:
+    ret = downlink_awaiting_release (&cch_i, frame+l2_offset);
+    break;
+  }/* endswitch channel_state */
+  if (channel EQ C_DCCH0)
+  { /*
+     * Reconstruct the temporary overwritten pcch->ch_type with the value
+     * of dl_data->dcch0_ch_type.
+     */
+    cch_i.pcch->ch_type = dl_data->dcch0_ch_type;
+  }
+
+
+  TRACE_EVENT_WIN_P5 ("%s SAPI=%u vtx=%s (%s#%u)", CH_TYPE_NAME[cch_i.pcch->ch_type], cch_i.sapi,
+                                            VTX_NAME[cch_i.pcch->vtx],
+                                            __FILE10__, __LINE__);
+  if (ret NEQ 0)
+  {
+    TRACE_EVENT_WIN_P1 ("downlink() returns %d", ret);
+    return ret;
+  }
+
+  /* transfer states and flags to dl_data */
+  dl_data->cch[channel].f_bit_flag = dl_data->cch[channel].f_bit = cch_i.pf_bit_flag;
+  if (cch_i.T200_Start)
+  {
+    dl_data->cch[channel].T200_counter = T200_STOPPED;
+    dl_data->cch[channel].time_flag = TRUE;
+  }
+  else if (cch_i.T200_Stop)
+  {
+    dl_data->cch[channel].T200_counter = T200_STOPPED;
+    dl_data->cch[channel].time_flag = FALSE;
+  }
+
+  if (cch_i.dl_data_ind)
+  {
+    com_data_ind(channel_type, frame_sapi, fn);
+  }
+
+  if (channel_state NEQ cch_i.state)
+  {
+    set_channel_state (
+      (UBYTE)((frame_sapi EQ PS_SAPI_0) ? C_DCCH0 : C_DCCH3), cch_i.state);
+  }
+
+  if (cch_i.mdl_error_ind)
+  {
+    mdl_error_ind ( cch_i.mdl_error_ind, channel_type, frame_sapi);
+  }
+
+
+
+  TRACE_EVENT_WIN_P4 ("downlink() out:%s SAPI=%u st=%u vtx=%s",
+    CH_TYPE_NAME[channel_type], cch_i.sapi, cch_i.state,
+    VTX_NAME[cch_i.pcch->vtx]);
+  TRACE_EVENT_WIN_P9 ("vr=%u vs=%u va=%u rc=%u contres=%u reje=%u ackp=%u %c=%u",
+    cch_i.pcch->vr, cch_i.pcch->vs, cch_i.pcch->va, cch_i.pcch->rc,
+    cch_i.pcch->contention_resolution, cch_i.pcch->reject_exception,
+    cch_i.pcch->acknowledge_pending,
+    cch_i.pcch->time_flag ? 'T' : 't', cch_i.pcch->T200_counter);
+  TRACE_EVENT_WIN_P4 ("T200=%s  %s  %s  %s",
+    cch_i.T200_Start ? "Start" : cch_i.T200_Stop ? "Stop" : "...",
+    cch_i.pf_bit_flag ? "P/F" : "",
+    cch_i.dl_data_ind ? "DATA_IND" : "", cch_i.mdl_error_ind ? "ERROR_IND" : "");
+  TRACE_EVENT_WIN_P1 ("downlink() returns %d", ret);
+
+  return ret;
+}/* endfunc downlink */
+
+LOCAL int frame_validation (UBYTE channel_type, UBYTE* frame)
+{
+  UBYTE frame_length;
+  BOOL  frame_m_bit;
+  UBYTE N201;
+
+  if (!GET_EA (frame))
+    return FRAME_NOT_IMPLEMENTED; /* ETSI GSM 04.06 Annex G.2.3 */
+
+  if (!GET_EL (frame))
+    return FRAME_NOT_IMPLEMENTED; /* ETSI GSM 04.06 Annex G.4.1 */
+
+  frame_length = GET_LENGTH_INDICATOR (frame);
+  frame_m_bit = GET_M_BIT (frame);
+
+  /* get the maximal number of octets */
+  switch (channel_type)
+  {
+  case L2_CHANNEL_SDCCH:
+    N201 = N201_SDCCH;
+    break;
+  case L2_CHANNEL_SACCH:
+    N201 = N201_SACCH;
+    break;
+  default:/* CH_TYPE_FACCH_FR, L2_CHANNEL_FACCH_H */
+    N201 = N201_FACCH;
+    break;
+  }
+
+  switch (GET_FORMAT_TYPE (frame))
+  {
+  case I_FORMAT: /* I format */
+  case I1_FORMAT:
+    if ((frame_length > N201) OR (frame_length EQ 0))
+      return I_FRAME_WITH_INCORRECT_LENGTH; /* ETSI GSM 04.06 Annex G.4.2 */
+    if ((frame_length < N201) AND (frame_m_bit EQ 1))
+      return I_FRAME_WITH_INCORRECT_USE_OF_M_BIT; /* ETSI GSM 04.06 Annex G.4.2 */
+    break;
+  case S_FORMAT: /* S format */
+    if (frame_length OR frame_m_bit)
+      return S_FRAME_WITH_INCORRECT_PARAMETERS; /* ETSI GSM 04.06 Annex G.4.3 */
+    if ((frame[1] & 0x0f) EQ 0x0d)
+      return FRAME_NOT_IMPLEMENTED; /* ETSI GSM 04.06 Annex G.3.1 */
+    break;
+  case U_FORMAT: /* U format */
+    switch (GET_U_TYPE (frame))
+    {
+    case DM_FRAME:
+    case DISC_FRAME:
+      if (frame_length OR frame_m_bit)
+        return U_FRAME_WITH_INCORRECT_PARAMETERS; /* ETSI GSM 04.06 Annex G.4.4 */
+      break;
+    case UA_FRAME:
+    case SABM_FRAME:
+    case UI_FRAME:
+      if ((frame_length > N201) OR (frame_m_bit))
+        return U_FRAME_WITH_INCORRECT_PARAMETERS; /* ETSI GSM 04.06 Annex G.4.5 */
+      break;
+    default:
+      return FRAME_NOT_IMPLEMENTED; /* ETSI GSM 04.06 Annex G.3.2 */
+      /*break;*/
+    }/* endswitch U frame_type */
+    break;
+  }/* endswitch frame_format */
+
+  /*
+   * ETSI GSM 04.06 Annex G.2.1, G.2.2 will be check in the following functions
+   */
+
+  return -1; /* frame is valid */
+}/* endfunc frame_validation */
+
+static  int downlink_idle( T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  /*
+   * According to 3GPP TS 04.05, 5.4.5 Idle state:
+   * While in the idle state:
+   * - the receipt of a DISC command shall result in the transmission of a
+   *   DM response with the F bit set to the value of the received P bit;
+   * - the receipt of an I frame or supervisory frame with the P bit set to "1"
+   *   shall result in the transmission of a DM response with the F bit set to
+   *   "1" (as defined in subclause 5.2.2);
+   * - the content of any received I frame shall be discarded;
+   * - on receipt of an SABM command, the procedures defined in subclause 5.4.1
+   *   shall be followed;
+   * - on receipt of UI commands, the procedures defined in subclause 5.3 shall
+   *   be followed;
+   * - all other frame types shall be discarded.
+  */
+  T_CCH*  pcch;
+
+  CHECK_PCCH_Ir();
+  pcch = pcch_i->pcch;
+
+  TRACE_FUNCTION ("downlink_idle()");
+
+  switch (GET_FORMAT_TYPE (frame))
+  {
+  case S_FORMAT: /* S frame */
+    if (GET_S_TYPE (frame) NEQ RR_CMD)
+      break;
+    /*lint -fallthrough*/
+  case I_FORMAT: /* I frame */
+  case I1_FORMAT:
+    if (GET_P_BIT (frame))
+    {
+      pcch->vtx = DM_CMD;
+      pcch_i->pf_bit_flag = TRUE;
+    }
+    break;
+  case U_FORMAT: /* U frame */
+    if (GET_CR (frame))
+    { /* command */
+      switch (GET_U_TYPE (frame))
+      {
+      case SABM_FRAME:
+        if (!GET_LENGTH_INDICATOR (frame) AND pcch_i->sapi EQ PS_SAPI_3)
+        { /*
+           * Mobile Terminated Establishment, but only for SAPI=3!
+           *
+           * According to 3GPP TS 04.06, 5.4.1 Establishment of multiple frame
+           * operation, 5.4.1.1 General, Note:
+           * For SAPI 0 the data link is always established by the MS.
+           */
+          com_restore_queue ( pcch_i->sapi, NULL);
+          pcch->vtx = UA_CMD;
+          pcch_i->pf_bit_flag = GET_P_BIT (frame);
+          pcch->va    = 0;
+          pcch->vr    = 0;
+          pcch->vs    = 0;
+          pcch->rc    = 0;
+        }
+        break;
+      case DISC_FRAME:
+        pcch->vtx   = DM_CMD;
+        pcch_i->pf_bit_flag = GET_P_BIT (frame);
+        break;
+      case UI_FRAME:
+        /* drr_dl_unitdata_ind() was called in the main downlink function */
+        break;
+      default:
+        break;
+      }
+    }
+    break;
+  default:
+    break;
+  }
+
+  return 0;
+}/* endfunc downlink_idle */
+
+static  int downlink_contention_resolution(T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  GET_INSTANCE_DATA;
+  UBYTE frame_type;
+  UBYTE frame_format;
+  UBYTE frame_cr;
+
+  UBYTE establish_cnf = FALSE;
+  UBYTE release_ind = FALSE;
+  UBYTE release_ind_cs = NOT_PRESENT_8BIT;
+
+  T_CCH*  pcch;
+
+  CHECK_PCCH_Ir();
+  pcch = pcch_i->pcch;
+
+  TRACE_FUNCTION ("downlink_contention_resolution()");
+
+  frame_format = GET_FORMAT_TYPE (frame);
+  if (frame_format EQ U_FORMAT)
+  { /* U frame */
+    frame_cr = GET_CR (frame);
+    frame_type = GET_U_TYPE (frame);
+    if (frame_cr)
+    { /* command */
+      switch (frame_type)
+      {
+      case SABM_FRAME:
+        if (pcch_i->sapi EQ PS_SAPI_3)
+        { /* DCCH3 */
+          /*
+           * According to 3GPP TS 04.06, 5.4.6.1:
+           * SAPI = 3, Collision of unnumbered commands and responses.
+           * Collision situations (Identical transmitted and received commands)
+           * shall be resolved in the following way:  If the transmitted and
+           * received unnumbered commands (SABM or DISC) are the same, the data
+           * link layer entities shall send the UA response at the earliest
+           * possible opportunity. The indicated state shall be entered after
+           * receiving the UA response. The data link layer entities shall each
+           * notify its respective layer 3 entity by means of the appropriate
+           * confirm primitive, i.e. DL-ESTABLISH-CONFIRM or DL-RELEASE-CONFIRM.
+           */
+          if (!GET_LENGTH_INDICATOR (frame))
+          {
+            com_clear_queue (PS_SAPI_3);
+            /* establish_cnf = TRUE; cnf will be sent at uplink opportunity */
+
+            pcch_i->pf_bit_flag = GET_P_BIT (frame);
+            pcch->vtx = UA_CMD;
+            pcch->va    = 0;
+            pcch->vr    = 0;
+            pcch->vs    = 0;
+            pcch->rc    = 0;
+            pcch_i->T200_Stop = TRUE;
+          }
+          else
+          {
+            /* no contention resolution procedure with SAPI=3! */
+          }
+        }
+        else
+        {/* DCCH0 */
+          /*
+           * According to 3GPP TS 04.06, 5.4.1.1 General:
+           * NOTE: SAPI=0 the data link is always established by the MS!
+           *
+           * According to 3GPP TS 04.06, 5.4.1.4 Contention resolution
+           * establishment procedure:
+           * All frames other than unnumbered frame formats received for the
+           * SAPI in use during the establishment procedures shall be ignored.
+           * The reception of unnumbered frames other than UA is treated as
+           * specified for the normal establishment case.
+           * NOTE 4: In fact, there are no foreseen cases in which the network
+           *         will send SABM, DISC or DM, but for sake of completeness
+           *         these occurrences are specified and must be treated.
+           */
+          establish_cnf = TRUE;/* Treated as normal establishment case */
+          pcch_i->pf_bit_flag = GET_P_BIT (frame);
+          pcch->vtx = UA_CMD;
+          pcch->va    = 0;
+          pcch->vr    = 0;
+          pcch->vs    = 0;
+          pcch->rc    = 0;
+          pcch_i->T200_Stop = TRUE;
+        }
+        break;
+
+      case DISC_FRAME:
+        {
+          release_ind = TRUE;
+
+          pcch_i->pf_bit_flag = GET_P_BIT (frame);
+          pcch->vtx   = DM_CMD;
+        }
+        break;
+
+      default:
+        break;
+      }/* endswitch command frame_type */
+    }
+    else
+    { /* response */
+      switch (frame_type)
+      {
+      case DM_FRAME:
+        /*
+         * PATCH LE 14.09.99
+         * Ignore DM(F=0) frames
+         */
+        if (GET_P_BIT (frame))
+        {
+          release_ind = TRUE;
+        }
+        break;
+
+      case UA_FRAME:
+        if (pcch_i->sapi EQ PS_SAPI_0)
+        {
+          if (pcch->contention_resolution)
+          {
+            if (com_compare_L3_msg (dl_data->dcch0_queue.switch_buffer, frame))
+            {
+              establish_cnf = TRUE;
+              COM_FREE_QUEUE_BUFFER(&dl_data->dcch0_queue, INDEX_SWITCH_BUFFER);
+            }
+            else
+            {
+              release_ind = TRUE;
+              release_ind_cs = CAUSE_DL_INFO_FIELD_MISMATCH;
+            }
+          }
+          else
+          {
+            if (!GET_LENGTH_INDICATOR (frame))
+            {
+              establish_cnf = TRUE;
+            }
+            else
+            {
+              release_ind = TRUE;
+            }
+          }
+        }/* endif PS_SAPI_0 */
+        else if (pcch_i->sapi EQ PS_SAPI_3)
+        {
+          if (!GET_LENGTH_INDICATOR (frame) OR (pcch->ch_type EQ L2_CHANNEL_SACCH))
+          {
+            establish_cnf = TRUE;
+          }
+          else
+          {
+            release_ind = TRUE;
+          }
+
+        }/* endif PS_SAPI_3 */
+
+        if (establish_cnf AND (pcch_i->sapi EQ PS_SAPI_0))
+        {
+          dcch3_enable(pcch->ch_type);
+        }
+
+        break;/* endbreak UA_FRAME */
+
+      default:
+        break;
+      }/* endswitch response frame_type */
+    }/* endifelse command/response */
+
+    if (establish_cnf)
+    {
+      drr_dl_establish_cnf (pcch->ch_type, pcch_i->sapi);
+      pcch->va = 0;
+      pcch->vr = 0;
+      pcch->vs = 0;
+      pcch_i->T200_Stop = TRUE;
+      pcch_i->state = STATE_MULTIPLE_FRAME_ESTABLISHED;
+    }
+    else if (release_ind)
+    {
+      if (pcch_i->sapi EQ PS_SAPI_0)
+      {
+        if (pcch->contention_resolution)
+        {
+          pcch->contention_resolution = FALSE;
+          COM_FREE_QUEUE_BUFFER(&dl_data->dcch0_queue, INDEX_SWITCH_BUFFER);
+        }
+      }
+
+      drr_dl_release_ind (pcch->ch_type, pcch_i->sapi, release_ind_cs, FALSE);
+      pcch_i->T200_Stop = TRUE;
+      pcch_i->state = STATE_IDLE_DL;
+    }
+
+  }/* endif frame_format == 3 (only unnumbered frames) */
+
+  return 0;
+}/* endfunc downlink_contention_resolution */
+
+static  int downlink_mfe(T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  TRACE_EVENT_WIN ("downlink_mfe()");
+
+  switch (GET_FORMAT_TYPE (frame))
+  {
+  case I_FORMAT:/* I frame */
+  case I1_FORMAT:
+    downlink_mfe_information (pcch_i, frame);
+    break;
+  case S_FORMAT: /* S frame */
+    downlink_mfe_supervisory (pcch_i, frame);
+    break;
+  case U_FORMAT: /* U frame */
+    downlink_mfe_tr_unnumbered (pcch_i, frame,
+                                STATE_MULTIPLE_FRAME_ESTABLISHED);
+    break;
+  default:
+    TRACE_EVENT_WIN ("invalid/unknown frame");
+    break;
+  }
+
+  return 0;
+}/* endfunc downlink_mfe */
+
+static  void downlink_mfe_supervisory (T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  UBYTE frame_cr;
+  UBYTE frame_pollbit;
+  UBYTE frame_nr;
+  UBYTE frame_type;
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  TRACE_EVENT_WIN ("downlink_mfe_supervisory()");
+
+  frame_type = GET_S_TYPE (frame);
+
+  /*
+   * Ignore RNR frame without notification
+   * (ETSI GSM 04.06, section 6.
+   * "Special protocol operation on SAPI=0 and SAPI=3", page 53)
+   */
+  if (frame_type EQ RNR_FRAME)
+    return;
+
+  frame_cr = GET_CR (frame);
+  frame_pollbit = GET_P_BIT (frame);
+  frame_nr = GET_RECEIVE_NUMBER (frame);
+
+  /* in accordance with CCITT Q.921 figure B.7 (sheet 5 to 7 of 10) */
+  if (frame_pollbit)
+  {
+    if (frame_cr)
+    {
+      enquiry_response (pcch_i, frame);
+    }
+    else
+    {
+      mdl_error_ind (UNSOLICITED_SUPERVISORY_RESPONSE, pcch->ch_type, pcch_i->sapi);/* 3GPP TS 04.06, 5.4.2.2 */
+    }
+  }
+
+  if (com_check_nr (pcch->va, pcch->vs, frame_nr))
+  {
+    /*
+     * N(R) check is successfull
+     * in accordance with CCITT Q.921 figure B.7 (sheet 6 and 7 of 10)
+     */
+
+    switch (frame_type)
+    {
+    case RR_FRAME:
+      if (frame_nr EQ pcch->vs)
+      {
+        /* T200 handling under ETSI GSM 04.06 section 5.5.3.1 */
+        if (((8 + frame_nr - pcch->va ) & 7) > 0 /*frame_nr > pcch->va*/)
+          pcch_i->T200_Stop = TRUE;
+
+        pcch->va = frame_nr;
+
+        free_sending_buffer (pcch->ch_type, pcch_i->sapi);
+      }
+      else
+      {
+        if (frame_nr NEQ pcch->va)
+        {
+          pcch->va = frame_nr;
+          pcch_i->T200_Start = TRUE;
+        }
+      }
+      break;
+    case REJ_FRAME:
+      /*
+       * in accordance with ETSI GSM 04.06; chapter 5.5.4.1 i)
+       *
+       * clear existing peer receiver busy condition (not applicable in GSM)
+       */
+
+      /* reset timer T200 */
+      pcch_i->T200_Stop = TRUE;
+      /*
+       * If REJ command with P bit set to 1,
+       * transmit an appropiate supervisory response frame with F bit set to 1
+       */
+      if (frame_pollbit AND frame_cr)
+        enquiry_response (pcch_i, frame);
+      /* transmit the corresponding I frame asap */
+      invoke_retransmission (pcch_i, frame_nr);
+
+
+      /*
+       * set its send state variable V(S) and its acknowledge state
+       * variable V(A) to the value of the N(R) contained in the REJ frame
+       * control field
+       */
+      pcch->vs = pcch->va = frame_nr;
+
+      /*
+       * if it was an REJ response frame with the F bit set to 1, notify
+       * a protocol violation to layer 3 (cause=unsolicited supervisory frame)
+       *
+       * fulfilled at the beginning of this function!
+       */
+      break;
+    case RNR_FRAME:
+      /*
+       * Ignore frame without notification
+       * (ETSI GSM 04.06, section 6.
+       * "Special protocol operation on SAPI=0 and SAPI=3", page 53)
+       */
+      break;
+    default:
+      TRACE_EVENT_WIN ("invalid S frame");  /* GSM 04.06 Annex G.3.1 */
+      mdl_error_ind (FRAME_NOT_IMPLEMENTED, pcch->ch_type, pcch_i->sapi);
+      break;
+    }
+
+  }
+  else
+  {
+    nr_error_recovery (pcch_i, frame);
+  }
+}/* endfunc downlink_mfe_supervisory */
+
+
+static  void downlink_mfe_sabm (T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  TRACE_EVENT_WIN ("downlink_mfe_sabm()");
+
+  if (!GET_LENGTH_INDICATOR (frame))
+  {
+    T_CCH*  pcch;
+
+    CHECK_PCCH_I();
+    pcch = pcch_i->pcch;
+
+    if (pcch_i->sapi EQ PS_SAPI_0)
+    {
+      /* SACCH0: only unacknowledge mode available -> ignore SABM */
+      if (pcch->ch_type EQ L2_CHANNEL_SACCH)
+        return;
+      /*
+       * SDCCH0, FACCH: can not be a normal establishment procedure
+       * because for SAPI=0 the data link is always established by the MS.
+       * Therefore only the V state variables and any exception states
+       * will be reseted.
+       */
+    }
+    else if (pcch_i->sapi EQ PS_SAPI_3)
+    { /*
+       * SDCCH3, SACCH: normal establishment procedure,
+       * might be a re-establishment according to GSM 04.06, 5.4.1.2
+       */
+      com_clear_queue (PS_SAPI_3);
+    }
+
+    /* respond with an unnumbered acknowledgement */
+    pcch->vtx = UA_CMD;
+    /* with the F bit set to the same value as the P bit */
+    pcch_i->pf_bit_flag = GET_P_BIT (frame);
+    /* reset timer T200 */
+    pcch_i->T200_Stop = TRUE;
+    /* reset all state variables (internal sequence counter) */
+    pcch->va = 0;
+    pcch->vr = 0;
+    pcch->vs = 0;
+    /* reset the retransmission counter */
+    pcch->rc = 0;
+    /* clear all exception conditions */
+    pcch->reject_exception = FALSE;
+    pcch->acknowledge_pending = FALSE;
+    /* enter the multiple-frame-established state */
+    pcch_i->state = STATE_MULTIPLE_FRAME_ESTABLISHED;
+  }
+  else
+  {
+    /*
+     * frame_length NEQ 0 is only possible in contention resolution establishment
+     * initiated by mobile!
+     */
+     mdl_error_ind (U_FRAME_WITH_INCORRECT_PARAMETERS, pcch_i->pcch->ch_type, pcch_i->sapi);/* according to GSM 04.06, 5.4.2.1 */
+  }
+}/* endfunc mfe_sabm */
+
+static  void downlink_mfe_dm (T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  if (!GET_P_BIT (frame))
+  { /* release after unsolicited DM response during connection */
+
+    /* New! Called now by mdl_error_ind() from caller of this function
+     * drr_dl_release_ind (dl_data, pcch->ch_type, pcch_i->sapi, NOT_PRESENT_8BIT);
+     */
+
+    if ((pcch->ch_type EQ L2_CHANNEL_SDCCH) AND (pcch_i->sapi EQ PS_SAPI_3))
+    {
+      pcch_i->T200_Stop = TRUE;
+      com_clear_queue (PS_SAPI_3);
+    }
+  }
+}/* endfunc mfe_dm */
+
+static  void downlink_mfe_tr_unnumbered (T_CCH_INTERN* pcch_i,
+                                         UBYTE *frame, UBYTE state)
+{
+  UBYTE frame_type;
+  T_CCH*  pcch;
+
+  TRACE_EVENT_WIN ("downlink_mfe_tr_unnumbered()");
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  frame_type = GET_U_TYPE (frame);
+  if (GET_CR (frame))
+  { /* command */
+    switch (frame_type)
+    {
+    case SABM_FRAME:
+      downlink_mfe_sabm (pcch_i, frame);
+      break;
+    case UI_FRAME:
+      /* drr_dl_unitdata_ind() was called in the main downlink function */
+      break;
+    case DISC_FRAME:
+      pcch->vtx = UA_CMD;
+      pcch_i->T200_Stop = TRUE;
+      pcch_i->state = STATE_AWAITING_RELEASE;
+#if !defined(LATE_LEAVING_DEDICATED)
+      com_leave_dedicated (pcch->ch_type);
+#endif /* LATE_LEAVING_DEDICATED */
+      if (pcch_i->sapi EQ PS_SAPI_0)
+      {
+        dcch3_enable(pcch->ch_type);
+      }
+      break;
+    default:
+      /* GSM 04.06 Annex G.2.2, G.3.2 */
+      TRACE_EVENT_WIN_P1 ("invalid command U frame (%02x)", frame_type);
+      break;
+    }
+  }
+  else
+  { /* response */
+    switch (frame_type)
+    {
+    case DM_FRAME:
+      /* fulfill the actions required by 3GPP TS 4.06 section 5.4.2.2, table 7 */
+      if (!GET_P_BIT(frame))
+      {
+        mdl_error_ind (UNSOLICITED_DM_RESPONSE_ABNORMAL_REL, pcch->ch_type, pcch_i->sapi);
+      }
+      else if (state EQ STATE_MULTIPLE_FRAME_ESTABLISHED)
+      {
+        mdl_error_ind (UNSOLICITED_DM_RESPONSE, pcch->ch_type, pcch_i->sapi);
+      }
+
+      downlink_mfe_dm (pcch_i, frame);
+      break;
+    case UA_FRAME:
+      mdl_error_ind (UNSOLICITED_UA_RESPONSE, pcch->ch_type, pcch_i->sapi);
+      break;
+    default:
+      /* GSM 04.06 Annex G.2.2, G.3.2 */
+      TRACE_EVENT_WIN_P1 ("invalid response U frame (%02x)", frame_type);
+      break;
+    }
+  }
+}/* endfunc downlink_mfe_unnumbered */
+
+static  void downlink_i_frame (T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  /*
+   * in accordance with CCITT Q.921 figure B.7 (sheet 8 of 10)
+   * in accordance with CCITT Q.921 figure B.8 (sheet 7 of 9)
+   * according to GSM 04.06 (same as CCITT Q.921!)
+   */
+
+  UBYTE frame_pollbit;
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  frame_pollbit = GET_P_BIT (frame);
+
+  if (GET_SEND_NUMBER (frame) EQ pcch->vr)
+  {
+    pcch->vr++;
+    pcch->vr &= 7;
+    pcch->reject_exception = FALSE;
+    concatenate (pcch->ch_type, pcch_i->sapi, frame);
+    if (!GET_M_BIT (frame))
+      pcch_i->dl_data_ind = TRUE;/* send DL-DATA indication */
+
+    if (frame_pollbit)
+    {
+      pcch_i->pf_bit_flag = TRUE;
+      pcch->vtx = RR_RSP;
+      pcch->acknowledge_pending = FALSE;
+    }
+    else if (pcch->acknowledge_pending EQ FALSE)
+    {
+#if defined(IFRAME_AS_RR)
+      pcch->vtx = RR_CMD;
+#else
+      pcch->vtx = RR_RSP;
+#endif  /* IFRAME_AS_RR */
+    }
+  }/* endif ns == vr */
+  else
+  {
+    if (pcch->reject_exception)
+    {
+      if (frame_pollbit)
+      {
+        pcch_i->pf_bit_flag = TRUE;
+        /*pcch->vtx = RR_RSP; */
+        pcch->vtx = REJ_CMD;
+        pcch->acknowledge_pending = FALSE;
+      }
+    }
+    else
+    {
+      pcch->reject_exception = TRUE;
+      pcch_i->pf_bit_flag = frame_pollbit;
+      pcch->vtx = REJ_CMD;
+      pcch->acknowledge_pending = FALSE;
+    }
+    if (pcch->vtx EQ REJ_CMD)
+    {
+      TRACE_EVENT_WIN_P1 ("->REJ_CMD pf=%u", pcch_i->pf_bit_flag);
+    }
+  }/* endelse ns != vr */
+}/* endfunc downlink_i_frame */
+
+static  void downlink_mfe_information ( T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  UBYTE frame_nr;
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  TRACE_EVENT_WIN ("downlink_mfe_information()");
+
+  if (!GET_CR (frame))
+  { /* GSM 04.06 Annex G.2.2 */
+    TRACE_EVENT_WIN ("invalid I response (C=0)");
+    return;
+  }
+
+  frame_nr = GET_RECEIVE_NUMBER (frame);
+
+  /* in accordance with CCITT Q.921 figure B.7 (sheet 8 of 10) */
+  downlink_i_frame (pcch_i, frame);
+
+  /* in accordance with CCITT Q.921 figure B.7 (sheet 9 of 10) */
+  if (com_check_nr (pcch->va, pcch->vs, frame_nr))
+  { /* N(R) check is successfull */
+    if (frame_nr EQ pcch->vs)
+    {
+
+      /* T200 handling under ETSI GSM 04.06 section 5.5.3.1 */
+      if (((8 + frame_nr - pcch->va ) & 7) > 0 /*frame_nr > pcch->va*/)
+        pcch_i->T200_Stop = TRUE;
+
+      pcch->va = frame_nr;
+      free_sending_buffer (pcch->ch_type, pcch_i->sapi);
+    }
+    else if (frame_nr NEQ pcch->va)
+    {
+      pcch->va = frame_nr;
+      pcch_i->T200_Start = TRUE;
+    }
+  }
+  else
+  {
+    nr_error_recovery (pcch_i, frame);
+  }/* endifelse com_check_nr */
+}/* endfunc downlink_mfe_information */
+
+static  int downlink_timer_recovery(T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  TRACE_EVENT_WIN ("downlink_timer_recovery()");
+
+  switch (GET_FORMAT_TYPE (frame))
+  {
+  case I_FORMAT:
+  case I1_FORMAT: /* I frame */
+    downlink_tr_information (pcch_i, frame);
+    break;
+  case S_FORMAT: /* S frame */
+    downlink_tr_supervisory (pcch_i, frame);
+    break;
+  case U_FORMAT: /* U frame */
+    downlink_mfe_tr_unnumbered (pcch_i, frame, STATE_TIMER_RECOVERY);
+    break;
+  default:
+    TRACE_EVENT_WIN ("invalid/unknown frame");
+    break;
+  }
+
+  return 0;
+}/* endfunc downlink_tr */
+
+static  void downlink_tr_supervisory (T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  UBYTE frame_cr;
+  UBYTE frame_pollbit;
+  UBYTE frame_nr;
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  TRACE_EVENT_WIN ("downlink_tr_supervisory()");
+
+  frame_cr = GET_CR (frame);
+  frame_pollbit = GET_P_BIT (frame);
+  frame_nr = GET_RECEIVE_NUMBER (frame);
+
+  switch (GET_S_TYPE (frame))
+  {
+  case RR_FRAME:
+    /*
+     * in accordance with CCITT Q.921 figure B.8 (sheet 5 and 6 of 9) and
+     * 3GPP 04.06, 5.5.3.1 "On receipt of a valid I frame or supervisory frame"
+     * and 5.5.7 "Waiting acknowledgement".
+     */
+    if (frame_pollbit AND frame_cr)
+    {
+      enquiry_response (pcch_i, frame);
+    }
+
+    if (com_check_nr (pcch->va, pcch->vs, frame_nr))
+    {
+      /* N(R) check is successfull */
+
+      TRACE_EVENT_WIN_P5 ("V(A)=%d =< N(R)=%d =< V(S)=%d check is successfull, pf=%u cr=%u",
+        pcch->va, frame_nr, pcch->vs, frame_pollbit, frame_cr);
+
+      pcch->va = frame_nr;
+
+      if (frame_pollbit AND !frame_cr)
+      { /*
+         * 3GPP 04.06, 5.5.7:
+         * The timer recovery state is only cleared if the DL receives a valid
+         * supervisory frame response with the F bit set to 1.
+         */
+        pcch_i->T200_Stop = TRUE;
+        pcch_i->state = STATE_MULTIPLE_FRAME_ESTABLISHED;
+        /* acknowledgement according to GSM 04.06, 5.5.3.1*/
+        free_sending_buffer (pcch->ch_type, pcch_i->sapi);
+      }
+      else
+      {
+        invoke_retransmission (pcch_i, frame_nr);
+      }
+    }
+    else
+    {
+      nr_error_recovery (pcch_i, frame);
+    }/* endifelse com_check_nr */
+    break;
+
+  case REJ_FRAME:
+#if defined(NEW_REJ_ACK)
+    /*
+     * in accordance with
+     * 3GPP 04.06, 5.5.3.1 "On receipt of a valid I frame or supervisory frame"
+     */
+    if (com_check_nr (pcch->va, pcch->vs, frame_nr))
+    {
+      /* N(R) check is successfull */
+
+      TRACE_EVENT_WIN_P5 ("V(A)=%d =< N(R)=%d =< V(S)=%d check is successfull, pf=%u cr=%u",
+        pcch->va, frame_nr, pcch->vs, frame_pollbit, frame_cr);
+
+      /* acknowledgement according to GSM 04.06, 5.5.3.1*/
+      free_sending_buffer (pcch->ch_type, pcch_i->sapi);
+
+      pcch->va = frame_nr;
+    }
+#endif  /* NEW_REJ_ACK */
+
+    /*
+     * in accordance with GSM 04.06; chapter 5.5.4.1
+     * Receipt of a valid REJ frame [ii) and iii)]
+     *
+     * clear existing peer receiver busy condition (not applicable in GSM)
+     */
+
+    if (frame_pollbit AND !frame_cr)
+    {/* REJ response with F bit set to 1 */
+      /* clear the timer recovery state */
+      pcch_i->state = STATE_MULTIPLE_FRAME_ESTABLISHED;
+
+      /* reset timer T200 */
+      pcch_i->T200_Stop = TRUE;
+
+#if defined(NEW_REJ_ACK)
+      /* transmit the corresponding I frame asap */
+      if (pcch->va NEQ frame_nr)
+      {
+        TRACE_EVENT_WIN_P2 ("REJ: V(A)=%d != N(R)=%d => invoke retransmission",
+          pcch->va, frame_nr);
+
+        invoke_retransmission (pcch_i, frame_nr);
+      }
+#else /* NEW_REJ_ACK */
+      /* transmit the corresponding I frame asap */
+      invoke_retransmission (pcch_i, frame_nr);
+#endif  /* NEW_REJ_ACK */
+
+
+      /*
+       * set its send state variable V(S) and its acknowledge state
+       * variable V(A) to the value of the N(R) contained in the REJ frame
+       * control field
+       */
+      pcch->vs = pcch->va = frame_nr;
+    }
+    else
+    {
+      /*
+       * set its its acknowledge state variable V(A) to the value
+       * of the N(R) contained in the REJ frame control field
+       */
+      pcch->va = frame_nr;
+
+      /*
+       * if REJ command with P bit set to 1,
+       * transmit an appropiate supervisory response frame with F bit set to 1
+       */
+      if (frame_pollbit AND frame_cr)
+      {
+        enquiry_response (pcch_i, frame);
+      }
+    }
+    break;
+
+  case RNR_FRAME:
+    /*
+     * ignore RNR frame without notification
+     * (ETSI GSM 04.06, section 6.
+     * "Special protocol operation on SAPI=0 and SAPI=3", page 53)
+     */
+  default:
+    /* frame not implemented, */
+    TRACE_EVENT_WIN ("invalid S frame");  /* GSM 04.06 Annex G.3.1 */
+    return;
+  }/* endswitch frame_type */
+
+
+}/* endfunc downlink_tr_supervisory */
+
+static  void downlink_tr_information ( T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  T_CCH*  pcch;
+  UBYTE   frame_nr;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  TRACE_EVENT_WIN ("downlink_tr_information()");
+
+  if (!GET_CR (frame))
+  { /* GSM 04.06 Annex G.2.2 */
+    TRACE_EVENT_WIN ("invalid I response (C=0)");
+    return;
+  }
+
+  /* in accordance with CCITT Q.921 figure B.8 (sheet 7 of 9) */
+  downlink_i_frame (pcch_i, frame);
+
+  /*
+   * in accordance with CCITT Q.921 figure B.8 (sheet 8 of 9) and
+   * 3GPP 04.06, 5.5.3.1 "On receipt of a valid I frame or supervisory frame"
+   * and 5.5.7 "Waiting acknowledgement".
+   */
+  frame_nr = GET_RECEIVE_NUMBER (frame);
+  if (com_check_nr (pcch->va, pcch->vs, frame_nr))
+    pcch->va = frame_nr;/* N(R) check is successfull */
+  else
+    nr_error_recovery (pcch_i, frame);
+}/* endfunc downlink_tr_information */
+
+static  int downlink_awaiting_release(T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  T_CCH*  pcch;
+
+  CHECK_PCCH_Ir();
+  pcch = pcch_i->pcch;
+
+  TRACE_FUNCTION ("downlink_awaiting_release()");
+
+  if (GET_FORMAT_TYPE (frame) EQ U_FORMAT)
+  { /* U frame */
+    if (GET_CR (frame))
+    { /* command */
+      switch (GET_U_TYPE (frame))
+      {
+      case SABM_FRAME:
+        if (pcch_i->sapi EQ PS_SAPI_0)
+        {
+          pcch->vtx = DM_CMD;
+          pcch_i->pf_bit_flag = GET_P_BIT (frame);
+          pcch_i->T200_Stop = TRUE;
+          pcch_i->state = STATE_IDLE_DL;
+          drr_dl_release_cnf (pcch->ch_type, PS_SAPI_0, FALSE);
+        }
+        break;
+      case UI_FRAME:
+        /* drr_dl_unitdata_ind() was called in the main downlink function */
+        break;
+      case DISC_FRAME:
+        pcch->vtx = UA_CMD;
+        pcch_i->T200_Stop = TRUE;
+#if !defined(LATE_LEAVING_DEDICATED)
+        com_leave_dedicated (pcch->ch_type);
+#endif  /* LATE_LEAVING_DEDICATED */
+        break;
+      default:
+        /* GSM 04.06 Annex G.2.2, G.3.2 */
+        TRACE_EVENT_WIN_P1 ("invalid command U frame (%02x)", GET_U_TYPE (frame));
+        break;
+      }
+    }
+    else
+    { /* response */
+      switch (GET_U_TYPE (frame))
+      {
+      case DM_FRAME:
+        /*
+         * PATCH LE 14.09.99
+         * Ignore DM(F=0) frames
+         */
+        if (!GET_P_BIT (frame))
+          break;
+
+        /* the same as UA_FRAME */
+        /*lint -fallthrough*/
+
+      case UA_FRAME:
+        pcch_i->T200_Stop = TRUE;
+#if defined(LATE_LEAVING_DEDICATED)
+        com_leave_dedicated (pcch->ch_type);
+#endif  /* LATE_LEAVING_DEDICATED */
+        pcch_i->state = STATE_IDLE_DL;
+        drr_dl_release_cnf (pcch->ch_type, pcch_i->sapi, TRUE);
+        break;
+
+      default:
+        TRACE_EVENT_WIN_P1 ("invalid response U frame (%02x)", GET_U_TYPE (frame));
+        break;
+      }
+    }
+  }
+  return 0;
+}/* endfunc downlink_awaiting_release */
+
+static  void invoke_retransmission (T_CCH_INTERN* pcch_i, UBYTE frame_nr)
+{
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  if (pcch->vs NEQ frame_nr)
+  { /* decrement V(S) and recover queue for retransmission */
+    TRACE_EVENT_WIN ("invoke retransmission");
+
+    pcch->vs--;
+    pcch->vs &= 7;
+    com_recover_queue (pcch_i->sapi);
+  }
+}/* endfunc invoke_retransmission */
+
+
+static  void enquiry_response (T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  TRACE_EVENT_WIN ("enquiry_response()");
+  /*
+   * in accordance with ETSI GSM 04.06, 5.5.3.2 Receiving supervisory
+   * commands with the P bit set to "1" and ETSI GSM 04.06, 5.5.4.1 iii
+   */
+
+  pcch_i->pf_bit_flag = TRUE;
+  pcch->acknowledge_pending = FALSE;
+  pcch->vtx = RR_RSP;
+}/* endfunc enquiry_response */
+
+#if defined(_SIMULATION_)
+LOCAL const char * const _str_error_ind_cause[] =
+{
+  "T200_EXPIRED_N200_PLUS_1_TIMES, \"T200 expired (N200 + 1 times)\"",
+  "CS_REEST_REQ, \"re-establishment request\"",
+  "UNSOLICITED_UA_RESPONSE, \"unsolicited UA response\"",
+  "UNSOLICITED_DM_RESPONSE, \"unsolicited DM response\"",
+  "UNSOLICITED_DM_RESPONSE_ABNORMAL_REL, \"unsolicited DM response, multiple frame established state\"",
+  "UNSOLICITED_SUPERVISORY_RESPONSE, \"unsolicited supervisory response\"",
+  "SEQUENCE_ERROR, \"sequence error\"",
+  "U_FRAME_WITH_INCORRECT_PARAMETERS, \"U frame with incorrect parameters\"",
+  "S_FRAME_WITH_INCORRECT_PARAMETERS, \"S frame with incorrect parameters\"",
+  "I_FRAME_WITH_INCORRECT_USE_OF_M_BIT, \"I frame with incorrect use of M bit\"",
+  "I_FRAME_WITH_INCORRECT_LENGTH, \"I frame with incorrect length\"",
+  "FRAME_NOT_IMPLEMENTED, \"frame not implemented\"",
+};
+#endif  /* _SIMULATION_ */
+
+static  void mdl_error_ind (UBYTE cause,
+                         UBYTE channel_type, UBYTE sapi)
+{
+  TRACE_EVENT_WIN_P2 ("mdl_error_ind(%u %s)", cause, _str_error_ind_cause[cause]);
+  switch(cause)
+  {
+    case T200_EXPIRED_N200_PLUS_1_TIMES:
+    case UNSOLICITED_DM_RESPONSE:
+    case UNSOLICITED_DM_RESPONSE_ABNORMAL_REL:
+    case SEQUENCE_ERROR:
+      drr_error_ind (channel_type, sapi);
+      break;
+    default:
+      break;
+  }
+}/* endfunc mdl_error_ind */
+
+static  void nr_error_recovery (T_CCH_INTERN* pcch_i, UBYTE *frame)
+{
+  
+  T_CCH*  pcch;
+
+  CHECK_PCCH_I();
+  pcch = pcch_i->pcch;
+
+  TRACE_EVENT_WIN ("nr_error_recovery()");
+  DL_OFFLINE_TRACE (TRACE_DL_EVENT, TRACE_CH_UNKNOWN, pcch->ch_type, "N(R) sequence error");
+
+  switch (pcch->ch_type)
+  {
+  case L2_CHANNEL_SDCCH:
+    if (pcch_i->sapi EQ PS_SAPI_3)
+      break;
+    /*lint -fallthrough*/
+  case L2_CHANNEL_SACCH:
+  case L2_CHANNEL_FACCH_F:
+  case L2_CHANNEL_FACCH_H:
+    if ((GET_P_BIT (frame) EQ 1) AND !GET_M_BIT (frame))
+      pcch_i->dl_data_ind = TRUE; /* indicate a complete message to layer 3 */
+    else
+      pcch_i->dl_data_ind = FALSE;/* no indication if P bit set to "0" or message is incomplete */
+
+    /*
+     * GSM 04.06, 5.7.4 The data link shall remain in current state
+     * until it´s release by layer 3 ???
+     */
+    pcch_i->mdl_error_ind = SEQUENCE_ERROR; /* send mdl error ind after sequence error */
+
+    DL_EM_CHANNEL_FAILURE;
+
+    break;
+  }
+  pcch->vtx = EMPTY_CMD;/* no answer after N(R) sequence error */
+}/* endfunc nr_error_recovery */
+
+
+static void concatenate (UBYTE ch_type, UBYTE sapi,UBYTE *frame)
+{
+  GET_INSTANCE_DATA;
+  switch (ch_type)
+  {
+  case L2_CHANNEL_SDCCH:
+    if (sapi EQ PS_SAPI_0)
+      com_concatenate (&dl_data->dcch0_in_msg, frame);
+    else if (sapi EQ PS_SAPI_3)
+      com_concatenate (&dl_data->dcch3_in_msg, frame);
+    break;
+  case L2_CHANNEL_SACCH:
+    if (sapi EQ PS_SAPI_3)
+    {
+      com_concatenate (&dl_data->dcch3_in_msg, frame);
+    }
+    break;
+  case L2_CHANNEL_FACCH_F:
+  case L2_CHANNEL_FACCH_H:
+    if (sapi EQ PS_SAPI_0)
+    {
+      com_concatenate (&dl_data->dcch0_in_msg, frame);
+    }
+    break;
+  default:
+    break;
+  }
+}/* endfunc concatenate */
+
+/*
++--------------------------------------------------------------------+
+| PROJECT : GSM-PS (6147)          MODULE  : DL_COM                  |
+| STATE   : code                   ROUTINE : free_sending_buffer     |
++--------------------------------------------------------------------+
+
+  PURPOSE : After confirmation of an I frame the sending buffer is
+            released if it was the last segment. This avoids
+            resending of I frames after resumption.
+
+*/
+static void free_sending_buffer ( UBYTE ch_type, UBYTE sapi)
+{
+  GET_INSTANCE_DATA;
+  T_QUEUE *queue = NULL;
+
+  TRACE_EVENT_WIN_P2 ("free_sending_buffer():%s SAPI=%u", CH_TYPE_NAME[ch_type], sapi);
+
+  switch (ch_type)
+  {
+  case L2_CHANNEL_SDCCH:
+    if (sapi EQ PS_SAPI_0)
+      queue = &dl_data->dcch0_queue;
+    else if (sapi EQ PS_SAPI_3)
+      queue = &dl_data->dcch3_queue;
+    break;
+  case L2_CHANNEL_SACCH:
+    if (sapi EQ PS_SAPI_3)
+      queue = &dl_data->dcch3_queue;
+    break;
+  case L2_CHANNEL_FACCH_F:
+  case L2_CHANNEL_FACCH_H:
+    if (sapi EQ PS_SAPI_0)
+      queue = &dl_data->dcch0_queue;
+    break;
+  default:
+    break;
+  }
+
+  if (queue)
+  {
+    if (queue->switch_buffer)
+    {
+      TRACE_EVENT_WIN_P3 ("CNF on %s SAPI=%u %s (switch_buffer)",
+        CH_TYPE_NAME[queue->switch_buffer->ch_type],
+        queue->switch_buffer->sapi,
+        queue->switch_buffer->cnf ? "CNF required" : "");
+
+      com_l3trace (TRACE_UPLINK, queue->switch_buffer->ch_type, (UBYTE *)queue->switch_buffer);
+      COM_FREE_QUEUE_BUFFER ( queue, INDEX_SWITCH_BUFFER);
+    }
+    else if (queue->sending_buffer)
+    {
+      TRACE_EVENT_WIN_P4 ("CNF on %s SAPI=%u %s (sending_buffer) act_length=%u",
+        CH_TYPE_NAME[queue->sending_buffer->ch_type],
+        queue->sending_buffer->sapi,
+        queue->sending_buffer->cnf ? "CNF required" : "",
+        queue->act_length);
+
+      if (queue->act_length EQ 0)
+      { /* entire message has been sent */
+        com_l3trace (TRACE_UPLINK, queue->sending_buffer->ch_type, (UBYTE *)queue->sending_buffer);
+
+        if (queue->sending_buffer->cnf)
+        {
+          drr_dl_data_cnf (sapi);
+        }
+        else
+        {
+          COM_FREE_QUEUE_BUFFER (queue, INDEX_SENDING_BUFFER);
+        }
+      }
+    }
+    else
+    {
+      TRACE_EVENT_WIN_P2 ("%s SAPI=%u: sending_buffer and switch_buffer=NULL !!!",
+        CH_TYPE_NAME[ch_type], sapi);
+    }
+  }
+}/* endfunc concatenate */
+
+static void T200_expiry ( UBYTE channel, UBYTE sapi)
+{
+  GET_INSTANCE_DATA;
+  UBYTE   old_state;
+  UBYTE   new_state;
+  T_CCH * pcch = &dl_data->cch[channel];
+  UBYTE   N200_counter;
+
+  TRACE_FUNCTION ("T200_expiry()");
+
+  switch (pcch->ch_type)
+  {
+    case L2_CHANNEL_SACCH:
+      N200_counter = SACCH_N200;
+      break;
+    case L2_CHANNEL_SDCCH:
+      N200_counter = SDCCH_N200;
+      break;
+    case L2_CHANNEL_FACCH_F:
+      N200_counter = FACCH_N200_FR;
+      break;
+    case L2_CHANNEL_FACCH_H:
+      N200_counter = FACCH_N200_HR;
+      break;
+    default:
+      N200_counter = NOT_PRESENT_8BIT;
+      break;
+  }
+
+  new_state = old_state = dl_data->state[channel];
+  switch (old_state)
+  {
+    case STATE_CONTENTION_RESOLUTION:
+      repeat_sabm (channel, sapi);
+      break;
+
+    case STATE_MULTIPLE_FRAME_ESTABLISHED:
+      pcch->rc        = 1;
+      pcch->p_bit_flag= 1;
+      pcch->time_flag = TRUE;
+      new_state   = STATE_TIMER_RECOVERY;
+      break;
+
+    case STATE_TIMER_RECOVERY:
+      if (N200_counter EQ NOT_PRESENT_8BIT)
+        break; /* invalid channel */
+
+      if (pcch->rc >= N200_counter)
+      { /* release connection due to T200 expired N200 plus 1 times */
+
+        TRACE_EVENT_WIN_P1 ("T200 expired, N200=%u", N200_counter);
+
+        /* New! Called now by mdl_error_ind().
+         * drr_dl_release_ind (dl_data, pcch->ch_type, sapi, NOT_PRESENT_8BIT);
+         */
+        mdl_error_ind (T200_EXPIRED_N200_PLUS_1_TIMES, pcch->ch_type, sapi);
+
+        if (sapi EQ PS_SAPI_0)
+        {
+          dcch3_init_dl_data();
+          pcch->T200_counter      = 0;
+          pcch->time_flag             = FALSE;
+          pcch->contention_resolution = FALSE;
+          pcch->vtx                   = EMPTY_CMD;
+          new_state         = STATE_IDLE_DL;
+        }
+      }
+      else
+      {
+        pcch->rc++;
+        pcch->p_bit_flag  = 1;
+        pcch->time_flag   = TRUE;
+      }
+      break;
+
+    case STATE_AWAITING_RELEASE:
+      if (pcch->rc >= N200_ESTABLISHMENT)
+      {
+        TRACE_EVENT_WIN_P1 ("T200 expired, N200=%u", N200_ESTABLISHMENT);
+        drr_dl_release_cnf (pcch->ch_type, sapi, TRUE);
+        new_state = STATE_IDLE_DL;
+
+      }
+      else
+      {
+        pcch->rc++;
+        pcch->vtx   = DISC_CMD;
+        pcch->time_flag = TRUE;
+      }
+      break;
+
+    default:
+      break;
+  }/* endswitch old_state */
+
+  if (new_state NEQ old_state)
+  {
+    set_channel_state (channel, new_state);
+  }
+
+}/* endfunc T200_expiry */
+
+/*
++--------------------------------------------------------------------+
+| PROJECT : GSM-PS (6147)       MODULE  : state                      |
+| STATE   : code                ROUTINE : repeat_sabm                |
++--------------------------------------------------------------------+
+
+  PURPOSE : Repeat if possible the SABM command.
+
+*/
+
+static void repeat_sabm (UBYTE channel, UBYTE sapi)
+{
+  GET_INSTANCE_DATA;
+  T_CCH*  pcch = &dl_data->cch[channel];
+
+  TRACE_FUNCTION ("repeat_sabm()");
+
+  if (pcch->rc >= N200_ESTABLISHMENT)
+  {
+    if (sapi EQ PS_SAPI_0)
+      pcch->contention_resolution = FALSE;
+
+    DL_OFFLINE_TRACE (TRACE_DL_EVENT, channel, pcch->ch_type, ">N200_EST");
+    SYST_TRACE_P ((SYST, "DL: >N200 of SABM"));
+    TRACE_ERROR ("DL: >N200 of SABM");
+
+    DL_EM_CHANNEL_ESTABLISHMENT_FAILED;
+
+    mdl_error_ind(T200_EXPIRED_N200_PLUS_1_TIMES, pcch->ch_type, sapi);
+    set_channel_state (channel, STATE_IDLE_DL);
+    /* New! Called now by mdl_error_ind().
+     * drr_dl_release_ind (dl_data, pcch->ch_type, sapi, NOT_PRESENT_8BIT);
+     */
+  }
+  else
+  {
+    pcch->rc++;
+    pcch->vtx       = SABM_CMD;
+    pcch->time_flag = TRUE;
+    TRACE_EVENT_P1 ("DL: T200 %u. repeat of SABM", pcch->rc);
+  }
+}/* endfunc repeat_sabm */
+
+GLOBAL  void set_channel_state (UBYTE channel, UBYTE state)
+{
+  GET_INSTANCE_DATA;
+  if (dl_data->state[channel] NEQ state)
+  {
+    #ifdef TRACE_STATE
+      vsi_o_state_ttrace ("STATE_%s:%s -> %s", PROCESS_NAME[channel],
+        channel EQ C_DCCH3 ? STATE_DCCH3_NAME[dl_data->state[channel]]:
+                             STATE_DCCH0_NAME[dl_data->state[channel]],
+        channel EQ C_DCCH3 ? STATE_DCCH3_NAME[state] :
+                             STATE_DCCH0_NAME[state]);
+    #endif /* TRACE_STATE */
+    dl_data->state[channel] = state;
+    DL_OFFLINE_TRACE (TRACE_CHSTATE, channel, dl_data->cch[channel].ch_type, NULL);
+  }
+}/* endfunc set_channel_state */
+
+static void set_T200_counter (T_CCH* pcch, UBYTE sapi)
+{
+  switch (pcch->ch_type)
+  {
+    case L2_CHANNEL_SACCH:
+      pcch->T200_counter = sapi EQ PS_SAPI_0 ?
+        T200_SDCCH_SAPI_0_CNT : T200_SACCH_SAPI_3_CNT;
+      break;
+    default:
+      TRACE_EVENT_WIN_P1 ("set_T200_counter: unknown ch_type=%u -> use SDCCH", pcch->ch_type);
+      /*lint -fallthrough*/
+    case L2_CHANNEL_SDCCH:
+      pcch->T200_counter = sapi EQ PS_SAPI_0 ?
+        T200_SDCCH_SAPI_0_CNT : T200_SDCCH_SAPI_3_CNT;
+      break;
+    case L2_CHANNEL_FACCH_F:
+      pcch->T200_counter = T200_FACCH_SAPI_0_CNT_FR;
+      break;
+    case L2_CHANNEL_FACCH_H:
+      pcch->T200_counter = T200_FACCH_SAPI_0_CNT_HR;
+      break;
+  }/* endswitch ch_type */
+  pcch->time_flag = FALSE;
+
+  TRACE_EVENT_WIN_P3 ("set_T200_counter: %s, SAPI=%u: %u",
+    CH_TYPE_NAME[pcch->ch_type], sapi, pcch->T200_counter);
+}/* endfunc set_T200_counter */
+
+#if defined(DELAYED_RELEASE_IND)
+static void delayed_release_ind( UBYTE channel)
+{
+  GET_INSTANCE_DATA;
+  /* delay DL RELEASE IND to RR for FTA 25.2.3 *********** */
+  if (dl_data->release_ind_ch_type NEQ NOT_PRESENT_8BIT)
+  {
+    if (dl_data->release_ind_delay > 0)
+      dl_data->release_ind_delay--;
+
+    if (dl_data->release_ind_delay EQ 0)
+    {
+      DL_OFFLINE_TRACE (TRACE_DL_EVENT, TRACE_CH_UNKNOWN,
+        dl_data->release_ind_ch_type, "UL:send delayed REL IND");
+      drr_dl_release_ind (dl_data->release_ind_ch_type,
+        dl_data->release_ind_sapi, NOT_PRESENT_8BIT, TRUE);
+    }
+    else
+    {
+      DL_OFFLINE_TRACE (TRACE_DL_EVENT, TRACE_CH_UNKNOWN,
+        dl_data->release_ind_ch_type, "UL:pend. delay REL IND");
+    }
+
+  }
+}
+
+#endif  /* DELAYED_RELEASE_IND */
+
+T_RADIO_FRAME* dl_uplink(UBYTE channel, UBYTE sapi,
+                         UBYTE no_signalling_mode, BOOL recursive)
+{
+  GET_INSTANCE_DATA;
+  T_CCH*          pcch = &dl_data->cch[channel];
+  T_RADIO_FRAME*  p_l2_frame = &dl_data->l2_frame;
+  int             send = UPLINK_NULL;
+
+  TRACE_EVENT_WIN_P3 ("uplink(): %s dcch0_ch_type:%s dedi=%u",
+      CH_TYPE_NAME[pcch->ch_type], CH_TYPE_NAME[dl_data->dcch0_ch_type], dl_data->RR_dedicated);
+  TRACE_EVENT_WIN_P6 ("UL: DCCH0=%s,%s vr=%u vs=%u va=%u T200=%u",
+    CH_TYPE_NAME[dl_data->cch[C_DCCH0].ch_type],
+    STATE_DCCH0_NAME[dl_data->state[C_DCCH0]],
+    dl_data->cch[C_DCCH0].vr, dl_data->cch[C_DCCH0].vs, dl_data->cch[C_DCCH0].va,
+    dl_data->cch[C_DCCH0].T200_counter);
+  TRACE_EVENT_WIN_P6 ("UL: DCCH3=%s,%s vr=%u vs=%u va=%u T200=%u",
+    CH_TYPE_NAME[dl_data->cch[C_DCCH3].ch_type],
+    STATE_DCCH3_NAME[dl_data->state[C_DCCH3]],
+    dl_data->cch[C_DCCH3].vr, dl_data->cch[C_DCCH3].vs, dl_data->cch[C_DCCH3].va,
+    dl_data->cch[C_DCCH3].T200_counter);
+
+
+  /* check SACCH SAPI and channel */
+  if (pcch->ch_type EQ L2_CHANNEL_SACCH)
+  {
+    if (!dl_data->RR_dedicated)
+    {
+      sapi = PS_SAPI_0;
+      send = UPLINK_EMPTY;
+      TRACE_EVENT_WIN ("SACCH without dedicated channel: change SAPI->0, UPLINK_EMPTY");
+    }
+    else if (dl_data->state[C_DCCH0] EQ STATE_SUSPENDED)
+    {
+      sapi = PS_SAPI_0;
+      send = UPLINK_REPORT;
+      TRACE_EVENT_WIN ("SACCH during suspended dedicated channel: change SAPI->0, UPLINK_REPORT");
+    }
+    else if (sapi EQ PS_SAPI_3)
+    {
+      if ((dl_data->state[C_DCCH0] <= STATE_IDLE_DL)
+             OR
+          (dl_data->dcch0_ch_type EQ L2_CHANNEL_SDCCH)
+             OR
+          ((dl_data->cch[C_DCCH3].vtx EQ EMPTY_CMD) AND
+           (!com_queue_awaiting_transmission (PS_SAPI_3)) AND
+           (dl_data->state[C_DCCH3] NEQ STATE_AWAITING_ESTABLISHMENT)
+           ) )
+      {
+        sapi = PS_SAPI_0;
+        TRACE_EVENT_WIN ("SACCH with SAPI=3 only together with FACCH and frame is awaiting tx: change SAPI->0");
+      }
+      else
+      {
+        channel = C_DCCH3;
+        pcch = &dl_data->cch[channel];
+        TRACE_EVENT_WIN ("SACCH with SAPI=3 together with FACCH: change channel->C_DCCH3");
+      }
+    }
+  }
+  else if ((dl_data->RR_dedicated) AND
+           (dl_data->dcch0_ch_type NEQ pcch->ch_type))
+  {
+    TRACE_EVENT_WIN_P2 ("dcch0_ch_type (%s) NEQ %s -> unexpected,unsolicited,invalid channel => UPLINK_EMPTY",
+      CH_TYPE_NAME[dl_data->dcch0_ch_type],
+      CH_TYPE_NAME[dl_data->cch[C_DCCH0].ch_type]);
+    send = UPLINK_EMPTY;
+  }
+
+  /* decrease T200 counter ************************************ */
+  if (channel EQ C_DCCH3)
+  {
+    if (dl_data->cch[C_DCCH3].T200_counter >= T200_ACTIVE)
+    {
+      dl_data->cch[C_DCCH3].T200_counter--;
+      TRACE_EVENT_WIN_P2 ("T200(SAPI_0)=%u  T200(SAPI_3)=%u*",
+        dl_data->cch[C_DCCH0].T200_counter, dl_data->cch[C_DCCH3].T200_counter);
+      /*DL_OFFLINE_TRACE (TRACE_DL_EVENT, channel, pcch->ch_type, "dec T200(SAPI_3)");*/
+    }
+  }
+  else
+  {
+    if (pcch->ch_type NEQ L2_CHANNEL_SACCH) /* no T200 for SACCH with SAPI=0 */
+    {
+      if (dl_data->cch[C_DCCH0].T200_counter >= T200_ACTIVE)
+      {
+        dl_data->cch[C_DCCH0].T200_counter--;
+        TRACE_EVENT_WIN_P2 ("T200(SAPI_0)=%u* T200(SAPI_3)=%u",
+          dl_data->cch[C_DCCH0].T200_counter, dl_data->cch[C_DCCH3].T200_counter);
+        /*DL_OFFLINE_TRACE (TRACE_DL_EVENT, channel, pcch->ch_type, "dec T200(SAPI_0)");*/
+      }
+    }
+  }
+
+  /* check activity of DL ************************************* */
+  if (dl_data->dl_active AND dl_data->state[channel] EQ STATE_MULTIPLE_FRAME_ESTABLISHED)
+  {
+    TRACE_EVENT_WIN_P1 ("uplink(): %s dl_active", PROCESS_NAME[channel]);
+    DL_OFFLINE_TRACE (TRACE_DL_EVENT, channel, pcch->ch_type, "UL:&dl_active");
+
+    /* signalling only */
+    send = UPLINK_EMPTY;
+  }/* endif dl_active */
+
+#if defined(DELAYED_SABM)
+  /* delay uplink SABM to PL for FTA 26.6.6.1 and 25.2.3 *********** */
+  if ((channel EQ C_DCCH0) AND (dl_data->dcch0_sabm_flag NEQ NOT_PRESENT_8BIT))
+  {
+    dcch0_delay_sabm (pcch);
+    if (dl_data->dcch0_sabm_flag NEQ NOT_PRESENT_8BIT)
+      send = UPLINK_EMPTY; /* send dummy only */
+  }
+#endif  /* DELAYED_SABM */
+
+
+#if defined(DELAYED_RELEASE_IND)
+  delayed_release_ind (channel);
+#endif  /* DELAYED_RELEASE_IND */
+
+  if ((send EQ UPLINK_NULL) OR (send EQ UPLINK_EMPTY))
+  { /* After a L3 release at the last downlink DL should send a DISC */
+    TRACE_EVENT_WIN_P6 ("uplink():%s %s SAPI=%u vtx=%s %s (#%u)",
+      PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi,
+      VTX_NAME[pcch->vtx], SEND_NAME[send], __LINE__);
+    switch (channel)
+    {
+      case C_DCCH0:
+        send = dcch0_check_disc (send);
+        break;
+      case C_DCCH3:
+        send = dcch3_check_disc (send);
+        break;
+      default:
+        break;
+    }
+    TRACE_EVENT_WIN_P6 ("uplink():%s %s SAPI=%u vtx=%s %s (#%u)",
+      PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi,
+      VTX_NAME[pcch->vtx], SEND_NAME[send], __LINE__);
+  }
+
+  if (send EQ UPLINK_NULL)
+  {
+    TRACE_EVENT_WIN_P5 ("uplink():%s %s SAPI=%u vtx=%s %s",
+      PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi,
+      VTX_NAME[pcch->vtx], recursive ? "RECURSIVE" : "");
+
+    TRACE_EVENT_WIN_P8 ("UL: DCCH%u=%s,%s vr=%u vs=%u va=%u T200=%u (#%u)",
+      sapi, CH_TYPE_NAME[dl_data->cch[channel].ch_type],
+      STATE_DCCH3_NAME[dl_data->state[channel]],
+      dl_data->cch[channel].vr, dl_data->cch[channel].vs, dl_data->cch[channel].va,
+      dl_data->cch[channel].T200_counter, __LINE__);
+
+    /* check timer T200 expiry ****************************** */
+    if ((channel EQ C_DCCH0) AND
+        (dl_data->cch[C_DCCH0].T200_counter EQ T200_EXPIRED))
+      T200_expiry (C_DCCH0, PS_SAPI_0);
+    if ((channel EQ C_DCCH3) AND
+        (dl_data->cch[C_DCCH3].T200_counter EQ T200_EXPIRED))
+      T200_expiry (C_DCCH3, PS_SAPI_3);
+
+    TRACE_EVENT_WIN_P8 ("UL: DCCH%u=%s,%s vr=%u vs=%u va=%u T200=%u (#%u)",
+      sapi, CH_TYPE_NAME[dl_data->cch[channel].ch_type],
+      STATE_DCCH3_NAME[dl_data->state[channel]],
+      dl_data->cch[channel].vr, dl_data->cch[channel].vs, dl_data->cch[channel].va,
+      dl_data->cch[channel].T200_counter, __LINE__);
+
+    /* state machine **************************************** */
+    switch (dl_data->state[channel])
+    {
+    case STATE_IDLE_DL:
+      if (pcch->vtx EQ EMPTY_CMD AND !dl_data->RR_dedicated)
+        break;
+      send = uplink_idle (channel, sapi);
+      break;
+    case STATE_CONTENTION_RESOLUTION: /* the same as STATE_AWAITING_ESTABLISHMENT */
+      send = uplink_awaiting_establishment (channel, sapi, no_signalling_mode);
+      break;
+    case STATE_MULTIPLE_FRAME_ESTABLISHED:
+      send = uplink_mfe (channel, sapi, no_signalling_mode);
+      break;
+    case STATE_TIMER_RECOVERY:
+      send = uplink_timer_recovery (channel, sapi, no_signalling_mode);
+      break;
+    case STATE_AWAITING_RELEASE:
+      send = uplink_awaiting_release ( channel, sapi);
+      break;
+    case STATE_SUSPENDED: /* only DCCH0 */
+      send = UPLINK_NULL;
+      break;
+    case STATE_DISABLED:   /* only SABM/UA or SACCH and SDCCH (SAPI 3) */
+      if (channel EQ C_DCCH3)
+      {
+        if (pcch->vtx EQ UA_CMD)
+        {
+          send = uplink_idle (channel, sapi);
+        }
+        else
+        {
+          send = UPLINK_EMPTY;
+        }
+      }
+      break;
+    }/* endswitch channel_state */
+
+  }/* endif send == NULL */
+
+  TRACE_EVENT_WIN_P8 ("UL: DCCH%u=%s,%s vr=%u vs=%u va=%u T200=%u (#%u)",
+    sapi, CH_TYPE_NAME[dl_data->cch[channel].ch_type],
+    STATE_DCCH3_NAME[dl_data->state[channel]],
+    dl_data->cch[channel].vr, dl_data->cch[channel].vs, dl_data->cch[channel].va,
+    dl_data->cch[channel].T200_counter, __LINE__);
+
+  if (pcch->ch_type EQ L2_CHANNEL_SACCH)
+  { /*
+     * According to 3GPP TS 04.05, 4.2.2 Priority:
+     * The priority arrangement on the SACCH must ensure that if a SAPI = 3
+     * frame is awaiting transmission, two SAPI = 0 frames are not sent in
+     * consecutive SACCH frames. In addition, for the mobile to network
+     * direction it must also be ensured that any SAPI = 3 frame is followed
+     * by at least one SAPI = 0 frame.
+     */
+
+    TRACE_EVENT_WIN_P3 ("sacch_last_uplink_sapi=%u SAPI=%u send=%s",
+      dl_data->sacch_last_uplink_sapi, sapi, SEND_NAME[send]);
+
+    if ((dl_data->sacch_last_uplink_sapi EQ PS_SAPI_3) OR
+        ((sapi EQ PS_SAPI_3 ) AND (send EQ UPLINK_EMPTY)))
+    {/* last uplinked SACCH frame was one with SAPI=3
+      * or
+      * no SACCH SAPI=3 frame is awaiting transmission
+      * -> uplink of a SACCH SAPI=0 frame
+      */
+      sapi = PS_SAPI_0;
+    }
+
+    if (send EQ UPLINK_EMPTY)
+    {
+      sapi = PS_SAPI_0;
+    }
+
+    if ((sapi EQ PS_SAPI_0) AND (dl_data->state[C_DCCH0] >= STATE_SUSPENDED))
+    { /*
+       * uplink measurement reports only if SAPI=0 is suspended,
+       * on contention resolution procedure, established or awaiting release
+       */
+      send = UPLINK_REPORT;
+      TRACE_EVENT_WIN ("uplink(): SACCH REPORT now");
+    }
+
+    TRACE_EVENT_WIN_P2 ("sacch_last_uplink_sapi:=%u->%u", dl_data->sacch_last_uplink_sapi, sapi);
+    dl_data->sacch_last_uplink_sapi = sapi;
+  }
+  else if ((pcch->ch_type EQ L2_CHANNEL_SDCCH) AND (channel NEQ C_DCCH3))
+  { /*
+     * According to 3GPP TS 04.05, 4.2.2 Priority:
+     * The priority between data links on SDCCH shall be as follows:
+     * Highest priority : SAPI = 0, Lowest priority : SAPI = 3.
+     */
+    if ((sapi EQ PS_SAPI_0) AND (send <= UPLINK_EMPTY))
+    { /* special case: nothing is awaiting transmission for SAPI=0 */
+      if ((dl_data->cch[C_DCCH3].vtx NEQ EMPTY_CMD) OR
+          com_queue_awaiting_transmission(PS_SAPI_3) OR
+          (dl_data->cch[C_DCCH3].T200_counter NEQ T200_STOPPED))
+      {/* something is awaiting transmission for SAPI=3 */
+        if (dl_data->state[C_DCCH0] >= STATE_CONTENTION_RESOLUTION)
+        { /*
+           * uplink SAPI=3 only if SAPI=0 is on contention resolution procedure
+           * or established or awaiting release
+           */
+          send = UPLINK_DCCH3;
+        }
+      }
+
+    }
+  }
+  else
+  {
+    /* no special treatment of FACCH */
+  }
+
+  /* return uplink frame buffer pointer dependent on the value of send */
+  TRACE_EVENT_WIN_P4 ("%s on %s SAPI=%u RR_dedicated=%u",
+    SEND_NAME[send], CH_TYPE_NAME[pcch->ch_type], sapi, dl_data->RR_dedicated);
+
+  switch (send)
+  {
+  case UPLINK_NORMAL:
+    break;
+
+  case UPLINK_UA:
+  case UPLINK_UA_F:
+    com_build_UA_response (pcch->ch_type, sapi, (UBYTE)((send EQ UPLINK_UA_F) ? 1 : 0));
+    break;
+
+  case UPLINK_IFRAME:
+  case UPLINK_IFRAME_P:
+    {
+      UBYTE m_bit;
+      UBYTE p_bit = (send EQ UPLINK_IFRAME_P) ? 1 : 0;
+      T_QUEUE *queue;
+
+      if (pcch->ch_type EQ L2_CHANNEL_SACCH)
+      {
+        sapi = PS_SAPI_3; /* acknowledged mode only for SAPI=3 */
+        TRACE_EVENT_WIN ("SACCH with I frame: acknowledged mode only for SAPI=3");
+      }
+
+      if (sapi EQ PS_SAPI_0)
+        queue = &dl_data->dcch0_queue;
+      else
+        queue = &dl_data->dcch3_queue;
+
+      if (send EQ UPLINK_IFRAME)
+      {
+        com_read_queue (pcch->ch_type, sapi, &m_bit);
+      }
+      else
+      {/* TIMER_RECOVERY state -> repetition of the last frame */
+        pcch->vs--;
+        pcch->vs &= 7;
+        pcch->p_bit_flag = 0;
+        m_bit = queue->m_bit; /* remember last m bit  */
+        TRACE_EVENT_WIN_P2 ("TIMER_RECOVERY state: decrement vs to %u, remember m=%u", pcch->vs, m_bit);
+      }
+
+
+      com_build_I_command (pcch->ch_type, sapi, pcch->vs, pcch->vr,
+                           p_bit, m_bit, queue);
+      pcch->vs++;
+      pcch->vs &= 7;
+      TRACE_EVENT_WIN_P4 ("%s SAPI=%u new vs=%u (pcch=%08x)",
+        CH_TYPE_NAME[pcch->ch_type], sapi, pcch->vs, pcch);
+    }
+    set_T200_counter (pcch, sapi);
+    break;
+
+  case UPLINK_RR:
+  case UPLINK_RR_F:
+    if (pcch->ch_type EQ L2_CHANNEL_SACCH)
+    {/*
+      * There are some tests at the start of dl_uplink() to determine the right
+      * SAPI value for SACCH. But the resulting value may be wrong because of
+      * the priority arrangement according to 3GPP TS 04.05, section 4.2.2.
+      * In case the MS has to uplink a RR frame it is clear that
+      * L2 is in acknowledged mode and this can be done only with
+      * a SAPI value of 3 for SACCH.
+      */
+      sapi = PS_SAPI_3; /* acknowledged mode only for SAPI=3 */
+      TRACE_EVENT_WIN ("SACCH with supervisory frame: acknowledged mode only for SAPI=3");
+    }
+    com_build_RR_response (pcch->ch_type, sapi, pcch->vr,
+                          (UBYTE)((send EQ UPLINK_RR_F) ? 1 : 0));
+    break;
+
+  case UPLINK_REJ:
+  case UPLINK_REJ_F:
+    if (pcch->ch_type EQ L2_CHANNEL_SACCH)
+    {/*
+      * There are some tests at the start of dl_uplink() to determine the right
+      * SAPI value for SACCH. But the resulting value may be wrong because of
+      * the priority arrangement according to 3GPP TS 04.05, section 4.2.2.
+      * In case the MS has to uplink a REJ frame it is clear that
+      * L2 is in acknowledged mode and this can be done only with
+      * a SAPI value of 3 for SACCH.
+      */
+      sapi = PS_SAPI_3; /* acknowledged mode only for SAPI=3 */
+      TRACE_EVENT_WIN ("SACCH with supervisory frame: acknowledged mode only for SAPI=3");
+    }
+    com_build_REJ_response (pcch->ch_type, sapi, pcch->vr,
+                           (UBYTE)((send EQ UPLINK_REJ_F) ? 1 : 0));
+    break;
+
+  case UPLINK_REPORT:
+    if (dl_data->RR_dedicated)
+    {/* measurement report only in RR dedicated mode */
+      sacch0_send_data ();
+      com_l3trace (TRACE_UACK_UP, pcch->ch_type, (UBYTE *)p_l2_frame+5);
+
+
+      /* no deleting of vtx!!! */
+      return p_l2_frame;
+      /* break; not necessary */
+    }/* endif measurement report only in RR dedicated mode */
+
+    /* else go through to UPLINK_EMPTY */
+    /*lint -fallthrough*/
+
+  default:
+    possible_reset_dcch0_ch_type();
+
+    /* check RR message with short PD, short L2 header type 1, format Bter */
+    if ((dl_data->state[C_DCCH0] >= STATE_SUSPENDED) AND
+         dl_data->rr_short_pd_buffer.l_buf AND
+        (dl_data->rr_short_pd_ch_type EQ pcch->ch_type))
+    { /* RR message with short PD, short L2 header type 1, format Bter */
+      com_build_UI_Bter (pcch->ch_type);
+      return p_l2_frame;
+    }
+
+    if (((pcch->ch_type EQ L2_CHANNEL_FACCH_F) OR
+         (pcch->ch_type EQ L2_CHANNEL_FACCH_H))
+        AND
+        ((no_signalling_mode NEQ SIG_ONLY) 
+#if defined(DELAYED_SABM)
+        OR
+         /* suppress UI frames if delayed SABM is pending
+          * (independent from signalling mode) */
+          (dl_data->dcch0_sabm_flag NEQ NOT_PRESENT_8BIT) 
+#endif  /* DELAYED_SABM */
+#if defined(DELAYED_RELEASE_IND)
+        OR
+         (dl_data->release_ind_ch_type NEQ NOT_PRESENT_8BIT)
+#endif  /* DELAYED_RELEASE_IND */
+       ))
+    {
+      TRACE_EVENT_WIN ("UPLINK_NULL return");
+      return NULL;
+    }
+    /* break; not necessary */
+
+    {
+      T_RADIO_FRAME* empty_frame;
+
+      TRACE_EVENT_WIN ("UPLINK_EMPTY return");
+      /* The use of one byte array instead of two T_RADIO_FRAME structures
+       * saves 21 byte. Casting is in this case the better way then two calls
+       * of memcpy.
+       */
+      if (pcch->ch_type EQ L2_CHANNEL_SACCH)
+        empty_frame = (T_RADIO_FRAME*)(&l2_empty_frame[0]);
+      else
+        empty_frame = (T_RADIO_FRAME*)(&l2_empty_frame[2]);
+
+      ATRC (empty_frame, 23);
+
+      return empty_frame;
+    }
+    /* break; not necessary */
+
+  case UPLINK_DCCH3:
+    return dl_uplink (C_DCCH3, PS_SAPI_3, no_signalling_mode, TRUE);
+    /* break; not necessary */
+  }/* endswitch send */
+
+
+  /*
+   * UPLINK_NORMAL, UPLINK_IFRAME (_P), UPLINK_UA (_F),
+   * UPLINK_RR (_F), UPLINK_REJ (_F)
+   */
+  TRACE_EVENT_WIN_P5 ("%s SAPI=%u delete vtx=%s ->EMPTY_CMD (%s#%u)",
+    CH_TYPE_NAME[pcch->ch_type], sapi, VTX_NAME[pcch->vtx], __FILE10__, __LINE__);
+  pcch->vtx = EMPTY_CMD;
+
+  possible_reset_dcch0_ch_type();
+
+  return p_l2_frame;
+}/* endfunc dl_uplink */
+
+static int uplink_idle (UBYTE channel, UBYTE sapi)
+{
+  GET_INSTANCE_DATA;
+  T_CCH*          pcch = &dl_data->cch[channel];
+  int             ret = UPLINK_NORMAL;
+  int             ua_response = FALSE;
+
+  TRACE_EVENT_WIN_P3 ("uplink_idle(): %s %s SAPI=%d",
+    PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi);
+
+  switch (channel)
+  {
+  case C_DCCH0:
+    switch (pcch->vtx)
+    {
+    case UA_CMD:
+      ret = pcch->f_bit ? UPLINK_UA_F : UPLINK_UA;
+      break;
+    case DM_CMD:
+      com_build_DM_response (pcch->ch_type, sapi, pcch->f_bit);
+      break;
+    default:
+      ret = UPLINK_EMPTY;/* no SAPI=3 frame if DCCH0 is idle */
+      break;
+    }
+    break;
+
+  case C_DCCH3:
+    switch (pcch->vtx)
+    {
+    case UA_CMD:
+      ua_response = TRUE;
+      ret = pcch->f_bit ? UPLINK_UA_F : UPLINK_UA;
+      break;
+    case DM_CMD:
+      com_build_DM_response (pcch->ch_type, PS_SAPI_3, 1);
+      ret = UPLINK_NORMAL;
+      break;
+    default:
+      ret = UPLINK_EMPTY;
+      break;
+    }
+    break;
+  }/* endswitch channel */
+
+  if (ua_response AND (ret EQ UPLINK_UA_F))
+  {
+    drr_dl_establish_ind (pcch->ch_type, sapi,
+      (UBYTE)(com_queue_awaiting_transmission (sapi) ? DL_UNSERVED : DL_ALL_DONE));
+    set_channel_state (channel, STATE_MULTIPLE_FRAME_ESTABLISHED);
+  }
+
+  return ret;
+}/* endfunc uplink_idle */
+
+static int uplink_awaiting_establishment (UBYTE channel, UBYTE sapi,
+                                 UBYTE no_signalling_mode)
+{
+  GET_INSTANCE_DATA;
+  T_CCH*  pcch = &dl_data->cch[channel];
+  int   ret = UPLINK_NORMAL;
+  int   ua_response = FALSE;
+  int   sabm_command = 0;
+
+  TRACE_EVENT_WIN_P3 ("uplink_awaiting_establishment(): %s %s SAPI=%d",
+    PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi);
+
+  if (pcch->time_flag)
+    set_T200_counter (pcch, sapi);
+
+  switch (channel)
+  {
+  case C_DCCH0:
+    if (pcch->vtx EQ SABM_CMD)
+    {
+      if (pcch->contention_resolution)
+        sabm_command = 2; /* com_build_SABM with L3 */
+      else
+        sabm_command = 1; /* com_build_SABM without L3 */
+    }
+    else
+    {
+      ret = UPLINK_EMPTY;/* no SAPI=0 frame is waiting transmission */
+    }
+    break;
+
+  case C_DCCH3:
+    switch (pcch->vtx)
+    {
+    case SABM_CMD:
+      sabm_command = 1; /* com_build_SABM without L3 */
+      break;
+    case UA_CMD:
+      ua_response = TRUE;
+      break;
+    case DM_CMD:
+      com_build_DM_response (pcch->ch_type, sapi, pcch->f_bit);
+      break;
+    default:
+      ret = UPLINK_EMPTY;
+      break;
+    }
+
+    break;
+  }
+
+  if (ua_response)
+  {
+    drr_dl_establish_cnf (pcch->ch_type, sapi);
+    set_channel_state (channel, STATE_MULTIPLE_FRAME_ESTABLISHED);
+
+    ret = pcch->f_bit ? UPLINK_UA_F : UPLINK_UA;
+  }
+  else if (sabm_command)
+  {
+    com_build_SABM(pcch->ch_type, sapi, sabm_command EQ 2);
+  }
+
+  return ret;
+}/* endfunc uplink_awaiting_establishment */
+
+static int uplink_mfe (UBYTE channel, UBYTE sapi, UBYTE no_signalling_mode)
+{
+  GET_INSTANCE_DATA;
+  T_CCH*  pcch = &dl_data->cch[channel];
+  int   ret = UPLINK_NORMAL;
+
+  TRACE_EVENT_WIN_P6 ("uplink_mfe(): %s:%s SAPI=%d vtx=%s (vs=%u va+1=%u)",
+    PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi,
+    VTX_NAME[pcch->vtx], pcch->vs, (pcch->va + 1)&7 );
+
+  if (pcch->time_flag)
+    set_T200_counter (pcch, sapi);
+
+  switch (pcch->vtx)
+  {
+  case RR_CMD:
+    if (pcch->f_bit_flag)
+    {
+      ret = UPLINK_RR_F;
+      pcch->f_bit_flag = 0;
+    }
+    else
+    {
+      if (!com_queue_awaiting_transmission (sapi))
+        ret = UPLINK_RR;
+      else
+      {
+        if (pcch->vs EQ ((pcch->va + 1)&7))
+          ret = UPLINK_RR;
+        else
+          ret = UPLINK_IFRAME;
+      }
+    }
+    break;
+
+  case UA_CMD:
+    ret = pcch->f_bit ? UPLINK_UA_F : UPLINK_UA;
+    break;
+
+  case RR_RSP:
+    ret = pcch->f_bit ? UPLINK_RR_F : UPLINK_RR;
+    pcch->f_bit_flag = 0;
+    break;
+
+  case REJ_CMD:
+    ret = pcch->f_bit ? UPLINK_REJ_F : UPLINK_REJ;
+    pcch->f_bit_flag = 0;
+    break;
+
+  default:  /* vtx = ELSE */
+    switch (channel)
+    {
+    case C_DCCH0:
+      if (com_queue_awaiting_transmission (PS_SAPI_0)
+            AND
+          (pcch->vs NEQ ((pcch->va + 1)&7)))
+        ret = UPLINK_IFRAME;
+      else
+      {
+        ret = UPLINK_EMPTY;/* no SAPI=0 frame is waiting transmission */
+      }
+      break;
+
+    case C_DCCH3:
+      if (com_queue_awaiting_transmission (PS_SAPI_3))
+      {
+        if (pcch->vs EQ ((pcch->va + 1)&7))
+          ret = UPLINK_EMPTY;
+        else
+          ret = UPLINK_IFRAME;
+      }
+      else
+      {
+        ret = dcch3_check_disc (UPLINK_EMPTY);/* no SAPI=3 frame is waiting transmission */
+      }
+      break;
+    }/* endswitch channel */
+    break;
+  }/* endswitch vtx */
+
+
+  return ret;
+}/* endfunc uplink_mfe */
+
+static int uplink_timer_recovery (UBYTE channel, UBYTE sapi, UBYTE no_signalling_mode)
+{
+  GET_INSTANCE_DATA;
+  T_CCH*  pcch = &dl_data->cch[channel];
+  int   ret = UPLINK_NORMAL;
+
+  TRACE_EVENT_WIN_P3 ("uplink_timer_recovery(): %s %s SAPI=%d",
+    PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi);
+
+  if (pcch->time_flag)
+    set_T200_counter (pcch, sapi);
+
+  switch (pcch->vtx)
+  {
+  case RR_CMD:
+    if (pcch->f_bit_flag)
+    {
+      ret = UPLINK_RR_F;
+      pcch->f_bit_flag = 0;
+    }
+    else
+    {
+      if (pcch->p_bit_flag)
+        ret = UPLINK_IFRAME_P;
+      else
+        ret = UPLINK_RR;
+    }
+    break;
+
+  case UA_CMD:
+    ret = pcch->f_bit ? UPLINK_UA_F : UPLINK_UA;
+    break;
+
+  case RR_RSP:
+    ret = pcch->f_bit ? UPLINK_RR_F : UPLINK_RR;
+    pcch->f_bit_flag = 0;
+    break;
+
+  case REJ_CMD:
+    ret = pcch->f_bit ? UPLINK_REJ_F : UPLINK_REJ;
+    pcch->f_bit_flag = 0;
+    break;
+
+  default:  /* vtx = ELSE */
+    TRACE_EVENT_WIN_P3 ("%s SAPI=%u vtx=%s",
+      CH_TYPE_NAME[pcch->ch_type], sapi, VTX_NAME[pcch->vtx]);
+
+    if (pcch->p_bit_flag)
+      ret = UPLINK_IFRAME_P;
+    else
+      switch (channel)
+      {
+      case C_DCCH0:
+        ret = UPLINK_EMPTY;/* no SAPI=0 frame is waiting transmission */
+        break;
+
+      case C_DCCH3:
+        ret = dcch3_check_disc (UPLINK_EMPTY);/* no SAPI=3 frame is waiting transmission */
+        break;
+      }
+    break;
+  }
+
+#if 0 /* decrement first short before sending the frame */
+  if (ret EQ UPLINK_IFRAME_P)
+  {
+    pcch->vs--;
+    pcch->vs &= 7;
+    pcch->p_bit_flag = 0;
+  }
+#endif /* 0 */
+  return ret;
+}/* endfunc uplink_timer_recovery */
+
+GLOBAL int uplink_awaiting_release (UBYTE channel, UBYTE sapi)
+{
+  GET_INSTANCE_DATA;
+  T_CCH*  pcch = &dl_data->cch[channel];
+  int   ret = UPLINK_NORMAL;
+
+  TRACE_EVENT_WIN_P3 ("uplink_awaiting_release(): %s %s SAPI=%d",
+    PROCESS_NAME[channel], CH_TYPE_NAME[pcch->ch_type], sapi);
+
+  if (pcch->time_flag)
+    set_T200_counter (pcch, sapi);
+
+  switch (pcch->vtx)
+  {
+  case DISC_CMD:
+    /* DL_OFFLINE_TRACE (TRACE_DL_EVENT, channel, pcch->ch_type, "UL:Send DISC"); */
+    com_build_DISC_command (pcch->ch_type, sapi, 1);
+    break;
+  case UA_CMD:
+    set_channel_state (channel, STATE_IDLE_DL);
+
+#if defined(DELAYED_RELEASE_IND)
+    switch (pcch->ch_type)
+    {
+    #if defined(DL_FACCH_RELEASE_DELAY_VALUE) && (DL_FACCH_RELEASE_DELAY_VALUE > 0)
+      case L2_CHANNEL_FACCH_F:
+      case L2_CHANNEL_FACCH_H:
+          /* delay DL RELEASE IND to RR for testcase 25.2.3 */
+          dl_data->release_ind_ch_type = pcch->ch_type;
+          dl_data->release_ind_sapi = sapi;
+          if (dl_data->release_ind_delay EQ 0)
+          {
+            dl_data->release_ind_delay = DL_FACCH_RELEASE_DELAY_VALUE;
+            DL_OFFLINE_TRACE (TRACE_DL_EVENT, C_DCCH0, pcch->ch_type, "delay REL IND on FACCH");
+          }
+        break;
+    #endif /* DL_FACCH_RELEASE_DELAY_VALUE */
+    #if defined(DL_SDCCH_RELEASE_DELAY_VALUE) && (DL_SDCCH_RELEASE_DELAY_VALUE > 0)
+      case L2_CHANNEL_SDCCH:
+          /* delay DL RELEASE IND to RR for testcase 25.2.3 */
+          dl_data->release_ind_ch_type = pcch->ch_type;
+          dl_data->release_ind_sapi = sapi;
+          if (dl_data->release_ind_delay EQ 0)
+          {
+            dl_data->release_ind_delay = DL_SDCCH_RELEASE_DELAY_VALUE;
+            DL_OFFLINE_TRACE (TRACE_DL_EVENT, channel, pcch->ch_type, "delay REL IND on SDCCH");
+          }
+        break;
+    #endif /* DL_SDCCH_RELEASE_DELAY_VALUE */
+      default:
+        drr_dl_release_ind (pcch->ch_type, sapi, NOT_PRESENT_8BIT, TRUE);
+        break;
+    }
+#else  /* DELAYED_RELEASE_IND */
+    drr_dl_release_ind (pcch->ch_type, sapi, NOT_PRESENT_8BIT, TRUE);
+#endif  /* DELAYED_RELEASE_IND */
+
+#if defined(LATE_LEAVING_DEDICATED)
+    com_leave_dedicated (pcch->ch_type);
+#endif  /* LATE_LEAVING_DEDICATED */
+    ret = UPLINK_UA_F;
+    break;
+
+  default:
+    switch (pcch->ch_type)
+    {
+      case L2_CHANNEL_SACCH:
+        if (channel EQ C_DCCH0)
+          ret = UPLINK_REPORT;
+        break;
+      default:
+        if (channel EQ C_DCCH0)
+        {
+          pcch = &dl_data->cch[C_DCCH3];
+          if (pcch->time_flag)
+            set_T200_counter (pcch, PS_SAPI_3);
+        }
+        ret = UPLINK_EMPTY;
+      break;
+    }/* endswitch channel */
+  }/* endswitch vtx */
+
+  return ret;
+}/* endfunc uplink_awaiting_release */
+
+#if defined(CHECK_PCCHI)
+static void check_pcch_i (T_CCH_INTERN* pcch_i, int line)
+{
+  char buf[23];
+  int ret = 0;
+
+  if (pcch_i EQ NULL)
+    ret = -1;
+  else if (pcch_i->pcch EQ NULL)
+    ret = -2;
+
+  if (ret)
+  {
+    sprintf (buf, "#%d pcch=NULL %d", line, -ret);
+    DL_OFFLINE_TRACE (TRACE_DL_EVENT, TRACE_CH_UNKNOWN, 0,buf);
+  }
+
+  return ret;
+}/* endfunc check_pcch_i */
+#endif  /* CHECK_PCCHI */
+
+#endif /* DL_C */