view src/g23m-aci/uart/uart_kerf.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 :
|  Modul   :
+-----------------------------------------------------------------------------
|  Copyright 2002 Texas Instruments Berlin, AG
|                 All rights reserved.
|
|                 This file is confidential and a trade secret of Texas
|                 Instruments Berlin, AG
|                 The receipt of or possession of this file does not convey
|                 any rights to reproduce or disclose its contents or to
|                 manufacture, use, or sell anything it may describe, in
|                 whole, or in part, without the specific written consent of
|                 Texas Instruments Berlin, AG.
+-----------------------------------------------------------------------------
|  Purpose :  This modul is part of the entity UART and implements all
|             procedures and functions as described in the
|             SDL-documentation (KER-statemachine)
+-----------------------------------------------------------------------------
*/

#ifndef UART_KERF_C
#define UART_KERF_C
#endif /* !UART_KERF_C */

#define ENTITY_UART

/*
 * Turn off spurious LINT warnings
 */
 /*lint -e415 access of out-of-bounds pointer */
 /*lint -e416 creation of out-of-bounds pointer */
 /*lint -e661 possible access of out-of-bounds pointer */
 /*lint -e662 possible craetion of out-of-bounds pointer */


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

#ifdef WIN32
#include "nucleus.h"
#endif /* WIN32 */
#include "typedefs.h"   /* to get Condat data types */
#include "vsi.h"        /* to get a lot of macros */
#include "macdef.h"     /* to get a lot of macros */
#include "custom.h"
#include "gsm.h"        /* to get a lot of macros */
#include "cnf_uart.h"   /* to get cnf-definitions */
#include "mon_uart.h"   /* to get mon-definitions */
#include "prim.h"       /* to get the definitions of used SAP and directions */
#include "dti.h"        /* to get dti lib */
#include "pei.h"        /* to get PEI interface */
#ifdef FF_MULTI_PORT
#include "gsi.h"        /* to get definitions of serial driver */
#else /* FF_MULTI_PORT */
#ifdef _TARGET_
#include "uart/serialswitch.h"
#include "uart/traceswitch.h"
#else /* _TARGET_ */
#include "serial_dat.h" /* to get definitions of serial driver */
#endif /* _TARGET_ */
#endif /* FF_MULTI_PORT */
#include "uart.h"       /* to get the global entity definitions */

#include "uart_kerf.h"  /* to get KER function definitions */
#include "uart_drxs.h"  /* to get signal definitions for service DRX */
#include "uart_dtxs.h"  /* to get signal definitions for service DTX */
#ifdef FF_MULTI_PORT
#include "uart_ptxs.h"  /* to get signal definitions for service TX */
#else /* FF_MULTI_PORT */
#include "uart_txs.h"   /* to get signal definitions for service TX */
#endif /* FF_MULTI_PORT */
#include "uart_rts.h"   /* to get signal definitions for service RT */
#include <string.h>    /* JK, delete warnings: to get memmove */
/*==== CONST ================================================================*/

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

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

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



/*
+------------------------------------------------------------------------------
| Function    : ker_setupUart
+------------------------------------------------------------------------------
| Description : The function ker_setupUart() sets the communication parameters
|               of UART
|
| Parameters  : no parameter
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_setupUart(void)
{
  T_UFRET ret; /* Error code returned from a function */

  TRACE_FUNCTION( "ker_setupUart" );

#ifdef FF_MULTI_PORT
  /*
   * set new XON / XOFF character
   */
  uart_data->xon  = uart_data->ker.act_dcb.XON;
  uart_data->xoff = uart_data->ker.act_dcb.XOFF;
  /*
   * set new parameters
   */
#ifndef _SIMULATION_
  if((ret = GSI_SetConfig(uart_data->device, &uart_data->ker.act_dcb))
                                                           NEQ DRV_OK)
  {
    TRACE_ERROR_P2
      ("GSI driver: Serial devise configuration failed; [%d], uart_kerf.c(%d)",
                                                                ret, __LINE__);
  }
#endif /* !_SIMULATION_ */
#else /* FF_MULTI_PORT */
  /*
   * set new XON / XOFF character
   */
  uart_data->xon  = uart_data->ker.act_xon;
  uart_data->xoff = uart_data->ker.act_xoff;
  /*
   * set new escape sequence parameters
   */
  uart_data->act_ec = uart_data->ker.act_ec;
  uart_data->act_gp = uart_data->ker.act_gp;
  /*
   * set new parameters
   */
  /*
   * set up the escape sequence
   */
  ret = UF_SetEscape (uart_data->device,
                      uart_data->act_ec,
                      uart_data->act_gp);
#ifdef _SIMULATION_
  TRACE_EVENT_P1 ("UF_SetEscape() = %x", (USHORT) ret);
#endif /* _SIMULATION_ */
  while ((ret = UF_SetComPar (uart_data->device,
                              uart_data->ker.act_br,
                              uart_data->ker.act_bpc,
                              uart_data->ker.act_sb,
                              uart_data->ker.act_par)) EQ UF_NOT_READY)
  {
    if(vsi_t_sleep (VSI_CALLER ONE_FRAME) NEQ VSI_OK)
    {
      TRACE_ERROR_P1("VSI entity: Can't suspend thread, uart_kerf.c(%d)",
                                                               __LINE__);
    }
  }

  /*
   * set new flow control
   */
  if (ret EQ UF_OK)
  {
    if((ret = UF_SetFlowCtrl (uart_data->device, uart_data->ker.act_fc_rx,
                              uart_data->xon, uart_data->xoff) NEQ UF_OK)
                              AND (uart_data->device NEQ 0))
    {
      TRACE_ERROR_P2("UF driver: Can't set new flow control, [%d], uart_kerf(%d)",
                                                                   ret, __LINE__);
    }
  }
#endif /* FF_MULTI_PORT */
} /* ker_setupUart() */



/*
+------------------------------------------------------------------------------
| Function    : ker_init
+------------------------------------------------------------------------------
| Description : The function ker_init() initializes the UART
|
| Parameters  : no parameter
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_init ()
{
#ifdef FF_MULTI_PORT
#ifndef _SIMULATION_
  T_DRV_EXPORT* drv_export;
#endif /* !_SIMULATION_ */
#endif /* FF_MULTI_PORT */

  T_UFRET ret;       /* Error code returned from a function */

  TRACE_FUNCTION( "ker_init" );

  /*
   * initialize values
   */
