view src/g23m-fad/t30/t30_bcsf.c @ 697:e0feeea32ca5

tpudrv12.h: sync with Tourmaline for CONFIG_TARGET_LEO_RFFE
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 09 Jun 2021 17:24:28 +0000
parents 90eb61ecd093
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  GSM-F&D (8411)
|  Modul   :  t30_bcsf
+----------------------------------------------------------------------------- 
|  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 procedures and functions for
|             the component T30 of the mobile station
+----------------------------------------------------------------------------- 
*/ 

#ifndef T30_BCSF_C
#define T30_BCSF_C
#endif

#define ENTITY_T30

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

#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include "typedefs.h"
#include "pcm.h"
#include "vsi.h"
#include "macdef.h"
#include "pconst.cdg"
#include "mconst.cdg"
#include "message.h"
#include "ccdapi.h"
#include "custom.h"
#include "gsm.h"
#include "prim.h"
#include "cnf_t30.h"
#include "mon_t30.h"
#include "pei.h"
#include "tok.h"
#include "dti.h"      /* functionality of the dti library */
#include "t30.h"

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

/*==== TYPES =======================================================*/

/*==== VAR EXPORT ==================================================*/

/*==== VAR LOCAL ===================================================*/


LOCAL const USHORT crctab[256] =
{
  0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
  0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
  0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
  0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
  0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
  0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
  0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
  0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
  0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
  0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
  0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
  0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
  0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
  0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
  0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
  0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
  0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
  0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
  0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
  0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
  0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
  0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
  0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
  0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
  0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
  0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
  0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
  0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
  0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
  0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
  0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
  0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};

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

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_build_report    |
+--------------------------------------------------------------------+

  PURPOSE : build the T30_REPORT_IND primitive out of a send/rec. frame

*/

