view src/g23m-gsm/dl/dl_state.c @ 303:f76436d19a7a default tip

!GPRS config: fix long-standing AT+COPS chance hanging bug There has been a long-standing bug in FreeCalypso going back years: sometimes in the AT command bring-up sequence of an ACI-only MS, the AT+COPS command would produce only a power scan followed by cessation of protocol stack activity (only L1 ADC traces), instead of the expected network search sequence. This behaviour was seen in different FC firmware versions going back to Citrine, and seemed to follow some law of chance, not reliably repeatable. This bug has been tracked down and found to be specific to !GPRS configuration, stemming from our TCS2/TCS3 hybrid and reconstruction of !GPRS support that was bitrotten in TCS3.2/LoCosto version. ACI module psa_mms.c, needed only for !GPRS, was missing in the TCS3 version and had to be pulled from TCS2 - but as it turns out, there is a new field in the MMR_REG_REQ primitive that needs to be set correctly, and that psa_mms.c module is the place where this initialization needed to be added.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 08 Jun 2023 08:23:37 +0000
parents fa8dc04885d8
children
line wrap: on
line source

/*
+-----------------------------------------------------------------------------
|  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 */