#ifdef FF_MULTI_PORT
  uart_data->ker.act_dcb.Baud          = GSI_BAUD_9600;
  uart_data->ker.act_dcb.DataBits      = GSI_CHAR8;
  uart_data->ker.act_dcb.StopBits      = GSI_STOP1;
  uart_data->ker.act_dcb.Parity        = GSI_PARITYNO;
  uart_data->ker.act_dcb.RxFlowControl = GSI_FLOWHW;
  uart_data->ker.act_dcb.TxFlowControl = GSI_FLOWHW;
  uart_data->ker.act_dcb.RxBufferSize  = GSI_MAX_BUFFER_SIZE;
  uart_data->ker.act_dcb.TxBufferSize  = GSI_MAX_BUFFER_SIZE;
  uart_data->ker.act_dcb.RxThreshold   = 1;
  uart_data->ker.act_dcb.TxThreshold   = 1;
  uart_data->ker.act_dcb.XON           = UART_IO_XON_DEFAULT;
  uart_data->ker.act_dcb.XOFF          = UART_IO_XOFF_DEFAULT;
  uart_data->ker.act_dcb.EscChar       = UART_ESC_CHARACTER_DEFAULT;
  uart_data->ker.act_dcb.GuardPeriod   = UART_GUARD_PERIOD_DEFAULT;
#else /* FF_MULTI_PORT */
  uart_data->ker.act_br    = UF_BAUD_9600; /* 9600 baud */
  uart_data->ker.act_bpc   = bpc_8;        /* 8 bits per character */
  uart_data->ker.act_sb    = sb_1;         /* 1 stop bit */
  uart_data->ker.act_par   = pa_none;      /* no parity no space */
  uart_data->ker.act_fc_rx = fc_rts;       /* Hardware flow control */
  uart_data->ker.act_fc_tx = fc_rts;       /* Hardware flow control */
  uart_data->ker.act_xon   = UART_IO_XON_DEFAULT;  /* XOn character */
  uart_data->ker.act_xoff  = UART_IO_XOFF_DEFAULT; /* XOff character */
  uart_data->ker.act_ec    = UART_IO_ESC_CHAR_DEFAULT; /* escape character */
  uart_data->ker.act_gp    = UART_IO_ESC_GP_DEFAULT;   /* guard period */
#endif /* FF_MULTI_PORT */

  /* bitfield of received UART primitives */
  uart_data->ker.received_prim = 0;
  uart_data->ker.flush_state   = UART_KER_NOT_FLUSHING;

  uart_data->ker.rx_data_desc    = NULL;    /* data received from peer  */
  uart_data->ker.receiving_state = UART_KER_NOT_RECEIVING;

  uart_data->ker.tx_data_desc    = NULL;    /* data to be sent to peer */
  /* data waiting for access to tx_data_desc */
  uart_data->ker.tx_data_waiting = NULL;
  uart_data->ker.sending_state   = UART_KER_NOT_SENDING;
  uart_data->ker.data_flow_tx    = UART_FLOW_ENABLED;
  uart_data->ker.nr_t1           = 0;       /* nr running T1 timers yet */
  uart_data->ker.nr_t2           = 0;       /* nr running T2 timers yet */
  uart_data->ker.n2              = 0;       /* max nr of retransmissions */

#ifdef FF_MULTI_PORT
  /*
   * initialize driver
   */
#ifndef _SIMULATION_
  if((ret=GSI_Init(uart_data->device, uart_data->device,
                          pei_uart_driver_signal, &drv_export)) NEQ DRV_OK)
  {
    TRACE_ERROR_P2("GSI driver: InitSerialDevice failed, [%d], uart_kerf.c(%d)",
                                                                 ret, __LINE__);
  }
#endif /* _SIMULATION_ */
  /*
   * set driver signals
   */
#ifndef _SIMULATION_
  if((ret = GSI_SetSignal(uart_data->device, DRV_SIGTYPE_READ  |
                                             DRV_SIGTYPE_WRITE |
                                             DRV_SIGTYPE_FLUSH) NEQ DRV_OK)
  {
    TRACE_ERROR_P2("GSI entity: SetSignals failed, [%d], uart_kerf.c(%d)",
                                                           ret, __LINE__);
  }
#endif /* _SIMULATION_ */
#else /* FF_MULTI_PORT */
  /*
   * initialize driver
   */
  if((ret = UF_Init (uart_data->device)) NEQ UF_OK)
  {
    TRACE_ERROR_P2("UF driver: InitSerialDevice failed, [%d], uart_kerf.c(%d)",
                                                                ret, __LINE__);
  }
#ifdef _SIMULATION_
  TRACE_EVENT_P1 ("UF_Init() = %x", (USHORT) ret);
#endif /* _SIMULATION_ */

  /*
   * disable UART
   */
  if((ret = UF_Enable (uart_data->device, FALSE)) NEQ UF_OK)
  {
    TRACE_ERROR_P2("UF driver: DisableDriver failed, [%d], uart_kerf.c(%d)",
                                                             ret, __LINE__);
  }
  /*
   * set buffer size
   */
  if((ret = UF_SetBuffer (uart_data->device, UF_MAX_BUFFER_SIZE, 1, 1))
                                                             NEQ UF_OK)
  {
    TRACE_ERROR_P2("UF driver: SetBufferSize failed, [%d], uart_kerf.c(%d)",
                                                             ret, __LINE__);
  }
#ifdef _SIMULATION_
  TRACE_EVENT_P1 ("UF_SetBuffer() = %x", (USHORT) ret);
  TRACE_EVENT_P1 ("Buffer avail = %d",
                  (USHORT) UF_OutpAvail (uart_data->device));
#endif /* _SIMULATION_ */
#endif /* FF_MULTI_PORT */

  /*
   * set communication parameters
   */
  ker_setupUart();

  INIT_STATE( UART_SERVICE_KER , KER_DEAD );

} /* ker_init() */