LOCAL void bcs_build_report(UBYTE *frame, USHORT len, T_T30_REPORT_IND *t30_report_ind)
{
  if (t30_data->bitorder & FBO_REV_STATUS) /* reverse the bitorder of each byte */
  {
    memcpy(t30_report_ind->sdu.buf, frame, len);
  }
  else
  {
    USHORT i;
    for (i=0; i<len; i++)
      t30_report_ind->sdu.buf[i] = BIT_MIRROR[frame[i]];
  }

  t30_report_ind->sdu.l_buf = (len << 3);
  t30_report_ind->sdu.o_buf = 0;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_init            |
+--------------------------------------------------------------------+

  PURPOSE : initialize BCS formatter

*/

GLOBAL void bcs_init(T_T30_DATA *pt30_data)
{
  TRACE_FUNCTION ("bcs_init()");
  memset (pt30_data->bcs_frm, 0, BCS_FRM_SIZE);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_check_frames    |
+--------------------------------------------------------------------+

  PURPOSE : This function checks the final flag and the frame checking sequence.
*/

LOCAL UBYTE bcs_check_frames(UBYTE *idx_max)
{

  T_sdu *sdu = (T_sdu*)t30_data->bcs_frm;
  UBYTE idx;

  for (idx = 0; idx < *idx_max; idx++)
  {
#ifdef _SIMULATION_ /* FCS generating/checking off */
    if (t30_data->test_mode & TST_FCS)
    {
    }
    else
#endif
    {
      USHORT crctt = 0xffff; /* initialize crc */

      /* shift each bit through polynomial */
      USHORT pos;
      for (pos = t30_data->frm[idx].beg; pos <= t30_data->frm[idx].end; pos++)
      {
        SHORT tmp = (crctt >> 8) ^ sdu->buf[pos];
        crctt = ((crctt << 8) ^ crctab[tmp & 0xff]) & 0xffff;
      }
      if (crctt NEQ 0x1D0F) /* check remainder */
        return CHK_FCS_ERR;
    }
#if defined _SIMULATION_ || defined KER_DEBUG_BCS
    ker_debug ("BCS_DATA_IND", (UBYTE*)&sdu->buf[t30_data->frm[idx].beg], (USHORT)(t30_data->frm[idx].end-t30_data->frm[idx].beg));
#endif
    /* count final flags */
    if (sdu->buf[t30_data->frm[idx].beg + 1] & 0x08)
    {
      *idx_max = (UBYTE)idx + 1;
      return CHK_OK;
    }
  }
#ifdef _SIMULATION_ /* Control checking OFF */
  if (t30_data->test_mode & TST_CTRL)
    return CHK_OK;
#endif
  return CHK_FIN_ERR;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_frame_pos       |
+--------------------------------------------------------------------+

  PURPOSE : This function extracts
            the beginning, the end and the length of each frame.
*/

LOCAL UBYTE bcs_frame_pos(UBYTE *flag_vector, UBYTE *idx_max)
{
  USHORT idx = 0;
  T_sdu *sdu = (T_sdu*)t30_data->bcs_frm;

  *idx_max = 0;
  while (idx < sdu->l_buf)
  {
    /* skip flags */
    while (idx < sdu->l_buf AND flag_vector[idx])
      idx++;

    /* ready if last byte is a flag */
    if (idx EQ sdu->l_buf)
      return FRM_OK;

    /* save begin of frame */
    t30_data->frm[*idx_max].beg = idx;

    /* search flag */
    while (idx < sdu->l_buf AND !flag_vector[idx])
      idx++;

    /* error if no flag found */
    if (idx EQ sdu->l_buf)
    {
      t30_data->frm[*idx_max].beg = 0;
      return FRM_OK;  /* return FRM_ERR_NO_FLAG; ??? */
    }

    /* save end of fame and length of frame */
    t30_data->frm[*idx_max].end = idx - 1;
    t30_data->frm[*idx_max].len = idx - t30_data->frm[*idx_max].beg;

    /* error if too many frames received */
    if (++(*idx_max) EQ FRAMES_MAX)
      return FRM_ERR_TOO_MANY_FRAMES;

    /* next frame */
    idx++;
  }
  return FRM_OK;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_report_rcv      |
+--------------------------------------------------------------------+

  PURPOSE : This function reports the received HDLC frames to MMI
            if desired.
*/

LOCAL void bcs_report_rcv(UBYTE idx_max)
{
  T_sdu *sdu = (T_sdu*)t30_data->bcs_frm;
  USHORT idx;

  for (idx = 0 ; idx < idx_max ; idx++)
  {
    if (t30_data->frm[idx].len >= 2)
    {
      PALLOC_SDU (t30_report_ind, T30_REPORT_IND, REPORT_SIZE_BITS);
      bcs_build_report (&sdu->buf[t30_data->frm[idx].beg], (USHORT)(t30_data->frm[idx].len - 2), t30_report_ind);
      t30_report_ind->dir = DIR_RCV;
      PSENDX (MMI, t30_report_ind);
    }
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_decode          |
+--------------------------------------------------------------------+

  PURPOSE : This function decodes each hdlc frame and sends the
            decoded data to the kernel.
*/

/* this function is only necessary because of an iarm compiler failure */
LOCAL void bcs_clear_flag (UBYTE *ptr)
{
  if (*ptr & 0x70)
    *ptr &= 0x7F;
}

LOCAL UBYTE bcs_decode(UBYTE idx_max)
{
  T_sdu *sdu = (T_sdu*)t30_data->bcs_frm;
  UBYTE idx;

  for (idx = 0; idx < idx_max; idx++)
  {
    USHORT begin = t30_data->frm[idx].beg + 2;

    /* set direction flag to zero for CCD decoding */
    bcs_clear_flag (sdu->buf + begin);

    /* set offset and length of current frame */
    sdu->o_buf = begin << 3;
    sdu->l_buf = ((USHORT)(t30_data->frm[idx].len - 4) << 3);

#ifdef _SIMULATION_ /* show contents of buffer */
    if (t30_data->test_mode /* & TST_BUF */)
    {
      ker_debug ("ccd_decode", &sdu->buf[begin], (USHORT)(sdu->l_buf >> 3));
    }
#endif

    if (ccd_decodeMsg(CCDENT_T30, DOWNLINK, (T_MSGBUF*)sdu, _decodedMsg, HDLC_ADDR) EQ ccdError)
    {
      TRACE_EVENT ("ERROR: ccd_decode");
      return CCD_ERR;
    }
    sig_bcs_ker_bdat_ind(); /* send signal to kernel */
  }
  return CCD_OK;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_destuff         |
+--------------------------------------------------------------------+

  PURPOSE : This function de-stuffs the received BCS data.
*/

LOCAL void bcs_destuff(UBYTE *flag_vector)
{
  T_sdu *sdu = (T_sdu*)t30_data->bcs_frm;
  SHORT bcs_tmp_len = 0;
  SHORT ones = 0;
  SHORT bits = 8;
  UBYTE flag = 0;
  UBYTE *bcs_tmp;
  SHORT i;

#ifdef _SIMULATION_ /* bit stuffing/destuffing OFF */
  if (t30_data->test_mode & TST_STUFF)
  {
    for (i = 0; i < sdu->l_buf; i++)
    {
      if (sdu->buf[i] EQ HDLC_FLAG)
        flag_vector[i] = 1;
    }
    if (flag_vector[sdu->l_buf-1] NEQ 1) // trailing HDLC_FLAG is missed
    {
      sdu->l_buf++;
      sdu->buf[sdu->l_buf-1] = HDLC_FLAG;
      flag_vector[sdu->l_buf-1] = 1;
    }
    return;
  }
#endif

  MALLOC(bcs_tmp, sdu->l_buf);
  
  for (i = 0 ; i < sdu->l_buf ; i++)
  {
    UBYTE bit_ptr = 0x80; /* points initially to MSB */

    do /* check each byte for destuffing */
    {
      if (sdu->buf[i] & bit_ptr) /* bit pointed to is 1 */
      {
        ones++;
        bcs_tmp[bcs_tmp_len] = (bcs_tmp[bcs_tmp_len] << 1) | 1;
        flag                 = (flag                 << 1) | 1;

        if (! --bits) /* counter of byte's bits*/
        {
          bcs_tmp_len++;
          bits = 8;
        }
      }
      else /* bit is 0 */
      {
        if (ones != 5)
        {
          bcs_tmp[bcs_tmp_len] <<= 1;
          flag                 <<= 1;

          if (ones EQ 6 && flag EQ HDLC_FLAG)
            flag_vector[bcs_tmp_len] = 1;

          if (! --bits) /* whole byte is scanned */
          {
            bcs_tmp_len++;
            bits = 8;
          }
        }
        ones = 0;
      }
      bit_ptr >>= 1;
    }
    while (bit_ptr);
  }
  /* copy destuffed BCS frame back to sdu */
  memcpy (sdu->buf, bcs_tmp, bcs_tmp_len);
  MFREE(bcs_tmp);

  sdu->l_buf = bcs_tmp_len; /* adjust buffer length */
  if (flag_vector[sdu->l_buf-1] NEQ 1) // trailing HDLC_FLAG is missed
  {
    sdu->l_buf++;
    sdu->buf[sdu->l_buf-1] = HDLC_FLAG;
    flag_vector[sdu->l_buf-1] = 1;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_bcs_decode      |
+--------------------------------------------------------------------+

  PURPOSE : This function gets the frame positions,
            checks the frames,
            reports the contents to MMI if desired,
            decodes the contents and sends the contents to the kernel.
*/

GLOBAL void bcs_bcs_decode(void)
{
  UBYTE *flag_vector, idx_max, ret;

  MALLOC(flag_vector, BCS_FRM_SIZE);
  memset(flag_vector, 0, BCS_FRM_SIZE);
  bcs_destuff(flag_vector);
  ret = bcs_frame_pos(flag_vector, &idx_max); /* determines "idx_max" */
  MFREE(flag_vector);

  switch (ret)
  {
  case FRM_OK:
    ret = bcs_check_frames(&idx_max);
    if (t30_data->hdlc_report)
    {
      bcs_report_rcv(idx_max);
    }
    switch (ret)
    {
    case CHK_OK:
      switch (bcs_decode(idx_max))
      {
      case CCD_OK:
        return;

      case CCD_ERR:
        sig_bcs_ker_err_ind(ERR_CCD_DEC);
        return;
      }
      break; /* dummy */

    case CHK_FCS_ERR:
      sig_bcs_ker_err_ind(ERR_FCS);
      return;

    case CHK_FIN_ERR:
      sig_bcs_ker_err_ind(ERR_FINAL);
      return;
    }
    break;

  case FRM_ERR_NO_FLAG:
    sig_bcs_ker_err_ind(ERR_FRAME_NO_FLAG);
    return;

  case FRM_ERR_TOO_MANY_FRAMES:
    sig_bcs_ker_err_ind(ERR_FRAME_TOO_MANY_FRAMES);
    return;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_report_snd      |
+--------------------------------------------------------------------+

  PURPOSE : This function reports the sent HDLC frames to MMI
            if desired.
*/

LOCAL void bcs_report_snd(void)
{
  T_sdu *BCI_stream = (T_sdu*)t30_data->BCI_stream; // Binary Coded Information

  PALLOC_SDU(t30_report_ind, T30_REPORT_IND, REPORT_SIZE_BITS);
  
  bcs_build_report(&BCI_stream->buf[0], (USHORT)((BCI_stream->l_buf >> 3) + 2), t30_report_ind);
  t30_report_ind->dir = DIR_SND;
  PSENDX (MMI, t30_report_ind);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_fcs_gen         |
+--------------------------------------------------------------------+

  PURPOSE : This function generates the frame checking sequence
            for one HDLC frame.
*/

LOCAL void bcs_fcs_gen (void)
{
  T_sdu *sdu = (T_sdu*)t30_data->BCI_stream;
  USHORT end = (sdu->l_buf >> 3) + 2;
  USHORT pos;
  UBYTE i;

#ifdef _SIMULATION_ /* FCS generating/checking OFF */
  if (t30_data->test_mode & TST_FCS)
  {
    pos = end;
    sdu->buf[pos++] = 0x12;
    sdu->buf[pos++] = 0xEF;
  }
  else
#endif
  {
    USHORT crctt = 0xffff;
    for (pos = 0; pos < end; pos++)
    {
      SHORT tmp =  (crctt >> 8) ^ sdu->buf[pos];
      crctt = ((crctt << 8) ^ crctab[tmp & 0xff]) & 0xffff;
    }
    sdu->buf[pos++] = (~(crctt & 0xffff) & 0xffff) >> 8;/*lint !e661 !e662 (Warning -- Possible access/creation of out-of-bounds pointer)*/
    sdu->buf[pos++] = (~(crctt & 0xffff) & 0xff);/*lint !e661 !e662 (Warning -- Possible access/creation of out-of-bounds pointer)*/
  }

  sdu->l_buf += 32; /* 4 bytes more */

  for (i = 0; i < HDLC_FLAGS; i++)
  {
    sdu->buf[pos++] = HDLC_FLAG;/*lint !e661 !e662 (Warning -- Possible access/creation of out-of-bounds pointer)*/
    sdu->l_buf += 8;
  }
  sdu->o_buf  = 0;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_stuff           |
+--------------------------------------------------------------------+

  PURPOSE : This function stuffs the BCS data.
*/

LOCAL void bcs_stuff (UBYTE final)
{
  T_sdu *sdu_inp  = (T_sdu*)t30_data->BCI_stream;
  T_sdu *sdu_out = &t30_data->fad_data_req->sdu;
  
  UBYTE *buf_in  = sdu_inp->buf;
  USHORT len_in  = sdu_inp->l_buf >> 3;
  UBYTE *buf_out;
  USHORT len_out = sdu_out->l_buf >> 3;
  USHORT i;

#ifdef _SIMULATION_ /* bit stuffing/destuffing OFF */
  if (t30_data->test_mode & TST_STUFF)
  {
    buf_out = &sdu_out->buf[len_out];
    memcpy (buf_out, buf_in, len_in);
    sdu_out->l_buf = (len_out + len_in) << 3;
    return;
  }
#endif

  buf_out = sdu_out->buf;

  for (i = 0; i < len_in; i++)
  {
    USHORT bit_ptr = 0x80;
    UBYTE stuff = 1;
    /*
    check HDLC_FLAGS
    */
    if (buf_in[i] EQ HDLC_FLAG)
    {
      USHORT k;
      for (k = 0; k < HDLC_FLAGS; k++)
      {
        if (i EQ len_in - k - 1)
        {
          stuff = 0;
          break;
        }
      }
    }
    do
    {
      switch (buf_in[i] & bit_ptr)
      {
      default:
        buf_out[len_out] = (buf_out[len_out] << 1) | 1;
        if (! --t30_data->stuff_bits)
        {
          len_out++;
          t30_data->stuff_bits = 8;
        }
        if (!stuff)
        {
          t30_data->stuff_ones = 0;
          break;
        }
        if (++t30_data->stuff_ones != 5)
          break;
        /*
         * otherwise fall through
         */

      case 0:
        buf_out[len_out] <<= 1;
        t30_data->stuff_ones = 0;

        if (! --t30_data->stuff_bits)
        {
          len_out++;
          t30_data->stuff_bits = 8;
        }
        break;
      }
      bit_ptr >>= 1;
    } while (bit_ptr);
  }

  if (final EQ FINAL_YES AND t30_data->stuff_bits NEQ 8)
  {
    buf_out[len_out] <<= t30_data->stuff_bits;
    len_out++;
  }

  sdu_out->l_buf = len_out << 3;

  if (final EQ FINAL_YES)
  {
    t30_data->stuff_ones = 0;
    t30_data->stuff_bits = 8;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_bcs_encode      |
+--------------------------------------------------------------------+

  PURPOSE : This function encodes the HDLC frames,
            reports the HDLC frames to MMI if desired,
            generates the frame checking sequence and stuffs the bits.
*/

GLOBAL void bcs_bcs_encode(UBYTE ctrl, UBYTE final)
{
  T_sdu *sdu = (T_sdu*)t30_data->BCI_stream;

  switch (sdu->buf[2])/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
  {
    case BCS_DIS:
      TRACE_EVENT ("Send BCS_DIS");
      break;
    case BCS_CSI:
      TRACE_EVENT ("Send BCS_CSI");
      break;
    case BCS_NSF:
      TRACE_EVENT ("Send BCS_NSF");
      break;
    case BCS_DTC:
      TRACE_EVENT ("Send BCS_DTC");
      break;
    case BCS_CIG:
      TRACE_EVENT ("Send BCS_CIG");
      break;
    case BCS_NSC:
      TRACE_EVENT ("Send BCS_NSC");
      break;
    case BCS_PWD_POLL:
      TRACE_EVENT ("Send BCS_PWD_POLL");
      break;
    case BCS_SEP:
      TRACE_EVENT ("Send BCS_SEP");
      break;

    case BCS_CFR:
      TRACE_EVENT ("Send BCS_CFR");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_CRP:
      TRACE_EVENT ("Send BCS_CRP");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_DCN:
      TRACE_EVENT ("Send BCS_DCN");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_DCS:
      TRACE_EVENT ("Send BCS_DCS");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_EOM:
      TRACE_EVENT ("Send BCS_EOM");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_EOP:
      TRACE_EVENT ("Send BCS_EOP");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_FTT:
      TRACE_EVENT ("Send BCS_FTT");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_MCF:
      TRACE_EVENT ("Send BCS_MCF");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_MPS:
      TRACE_EVENT ("Send BCS_MPS");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_NSS:
      TRACE_EVENT ("Send BCS_NSS");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_PIN:
      TRACE_EVENT ("Send BCS_PIN");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_PIP:
      TRACE_EVENT ("Send BCS_PIP");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_PRI_EOM:
      TRACE_EVENT ("Send BCS_PRI_EOM");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_PRI_EOP:
      TRACE_EVENT ("Send BCS_PRI_EOP");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_PRI_MPS:
      TRACE_EVENT ("Send BCS_PRI_MPS");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_PWD_SND:
      TRACE_EVENT ("Send BCS_PWD_SND");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_RTN:
      TRACE_EVENT ("Send BCS_RTN");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_RTP:
      TRACE_EVENT ("Send BCS_RTP");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_SUB:
      TRACE_EVENT ("Send BCS_SUB");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
    case BCS_TSI:
      TRACE_EVENT ("Send BCS_TSI");
      sdu->buf[2] |= t30_data->dir;/*lint !e415 !e416 (Warning -- access/creation of out-of-bounds pointer)*/
      break;
  }

  sdu->buf[0] = HDLC_ADDR;
  sdu->buf[1] = ctrl;/*lint !e415 (Warning -- access of out-of-bounds pointer)*/

  if (t30_data->hdlc_report)
  {
    bcs_report_snd();
  }
  bcs_fcs_gen();
  bcs_stuff(final);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-F&D (8411)             MODULE  : T30_BCSF            |
| STATE   : code                       ROUTINE : bcs_fill_bcs_frm    |
+--------------------------------------------------------------------+

  PURPOSE : This function checks if the BCS frame buffer has enough
            space to append the received data.
            If there is enough space the data is appended.
            If not than BCS_FRM_FULL is returned.

*/

GLOBAL UBYTE bcs_fill_bcs_frm (T_FAD_DATA_IND *fad_data_ind)
{
  USHORT data_len = fad_data_ind->sdu.l_buf >> 3;
  T_sdu *sdu = (T_sdu*)t30_data->bcs_frm;

  TRACE_FUNCTION("bcs_fill_bcs_frm()");

  if (sdu->l_buf + data_len < BCS_FRM_SIZE)
  {
    memcpy (&sdu->buf[sdu->l_buf], fad_data_ind->sdu.buf, data_len);
    sdu->l_buf += data_len;
    return ((fad_data_ind->final) ? BCS_FRM_FILLED : BCS_FRM_FILLING);
  }
  else
  {
    sdu->l_buf = BCS_FRM_SIZE;
    return (BCS_FRM_FULL);
  }
}