/*
+------------------------------------------------------------------------------
| Function    : ker_analyze_frame_info_command
+------------------------------------------------------------------------------
| Description : The function ker_analyze_frame_info_command() analyzes the
|               information field of incoming frames.
|               The appropriate internal signals are triggered and a response
|               frame information field is generated.
|
|               Precondition is that the frame check sequence has been
|               verified, the flags have been removed from the frame and
|               message resonses have been removed from the frame.
|
| Parameters  : forward - result of analysis
|               frame   - descriptor which includes frame type
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_analyze_frame_info_command (ULONG* forward, T_desc2* frame)
{
  T_DLC*  dlc;
  UBYTE   dlci;
  USHORT  i;
  USHORT  pos;
  USHORT  len;

  TRACE_FUNCTION( "ker_analyze_frame_info_command" );

  pos = UART_OFFSET_INFO;

  /*
   * parse frame info field until last octet is reached
   * (minimal message has 2 bytes: type + value)
   */
  while(pos < frame->len)
  {
    len = 0;
    while(!(frame->buffer[pos + len] & UART_EA))
    {
      len++;
    }
    len+= (frame->buffer[pos + 1] >> UART_MSG_LENGTH_POS) + 2;
    switch(frame->buffer[pos])
    {
      case UART_MSG_TYPE_CLD_C:
        /*
         * Close Down
         */
        *forward|= UART_FORWARD_CLD;
        frame->buffer[pos] = UART_MSG_TYPE_CLD_R;
        break;
      case UART_MSG_TYPE_FCON_C:
        /*
         * Flow Control On
         * inform all DRX instances except Control channel
         */
        uart_data->ker.data_flow_tx = UART_FLOW_ENABLED;
        *forward|= UART_FORWARD_FCON;
        frame->buffer[pos] = UART_MSG_TYPE_FCON_R;
        break;
      case UART_MSG_TYPE_FCOFF_C:
        /*
         * Flow Control Off
         * inform all DRX instances except Control channel
         */
        uart_data->ker.data_flow_tx = UART_FLOW_DISABLED;
        *forward|= UART_FORWARD_FCOFF;
        frame->buffer[pos] = UART_MSG_TYPE_FCOFF_R;
        break;
      case UART_MSG_TYPE_MSC_C:
        /*
         * Modem Status Command
         * can be 2 or 3 octets
         * (depends if break signal is included or not)
         */
        dlci = frame->buffer[pos+2] >> UART_DLCI_POS;

        if((dlci NEQ UART_DLCI_CONTROL) &&
           (uart_data->dlc_instance[dlci] NEQ UART_EMPTY_INSTANCE))
        {
          dlc = &uart_data->dlc_table[uart_data->dlc_instance[dlci]];

          /*
           * set flow control
           */
          if(frame->buffer[pos+3] & UART_MSC_FC_MASK)
            dlc->lines|= UART_FC_RX_MASK;
          else
            dlc->lines&= ~UART_FC_RX_MASK;

          /*
           * set line states
           */
          if(frame->buffer[pos+3] & UART_MSC_RTR_MASK)
            dlc->lines&= ~UART_RTS_MASK;
          else
            dlc->lines|= UART_RTS_MASK;

          if(frame->buffer[pos+3] & UART_MSC_RTC_MASK)
            dlc->lines&= ~UART_DTR_MASK;
          else
            dlc->lines|= UART_DTR_MASK;

          if((len > 4) &&
             (frame->buffer[pos+4] & UART_MSC_BRK_MASK))
          {
            dlc->lines|= UART_BRK_RX_MASK;
            dlc->lines|= ((ULONG)(frame->buffer[pos+4] & UART_MSC_BRKLEN_MASK) >>
                          UART_MSC_BRKLEN_POS) <<
                          UART_BRKLEN_RX_POS;
          }
          *forward|= UART_FORWARD_MSC;
        }
        else
        {
          TRACE_EVENT( "sig_ker_ker_MSC_C: MSC for control channel or \
                                                 not established DLC" );
        };
        frame->buffer[pos] = UART_MSG_TYPE_MSC_R;
        break;
      default:
        TRACE_EVENT( "ker_analyze_frame_info_command: received \
                                     unsupported message type" );
        /*
         * create Non Supported Command response
         */
        i = 0;
        while(!(frame->buffer[pos + i] & UART_EA))
        {
          i++;
        }
        if(frame->len < uart_data->n1)
        {
          /*
           * move commands behind current command
           */
          if(frame->len > (pos + len))
          {
            if(len NEQ (i + 3))
            {
              memmove(&frame->buffer[pos + i + 3],
                      &frame->buffer[pos + len],
                      frame->len - pos - len)
              ;/*lint !e797 Conceivable creation of out-of-bounds pointer*/
              frame->len = frame->len - len + i + 3;
            }
          }
          else
            frame->len = pos + i + 3;
          /*
           * insert Non Supported Command
           */
          len = i + 3;
          /*lint -e669 -e670 (Warning -- data overrun/access beyond array) */
          memmove(&frame->buffer[pos + 2], &frame->buffer[pos], i + 1)
            ;/*lint !e803 !e804 Conceivable data overrun and access beyond array*/
          /*lint +e669 +e670 (Warning -- data overrun/access beyond array) */
          frame->buffer[pos + 1] = (((UBYTE)i + 1) << UART_MSG_LENGTH_POS) |
                                                      UART_EA;
          frame->buffer[pos]     = UART_MSG_TYPE_NSC_R;
        }
        else
        {
          /*
           * remove command
           */
          if(frame->len > (pos + len))
          {
            memmove(&frame->buffer[pos],
                    &frame->buffer[pos + len],
                    frame->len - pos - len);
            frame->len-= len;
          }
          else
            frame->len = pos;
          len = 0;
        }
        break;
    }
    pos+= len;
  }

} /* ker_analyze_frame_info_command() */



/*
+------------------------------------------------------------------------------
| Function    : ker_search_msg_type
+------------------------------------------------------------------------------
| Description : The function ker_search_msg_type() searches for a message type
|               in a frame.
|
| Parameters  : frame - descriptor which includes message type
|               pos   - position to start searching
|               type  - message type to search for
|
| Return      : indicator whether message type was found
|
+------------------------------------------------------------------------------
*/
LOCAL BOOL ker_search_msg_type (T_desc2* frame, USHORT* pos, UBYTE type)
{
  TRACE_FUNCTION( "ker_search_msg_type" );

  while(*pos < frame->len)
  {
    if(frame->buffer[*pos] EQ type)
    {
      return TRUE;
    }
    *pos+= (frame->buffer[*pos + 1] >> UART_MSG_LENGTH_POS) + 2;
  }
  return FALSE;
} /* ker_search_msg_type() */



/*
+------------------------------------------------------------------------------
| Function    : ker_analyze_frame_info_resonse
+------------------------------------------------------------------------------
| Description : The function ker_analyze_frame_info_response() analyzes the
|               information field of incoming frames.
|               The appropriate internal signals are triggered and the
|               responses are removed from the information field.
|
|               Precondition is that the frame check sequence has been verified
|               and that the flags have been removed from the frame.
|
| Parameters  : forward - result of analysis
|               frame   - descriptor which includes frame type
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_analyze_frame_info_response (ULONG* forward, T_desc2* frame)
{
  USHORT  pos;
  USHORT  len;
  T_DLC*  dlc;
  ULONG   forward_value;
  USHORT  search_pos;
  USHORT  search_len;
  UBYTE   search_command;
  BOOL    search_found;
  BOOL    search_whole_msg;
  USHORT  i;

  TRACE_FUNCTION( "ker_analyze_frame_info_response" );

  /*
   * check for correct message structure:
   * - minimal message length == 2 octets
   * - frame must end with the last message
   */
  pos = UART_OFFSET_INFO;
  while(pos < frame->len)
  {
    len = 0;
    if(!(frame->buffer[pos] & UART_EA))
    {
      /*
       * Type field greater than one octet
       */
      do
        len++;
      while(((pos + len) < frame->len) &&
            (!(frame->buffer[pos + len] & UART_EA)));
    }
    if(((pos + len + 1) < frame->len) &&
       (frame->buffer[pos + len + 1] & UART_EA))
    {
      len+= (frame->buffer[pos + 1] >> UART_MSG_LENGTH_POS) + 2;
      if((pos + len) > frame->len)
        /*
         * given length in length field to long
         * remove information field
         */
        frame->len = UART_OFFSET_INFO;
      else
        pos += len;
    }
    else
      /*
       * one octet length field expected, but not found
       * remove information field
       */
      frame->len = UART_OFFSET_INFO;
  }
  /*
   * parse frame info field until last octet is reached
   */
  pos = UART_OFFSET_INFO;
  while(pos < frame->len)
  {
    len = 0;
    while(!(frame->buffer[pos + len] & UART_EA))
    {
      len++;
    }
    len+= (frame->buffer[pos + 1] >> UART_MSG_LENGTH_POS) + 2;
    if(frame->buffer[pos] & UART_CR)
    {
      /*
       * command detected move to next message
       */
      pos+= len;
    }
    else
    {
      /*
       * analyze response message
       */
      switch( frame->buffer[pos] )
      {
        case UART_MSG_TYPE_CLD_R:
          /*
           * Close Down
           */
          dlc = &uart_data->dlc_table[UART_CONTROL_INSTANCE];
          search_command   = UART_MSG_TYPE_CLD_C;
          forward_value    = UART_FORWARD_CLD;
          search_whole_msg = TRUE;
          break;

        case UART_MSG_TYPE_FCON_R:
          /*
           * Flow Control On
           */
          dlc = &uart_data->dlc_table[UART_CONTROL_INSTANCE];
          search_command   = UART_MSG_TYPE_FCON_C;
          forward_value    = 0;
          search_whole_msg = TRUE;
          break;

        case UART_MSG_TYPE_FCOFF_R:
          /*
           * Flow Control Off
           */
          dlc = &uart_data->dlc_table[UART_CONTROL_INSTANCE];
          search_command   = UART_MSG_TYPE_FCOFF_C;
          forward_value    = 0;
          search_whole_msg = TRUE;
          break;

        case UART_MSG_TYPE_MSC_R:
          /*
           * Modem Status Command
           */
          dlc = &uart_data->dlc_table[UART_CONTROL_INSTANCE];
          search_command   = UART_MSG_TYPE_MSC_C;
          forward_value    = 0;
          search_whole_msg = TRUE;
          break;

        case UART_MSG_TYPE_NSC_R:
          /*
           * not supported command,
           */
          dlc = &uart_data->dlc_table[UART_CONTROL_INSTANCE];
          if(len > 2)
            search_command = frame->buffer[pos + 2];
          else
            search_command = 0;
          switch(search_command)
          {
            case UART_MSG_TYPE_CLD_C:
              forward_value = UART_FORWARD_CLD;
              break;
            default:
              forward_value = 0;
              break;
          }
          search_whole_msg = FALSE;
          break;

        default:
          TRACE_ERROR( "Error in ker_analyze_frame_info_response: \
                        Unsupported message type received");
          dlc = &uart_data->dlc_table[UART_CONTROL_INSTANCE];
          search_command   = 0;
          forward_value    = 0;
          search_whole_msg = TRUE;
          break;
      }
      /*
       * search and remove command message
       */
      if(dlc->last_command NEQ NULL)
      {
        search_pos   = UART_OFFSET_INFO;
        search_found = FALSE;
        while((search_found EQ FALSE) &&
              (ker_search_msg_type(dlc->last_command,
                                   &search_pos,
                                   search_command) EQ TRUE))
        {
          search_len   = (dlc->last_command->buffer[search_pos + 1] >>
                          UART_MSG_LENGTH_POS) + 2;
          search_found = TRUE;
          if(search_whole_msg EQ TRUE)
          {
            /*
             * check whole message
             */
            for(i=1; i < search_len; i++)
            {
              if(dlc->last_command->buffer[search_pos + i] NEQ
                             frame->buffer[pos + i])
                search_found = FALSE;
            }
          }
          if(search_found EQ TRUE)
          {
            /*
             * corresponding command message found
             * remove it
             */
            if(dlc->last_command->len > (search_pos + search_len))
            {
              memmove(&dlc->last_command->buffer[search_pos],
                      &dlc->last_command->buffer[search_pos + search_len],
                      dlc->last_command->len - search_pos - search_len);
              dlc->last_command->len-= search_len;
            }
            else
              dlc->last_command->len = search_pos;
            /*
             * set retransmissions to zero and
             * set forward parameter
             */
            dlc->retransmissions = 0;
            *forward            |= forward_value;
          }
          else
          {
            search_pos+= search_len;
          }
        }
      }
      /*
       * remove resonse message
       */
      if(frame->len > (pos + len))
      {
        memmove(&frame->buffer[pos],
                &frame->buffer[pos + len],
                frame->len - pos - len);
        frame->len-= len;
      }
      else
        frame->len = pos;
    }
  }
} /* ker_analyze_frame_info_response() */



/*
+------------------------------------------------------------------------------
| Function    : ker_mux_dlc_release
+------------------------------------------------------------------------------
| Description : This function closes one open multiplexer channel
|
| Parameters  : dlc_instance - instance of dlc to release
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_mux_dlc_release (UBYTE dlc_instance)
{
  T_DLC*  dlc;
  UBYTE   dlci;

  TRACE_FUNCTION( "ker_mux_dlc_release" );

  /*
   * set dlc values
   */
  dlc  = &uart_data->dlc_table[dlc_instance];
  dlci = dlc->dlci;
  /*
   * stop timer if this was the last running,
   * free copy of last command frame
   */
  if(dlc->last_command NEQ NULL)
  {
    if(dlc->last_command->buffer[UART_OFFSET_CONTROL] EQ
        UART_UIH_CONTROL_FRAME)
    {
      uart_data->ker.nr_t2--;
      if( uart_data->ker.nr_t2 EQ 0 )
        sig_ker_rt_stop_t2_req();
    }
    else
    {
      uart_data->ker.nr_t1--;
      if( uart_data->ker.nr_t1 EQ 0 )
        sig_ker_rt_stop_t1_req();
    }
    MFREE_DESC2(dlc->last_command);
    dlc->last_command = NULL;
  }
  /*
   * set connection state
   */
  dlc->connection_state = UART_CONNECTION_DEAD;
  /*
   * remove DLC instance
   */
  if(dlc->next_command NEQ NULL)
  {
    MFREE_DESC2(dlc->next_command);
    dlc->next_command = NULL;
  }
  uart_data->dlc_instance[dlci] = UART_EMPTY_INSTANCE;
  dlc->dlci                     = UART_DLCI_INVALID;
  /*
   * close DTI connection
   */
  uart_data->drx = dlc->drx;
  uart_data->dtx = dlc->dtx;
  sig_ker_drx_dead_mode_req();
  sig_ker_dtx_dead_mode_req();
  if(dlc->dti_state NEQ DTI_CLOSED)
  {
    dti_close(uart_hDTI, uart_data->device,
              UART_DTI_UP_INTERFACE, dlc_instance, FALSE);
    dlc->dti_state = DTI_CLOSED;
  }
} /* ker_mux_dlc_release() */



/*
+------------------------------------------------------------------------------
| Function    : ker_mux_close_down
+------------------------------------------------------------------------------
| Description : This function closes all currently open multiplexer channels.
|
| Parameters  : no parameter
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_mux_close_down ()
{
  UBYTE   i;

  TRACE_FUNCTION( "ker_mux_close_down" );

  /*
   * close all channels
   */
  for(i = 0; i <= UART_MAX_NUMBER_OF_CHANNELS; i++)
  {
    /*
     * set dlc values
     */
    if(uart_data->dlc_table[i].dlci NEQ UART_DLCI_INVALID)
      ker_mux_dlc_release(i);
  }
  /*
   * stop timers
   */
  uart_data->ker.nr_t1 = 0;
  sig_ker_rt_stop_t1_req();
  uart_data->ker.nr_t2 = 0;
  sig_ker_rt_stop_t2_req();
  sig_ker_rt_stop_t3_req();

} /* ker_mux_close_down() */

/*
+------------------------------------------------------------------------------
| Function    : ker_mux_send_frame
+------------------------------------------------------------------------------
| Description : This function is used to send out a frame in multiplexer mode.
|               It checks if the KER service is currently sending. If not, the
|               descriptor is put in the output queue for the TX service and
|               the service is notified that data is available. If the KER
|               service is in state sending, the descriptor is put in a second
|               queue for later processing.
|
| Parameters  : frame - descriptor with frame to send
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_mux_send_frame (T_desc2* frame)
{
  T_desc2* desc;

  TRACE_FUNCTION( "ker_mux_send_frame" );

  if(uart_data->ker.tx_data_desc)
  {
    /*
     * currently sending, put frame in waiting queue
     */
    desc = uart_data->ker.tx_data_waiting;
    if(desc)
    {
      while(desc->next NEQ (ULONG)NULL)
        desc = (T_desc2*)desc->next;
      desc->next = (ULONG)frame;
    }
    else
      uart_data->ker.tx_data_waiting = frame;
  }
  else
  {
    /*
     * send frame immediately
     */
    uart_data->ker.tx_data_desc = frame;
    sig_ker_tx_data_available_req(uart_data->ker.tx_data_desc, 0);
  }
} /* ker_mux_send_frame() */



/*
+------------------------------------------------------------------------------
| Function    : ker_mux_send_command_frame
+------------------------------------------------------------------------------
| Description : This function is used to send out a command frame in
|               multiplexer mode.
|               It enables the response timer and saves a copy of the
|               command frame in the DLC's last_command variable so the
|               frame can be retransmitted if the timer expires.
|
| Parameters  : dlc_instance - dlc instance the command frame belongs to
|               frame        - descriptor with command frame to send
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_mux_send_command_frame (UBYTE dlc_instance, T_desc2* frame)
{
  T_DLC*   dlc;
  T_desc2* desc;

  TRACE_FUNCTION( "ker_mux_send_command_frame" );

  dlc = &uart_data->dlc_table[dlc_instance];
  /*
   * copy frame
   */
  if(dlc->last_command NEQ NULL)
  {
    /*
     * currently sending, put command frame in waiting queue
     */
    desc = dlc->next_command;
    if(desc)
    {
      while(desc->next NEQ (ULONG)NULL)
        desc = (T_desc2*)desc->next;
      desc->next = (ULONG)frame;
    }
    else
      dlc->next_command = frame;
  }
  else
  {
    MALLOC(dlc->last_command, (USHORT)(sizeof( T_desc2 ) - 1 +
                               frame->len));

    dlc->last_command->next = (ULONG)NULL;
    dlc->last_command->len  = frame->len;
    memcpy(dlc->last_command->buffer, frame->buffer, frame->len);

    /*
     * set response timer and counter
     */
    dlc->retransmissions = 0;
    if(frame->buffer[UART_OFFSET_CONTROL] EQ UART_UIH_CONTROL_FRAME)
    {
      /*
       * usual UIH Command frame
       * use T2 timer
       */
      uart_data->ker.nr_t2++;
      sig_ker_rt_start_t2_req();
    }
    else
    {
      /*
       * DISC frame
       * use T1 timer
       */
      uart_data->ker.nr_t1++;
      sig_ker_rt_start_t1_req();
    }

    /*
     * use the usual frame send function to transmit the frame
     */
    ker_mux_send_frame( frame );
  }
} /* ker_mux_send_command_frame() */



/*
+------------------------------------------------------------------------------
| Function    : ker_mux_send_line_states
+------------------------------------------------------------------------------
| Description : This function is used to send out a frame in multiplexer mode.
|               It creates an UIH frame with MSC command and includes new line
|               states.
|
| Parameters  : dlc_instance - instance of DLC
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_mux_send_line_states(UBYTE dlc_instance)
{
  T_DLC*   dlc;
  T_desc2* frame;
  USHORT   pos;
  ULONG    line_states;

  TRACE_FUNCTION( "ker_mux_send_line_states" );

  dlc         = &uart_data->dlc_table[dlc_instance];
  line_states = dlc->lines;
  /*
   * allocate memory
   */
  MALLOC(frame, (USHORT)(sizeof( T_desc2 ) - 1 + uart_data->n1 + 2));
  frame->next = (ULONG)NULL;

  /*
   * fill frame
   */
    /*
     * address field
     */
  pos = 0;
  frame->buffer[pos] = (UART_DLCI_CONTROL << UART_DLCI_POS) | UART_EA;
  pos++;
    /*
     * control field
     */
  frame->buffer[pos] = UART_UIH_CONTROL_FRAME;
  pos++;
    /*
     * type field
     */
  frame->buffer[pos] = UART_MSG_TYPE_MSC_C;
  pos++;
    /*
     * length field
     */
  if(line_states & UART_BRK_TX_MASK)
    /*
     * length 3 with break field
     */
    frame->buffer[pos] = (3 << UART_MSG_LENGTH_POS) | UART_EA;
  else
    /*
     * length 2 without break field
     */
    frame->buffer[pos] = (2 << UART_MSG_LENGTH_POS) | UART_EA;
  pos++;
    /*
     * DLCI field
     */
  frame->buffer[pos] = (dlc->dlci << UART_DLCI_POS) | UART_CR | UART_EA;
  pos++;
    /*
     * V.24 signals
     */
  frame->buffer[pos] = UART_EA;
  if(!(line_states & UART_DCD_MASK))
    frame->buffer[pos] |= UART_MSC_DV_MASK;

  if(line_states & UART_RI_MASK)
    frame->buffer[pos] |= UART_MSC_IC_MASK;

  if(!(line_states & UART_CTS_MASK))
    frame->buffer[pos] |= UART_MSC_RTR_MASK;

  if(!(line_states & UART_DSR_MASK))
    frame->buffer[pos] |= UART_MSC_RTC_MASK;

  if(line_states & UART_FC_TX_MASK)
    frame->buffer[pos] |= UART_MSC_FC_MASK;

  pos++;

  /*
   * break signal
   */
  if(line_states & UART_BRK_TX_MASK)
  {
    frame->buffer[pos] = (((UBYTE)((line_states & UART_BRKLEN_TX_MASK) >>
                                                  UART_BRKLEN_TX_POS)) <<
                                                  UART_MSC_BRKLEN_POS) |
                                                  UART_MSC_BRK_MASK |
                                                  UART_EA;
    pos++;
    /*
     * break sent, so clear break flag
     */
    dlc->lines&= ~UART_BRK_TX_MASK;
  }


  /*
   * send frame
   */
  frame->len = pos;
  ker_mux_send_command_frame(UART_CONTROL_INSTANCE, frame);

} /* ker_mux_send_line_states() */



/*
+------------------------------------------------------------------------------
| Function    : ker_mux_send_close_down
+------------------------------------------------------------------------------
| Description : This function is used to send out a frame in multiplexer mode.
|               It creates an UIH frame with CLD command
|
| Parameters  : no parameters
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_mux_send_close_down()
{
  T_desc2* frame;
  USHORT   pos;

  TRACE_FUNCTION( "ker_mux_send_close_down" );

  /*
   * allocate memory
   */
  MALLOC(frame, (USHORT)(sizeof( T_desc2 ) - 1 + uart_data->n1 + 2));
  frame->next = (ULONG)NULL;

  /*
   * fill frame
   */
    /*
     * address field
     */
  pos = 0;
  frame->buffer[pos] = (UART_DLCI_CONTROL << UART_DLCI_POS) | UART_EA;
  pos++;
    /*
     * control field
     */
  frame->buffer[pos] = UART_UIH_CONTROL_FRAME;
  pos++;
    /*
     * type field
     */
  frame->buffer[pos] = UART_MSG_TYPE_CLD_C;
  pos++;
    /*
     * length field
     */
  frame->buffer[pos] = UART_EA;
  pos++;

  /*
   * send frame
   */
  frame->len = pos;
  frame->size = pos;
  frame->offset = 0;
  ker_mux_send_command_frame(UART_CONTROL_INSTANCE, frame);
} /* ker_mux_send_close_down() */



/*
+------------------------------------------------------------------------------
| Function    : ker_send_disc_frame
+------------------------------------------------------------------------------
| Description : This function is used to send out a frame in multiplexer mode.
|               It creates an DISC frame and sends it.
|
| Parameters  : none
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_send_disc_frame(UBYTE dlci)
{
  T_desc2* frame;
  USHORT   pos;

  TRACE_FUNCTION( "ker_send_disc_frame" );

  /*
   * allocate memory
   */
  MALLOC(frame, (USHORT)(sizeof(T_desc2) - 1 + 2));
  frame->next = (ULONG)NULL;

  /*
   * fill frame
   */
    /*
     * address field
     */
  pos = 0;
  frame->buffer[pos] = (dlci << UART_DLCI_POS) | UART_EA;
  pos++;
    /*
     * control field
     */
  frame->buffer[pos] = UART_DISC_FRAME;
  pos++;

  /*
   * send frame
   */
  frame->len = pos;
  ker_mux_send_command_frame(uart_data->dlc_instance[dlci], frame);

} /* ker_send_disc_frame */



/*
+------------------------------------------------------------------------------
| Function    : ker_receive_sabm_frame
+------------------------------------------------------------------------------
| Description : This function analyzes received SABM frames.
|
| Parameters  : forward - result of analysis
|               frame   - frame to analyze
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_receive_sabm_frame(ULONG* forward, T_desc2* frame)
{
  T_DLC*  dlc;
  UBYTE   dlci;
  UBYTE   dlc_instance;
  UBYTE   i;

  TRACE_FUNCTION( "ker_receive_sabm_frame" );

  dlci         = frame->buffer[UART_OFFSET_ADDRESS] >> UART_DLCI_POS;
  dlc_instance = uart_data->dlc_instance[dlci];

  /*
   * analyze message responses
   */
  ker_analyze_frame_info_response(forward, frame);
  /*
   * check whether frame for an existing channel
   */
  if(dlc_instance != UART_EMPTY_INSTANCE)
  {
    /*
     * set DLC to this channel
     */
    dlc = &uart_data->dlc_table[dlc_instance];
    switch(dlc->connection_state)
    {
      case UART_CONNECTION_OPEN:
        /*
         * send UA frame
         */
        ker_analyze_frame_info_command(forward, frame);
        frame->buffer[UART_OFFSET_CONTROL] = UART_UA_FRAME;
        *forward                          |= UART_FORWARD_RESPONSE;
        break;

      case UART_CONNECTION_DISC_SENT:
        /*
         * send DM frame
         */
        ker_analyze_frame_info_command(forward, frame);
        frame->buffer[UART_OFFSET_CONTROL] = UART_DM_CONTROL_FRAME;
        *forward                          |= UART_FORWARD_RESPONSE;
        break;

      case UART_CONNECTION_SABM_RCVD:
        break;

      default:
        TRACE_ERROR( "DLC CONNECTION_STATE unexpected" );
        break;
    }
  }
  else
  {
    ker_analyze_frame_info_command(forward, frame);
    if( dlci EQ UART_DLCI_CONTROL )
    {
      /*
       * this is a SABM frame for the control channel,
       * therefore the appropriate instance is UART_CONTROL_INSTANCE
       */
      dlc = &uart_data->dlc_table[UART_CONTROL_INSTANCE];
      if(dlc->dlci EQ UART_DLCI_INVALID)
      {
        i = UART_CONTROL_INSTANCE;
        /*
         * mark DLC as opened
         */
        dlc->connection_state = UART_CONNECTION_SABM_RCVD;
        dlc->dlci             = dlci;

        /*
         * create UA response frame
         */
        frame->buffer[UART_OFFSET_CONTROL] = UART_UA_FRAME;
        *forward                          |= UART_FORWARD_SABM;
      }
      else
      {
        i = UART_MAX_NUMBER_OF_CHANNELS + 1;
      }
    }
    else
    {
      /*
       * if new, check whether there is a free channel left
       */
      for(i=0; i <= UART_MAX_NUMBER_OF_CHANNELS; i++ )
      {
        if(uart_data->dlc_table[i].dlci EQ UART_DLCI_INVALID)
        {
          dlc = &uart_data->dlc_table[i];
          /*
           * mark DLC as opened
           */
          dlc->connection_state = UART_CONNECTION_SABM_RCVD;
          dlc->dlci             = dlci;

          /*
           * create UA response frame
           */
          frame->buffer[UART_OFFSET_CONTROL] = UART_UA_FRAME;
          *forward                          |= UART_FORWARD_SABM;
          break;
        }
      }
    }
    if( i > UART_MAX_NUMBER_OF_CHANNELS )
    {
      /*
       * no free channel found, return DM frame
       */
      frame->buffer[UART_OFFSET_CONTROL] = UART_DM_CONTROL_FRAME;
    }
    *forward|= UART_FORWARD_RESPONSE;
  }
} /* ker_receive_sabm_frame */



/*
+------------------------------------------------------------------------------
| Function    : ker_receive_ua_frame
+------------------------------------------------------------------------------
| Description : This function analyzes received UA frames.
|
| Parameters  : forward - result of analysis
|               frame   - frame to analyze
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_receive_ua_frame(ULONG* forward, T_desc2* frame)
{
  T_DLC*  dlc;
  UBYTE   dlci;
  UBYTE   dlc_instance;

  TRACE_FUNCTION( "ker_receive_ua_frame" );

  dlci         = frame->buffer[UART_OFFSET_ADDRESS] >> UART_DLCI_POS;
  dlc_instance = uart_data->dlc_instance[dlci];

  /*
   * analyze message responses
   */
  ker_analyze_frame_info_response(forward, frame);
  /*
   * check whether frame for an existing channel
   */
  if(dlc_instance != UART_EMPTY_INSTANCE)
  {
    /*
     * set DLC to this channel
     */
    dlc = &uart_data->dlc_table[dlc_instance];
    switch(dlc->connection_state)
    {
      case UART_CONNECTION_DISC_SENT:
        /*
         * remove DISC frame
         */
        MFREE_DESC2(dlc->last_command);
        dlc->last_command = NULL;
        uart_data->ker.nr_t1--;
        if( uart_data->ker.nr_t1 EQ 0 )
          sig_ker_rt_stop_t1_req();
        /*
         * mark channel as closed
         */
        dlc->connection_state = UART_CONNECTION_DEAD;
        *forward             |= UART_FORWARD_DLC_RELEASE;
        break;

      case UART_CONNECTION_SABM_RCVD:
      case UART_CONNECTION_OPEN:
        break;

      default:
        TRACE_ERROR( "DLC CONNECTION_STATE unexpected" );
        break;
    }
  }
} /* ker_receive_ua_frame */



/*
+------------------------------------------------------------------------------
| Function    : ker_receive_dm_frame
+------------------------------------------------------------------------------
| Description : This function analyzes received DM frames.
|
| Parameters  : forward - result of analysis
|               frame   - frame to analyze
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_receive_dm_frame(ULONG* forward, T_desc2* frame)
{
  T_DLC*  dlc;
  UBYTE   dlci;
  UBYTE   dlc_instance;

  TRACE_FUNCTION( "ker_receive_dm_frame" );

  dlci         = frame->buffer[UART_OFFSET_ADDRESS] >> UART_DLCI_POS;
  dlc_instance = uart_data->dlc_instance[dlci];

  /*
   * analyze message responses
   */
  ker_analyze_frame_info_response(forward, frame);
  /*
   * check whether frame for an existing channel
   * and not for Control channel
   */
  if((dlc_instance NEQ UART_EMPTY_INSTANCE) &&
     (dlci NEQ UART_DLCI_CONTROL))
  {
    /*
     * set DLC to this channel
     */
    dlc = &uart_data->dlc_table[dlc_instance];
    switch(dlc->connection_state)
    {
      case UART_CONNECTION_DISC_SENT:
        /*
         * remove DISC frame
         */
        MFREE_DESC2(dlc->last_command);
        dlc->last_command = NULL;
        uart_data->ker.nr_t1--;
        if( uart_data->ker.nr_t1 EQ 0 )
          sig_ker_rt_stop_t1_req();
        /* fall through */
      case UART_CONNECTION_OPEN:
        /*
         * mark channel as closed
         */
        dlc->connection_state = UART_CONNECTION_DEAD;
        *forward             |= UART_FORWARD_DLC_RELEASE;
        break;

      case UART_CONNECTION_SABM_RCVD:
        break;

      default:
        TRACE_ERROR( "DLC CONNECTION_STATE unexpected" );
        break;
    }
  }
} /* ker_receive_dm_frame */



/*
+------------------------------------------------------------------------------
| Function    : ker_receive_disc_frame
+------------------------------------------------------------------------------
| Description : This function analyzes received DISC frames.
|
| Parameters  : forward - result of analysis
|               frame   - frame to analyze
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_receive_disc_frame(ULONG* forward, T_desc2* frame)
{
  T_DLC*  dlc;
  UBYTE   dlci;
  UBYTE   dlc_instance;

  TRACE_FUNCTION( "ker_receive_disc_frame" );

  dlci         = frame->buffer[UART_OFFSET_ADDRESS] >> UART_DLCI_POS;
  dlc_instance = uart_data->dlc_instance[dlci];

  /*
   * analyze messages
   */
  ker_analyze_frame_info_response(forward, frame);
  ker_analyze_frame_info_command(forward, frame);
  /*
   * check whether frame for an existing channel
   */
  if(dlc_instance NEQ UART_EMPTY_INSTANCE)
  {
    /*
     * set DLC to this channel
     */
    dlc = &uart_data->dlc_table[dlc_instance];
    if(dlci EQ UART_DLCI_CONTROL)
    {
      /*
       * send UA frame
       */
      frame->buffer[UART_OFFSET_CONTROL] = UART_UA_FRAME;
      *forward                          |= UART_FORWARD_CLD;
    }
    else
    {
      switch(dlc->connection_state)
      {
        case UART_CONNECTION_DISC_SENT:
          /*
           * remove DISC frame
           */
          MFREE_DESC2(dlc->last_command);
          dlc->last_command = NULL;
          uart_data->ker.nr_t1--;
          if( uart_data->ker.nr_t1 EQ 0 )
            sig_ker_rt_stop_t1_req();
          /* fall through */
        case UART_CONNECTION_SABM_RCVD:
        case UART_CONNECTION_OPEN:
          /*
           * mark channel as closed
           */
          dlc->connection_state = UART_CONNECTION_DEAD;
          /*
           * send UA frame
           */
          frame->buffer[UART_OFFSET_CONTROL] = UART_UA_FRAME;
          *forward                          |= UART_FORWARD_DLC_RELEASE;
          break;

        default:
          TRACE_ERROR( "DLC CONNECTION_STATE unexpected" );
          break;
      }
    }
  }
  else
  {
    /*
     * send DM frame
     */
    frame->buffer[UART_OFFSET_CONTROL] = UART_DM_CONTROL_FRAME;
  }
  *forward|= UART_FORWARD_RESPONSE;
} /* ker_receive_disc_frame */



/*
+------------------------------------------------------------------------------
| Function    : ker_receive_uih_control_frame
+------------------------------------------------------------------------------
| Description : This function analyzes received UIH Control frames.
|
| Parameters  : forward - result of analysis
|               frame   - frame to analyze
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_receive_uih_control_frame(ULONG* forward, T_desc2* frame)
{
  T_DLC*  dlc;
  UBYTE   dlci;
  UBYTE   dlc_instance;

  TRACE_FUNCTION( "ker_receive_uih_control_frame" );

  dlci         = frame->buffer[UART_OFFSET_ADDRESS] >> UART_DLCI_POS;
  dlc_instance = uart_data->dlc_instance[dlci];

  /*
   * analyze message responses
   */
  ker_analyze_frame_info_response(forward, frame);
  /*
   * check whether frame for an existing channel
   */
  if(dlc_instance NEQ UART_EMPTY_INSTANCE)
  {
    /*
     * set DLC to this channel
     */
    dlc = &uart_data->dlc_table[dlc_instance];
    /*
     * check whether it is an command frame
     * discard frame if it is a response frame
     */
    if(frame->buffer[UART_OFFSET_ADDRESS] & UART_CR)
    {
      switch(dlc->connection_state)
      {
        case UART_CONNECTION_OPEN:
          /*
           * send UIH response frame
           */
          ker_analyze_frame_info_command(forward, frame);
          *forward|= UART_FORWARD_RESPONSE;
          break;

        case UART_CONNECTION_DISC_SENT:
          /*
           * send DM frame
           */
          ker_analyze_frame_info_command(forward, frame);
          frame->buffer[UART_OFFSET_CONTROL] = UART_DM_CONTROL_FRAME;
          *forward                          |= UART_FORWARD_RESPONSE;
          break;

        case UART_CONNECTION_SABM_RCVD:
          break;

        default:
          TRACE_ERROR( "DLC CONNECTION_STATE unexpected" );
          break;
      }
    }
  }
  else
  {
    /*
     * send DM frame
     */
    ker_analyze_frame_info_command(forward, frame);
    frame->buffer[UART_OFFSET_CONTROL] = UART_DM_CONTROL_FRAME;
    *forward                          |= UART_FORWARD_RESPONSE;
  }
} /* ker_receive_uih_control_frame */



/*
+------------------------------------------------------------------------------
| Function    : ker_receive_uih_data_frame
+------------------------------------------------------------------------------
| Description : This function analyzes received UIH Data frames.
|
| Parameters  : forward - result of analysis
|               frame   - frame to analyze
|
+------------------------------------------------------------------------------
*/
GLOBAL void ker_receive_uih_data_frame(ULONG* forward, T_desc2* frame)
{
  UBYTE dlci;
  UBYTE dlc_instance;

  TRACE_FUNCTION( "ker_receive_uih_data_frame" );

  dlci         = frame->buffer[UART_OFFSET_ADDRESS] >> UART_DLCI_POS;
  dlc_instance = uart_data->dlc_instance[dlci];

  /*
   * check whether frame for a not existing channel
   * discard packet if it is for an extisting channel
   */
  if(dlc_instance EQ UART_EMPTY_INSTANCE)
  {
    /*
     * send DM frame
     * shorten information field
     */
    frame->buffer[UART_OFFSET_CONTROL] = UART_DM_DATA_FRAME;
    frame->len                         = UART_OFFSET_INFO;
    *forward                          |= UART_FORWARD_RESPONSE;
  }
} /* ker_receive_uih_data_frame */