view src/g23m-aci/aci/conc_sms.c @ 199:78d46b5cda38

configs/hybrid: recipe created
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 14 Oct 2016 01:42:52 +0000
parents 53929b40109c
children
line wrap: on
line source

/*
+-----------------------------------------------------------------------------
|  Project :  $Workfile::
|  Modul   :  CONC_SMS
+-----------------------------------------------------------------------------
|  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 :  SMS Concatenation Handler
+-----------------------------------------------------------------------------
*/

#ifndef CONC_SMS_C
#define CONC_SMS_C
#endif

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

#include "aci_all.h"

#include "aci_cmh.h"
#include "ati_cmd.h"
#include "aci_cmd.h"

#include "aci_fd.h"
#include "aci_mem.h"

#include "psa.h"
#include "psa_sms.h"

#include "cmh.h"
#include "cmh_sms.h"

#include "psa_cc.h"

#include "typedefs.h"
#include "aci_lst.h"

#include "psa_util.h"
#include "conc_sms.h"

#ifdef _CONC_TESTING_
#include "aci_io.h"
#include "aci_mfw.h"
#endif

/*==== VARIABLES ==================================================*/

GLOBAL T_SM_ASSEMBLY assembly_list[MAX_BUF_ELEMS];
GLOBAL T_SEG_BUF     segBuf_list  [MAX_BUF_ELEMS];
GLOBAL T_CONC_BUF    concBuf_list [MAX_CONC_BUF_ELEMS];
LOCAL USHORT RefNum_Del = 0xFF;
LOCAL BOOL dFLAG = FALSE;
LOCAL CHAR Addres[MAX_SMS_ADDR_DIG];



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

LOCAL void concSMS_printConcatList ();
LOCAL USHORT concSMS_findMaxRefNum(void); // Marcus: Issue 872: 03/10/2002



/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_findSeqNumSB       |
+--------------------------------------------------------------------+

  PURPOSE : find 'seq_num' in segment buffer
*/
LOCAL BOOL concSMS_findSeqNumSB ( UBYTE critrerium,
                                  void *elem )
{
  T_SEG_BUF_ELEM *compared = (T_SEG_BUF_ELEM *)elem;

  if ( compared->seq_num == critrerium )
    return TRUE;
  else
    return FALSE;
}



/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_findSeqNumElemCB   |
+--------------------------------------------------------------------+

  PURPOSE : find 'seq_num' in concatenation buffer
*/
LOCAL BOOL concSMS_findSeqNumElemCB  ( UBYTE critrerium,
                                       void *elem )
{
  T_CONC_BUF_ELEM *compared = (T_CONC_BUF_ELEM *)elem;

  if ( compared->seq_num == critrerium )
    return TRUE;
  else
    return FALSE;
}



/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_findRecNumElemCB   |
+--------------------------------------------------------------------+

  PURPOSE : find 'rec_num' in concatenation buffer
*/
LOCAL BOOL concSMS_findRecNumElemCB  ( UBYTE critrerium,
                                       void *elem )
{
  T_CONC_BUF_ELEM *compared = (T_CONC_BUF_ELEM *)elem;

  if ( compared->rec_num == critrerium )
    return TRUE;
  else
    return FALSE;
}

/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_getAsBuffer        |
+--------------------------------------------------------------------+

  PURPOSE : This functions searchs the assembly buffer for ref_num
            and address.
*/
LOCAL T_SM_ASSEMBLY* concSMS_getAsBuffer( USHORT ref_num, CHAR *address )
{
  UBYTE i;

  TRACE_FUNCTION ("concSMS_getAsBuffer()");
 
  /* search for the element */
  for (i=0; i<MAX_BUF_ELEMS; i++)
  {
    if ( (assembly_list[i].ref_num EQ ref_num) AND
         (assembly_list[i].in_use) )
    {
      if ((address NEQ NULL) /* AND (address[0] NEQ '\0') */)
      {
        if (!strcmp(assembly_list[i].address, address))
        {
          return &assembly_list[i];
        }
      }
      else
      {
       return &assembly_list[i];
      }
    }
  }

  return NULL;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_addToAsBuffer      |
+--------------------------------------------------------------------+

  PURPOSE : This function adds data to the assembly buffer. It returns
            NULL if buffer is full.

*/
LOCAL T_SM_ASSEMBLY* concSMS_addToAsBuffer ( USHORT ref_num,
                                             CHAR   *address,
                                             UBYTE  max_num,
                                             T_SM_DATA_EXT *data )
{
  UBYTE i;
  T_SM_ASSEMBLY *assembly_elem = NULL;

  TRACE_FUNCTION ("concSMS_addToAsBuffer()");


  /* search for the element */
  assembly_elem = concSMS_getAsBuffer( ref_num, address );

#ifdef _CONC_TESTING_
  TRACE_EVENT_P1("addToAsBuffer:[0].in_use: %d", assembly_list[0].in_use);
  TRACE_EVENT_P1("addToAsBuffer:[1].in_use: %d", assembly_list[1].in_use);
#endif

  /* element not found */
  if (assembly_elem EQ NULL)
  {
    /* search for an unused list entry */
    for (i=0; i<MAX_BUF_ELEMS; i++)
    {
      if (assembly_list[i].in_use EQ FALSE)
      {
        assembly_elem = &assembly_list[i];
        break;
      }
    }

    /* buffer is full */
    if (assembly_elem EQ NULL)
      return NULL;


    /* create new assembly buffer for this ref_num*/
    assembly_elem->in_use       = TRUE;
    assembly_elem->ref_num      = ref_num;

    if ( (address NEQ NULL) AND (address[0] NEQ '\0') )
      strcpy(assembly_elem->address, address);
    else
      assembly_elem->address[0] = '\0';

    assembly_elem->next_exp_num = 1;
    assembly_elem->segs_left    = max_num;
    assembly_elem->seg_count    = 0;
  } /* if (assembly_elem EQ NULL) */

  if (assembly_elem->seg_count EQ 0)
  {
    /* alloc memory for data to assemble */

    UBYTE segs;

    segs = MINIMUM(assembly_elem->segs_left, CONC_MAX_SEGS);
    ACI_MALLOC(assembly_elem->data.data, (USHORT)(MAX_SM_LEN*segs));
    assembly_elem->segs_left -= segs;
    assembly_elem->data.len = 0;
  }

  memcpy(assembly_elem->data.data+assembly_elem->data.len,
         data->data, data->len);

  assembly_elem->data.len += data->len;
  assembly_elem->data.data[assembly_elem->data.len] = '\0';
  assembly_elem->next_exp_num++;
  assembly_elem->seg_count++;

#ifdef _CONC_TESTING_
  if (assembly_elem->data.len < TTRACE_LEN)
  {
    TRACE_EVENT_P1("addToAsBuffer:data.data:    %s", assembly_elem->data.data);
  }
  TRACE_EVENT_P1("addToAsBuffer:data.len:     %d", assembly_elem->data.len);
  TRACE_EVENT_P1("addToAsBuffer:next_exp_num: %d", assembly_elem->next_exp_num);
  TRACE_EVENT_P1("addToAsBuffer:seg_count:    %d", assembly_elem->seg_count);
#endif

  return assembly_elem;
}


/*
+---------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                    |
| STATE   : code                ROUTINE : concSMS_removeFromAsBuffer  |
+---------------------------------------------------------------------+

  PURPOSE :  This functions gets data from the assembly buffer and
             sets it to 'unused'. The assembly is completed.

*/
LOCAL UBYTE concSMS_removeFromAsBuffer(T_SM_DATA_EXT  *data_conc,
                                       USHORT         ref_num,
                                       CHAR           *address)
{
  T_SM_ASSEMBLY *assembly_buf;

  TRACE_FUNCTION ("concSMS_removeFromAsBuffer()");


  /* search for the element */
  assembly_buf = concSMS_getAsBuffer( ref_num, address );

  if (assembly_buf EQ NULL)
    return FALSE;

  assembly_buf->in_use = FALSE;

  data_conc->data = assembly_buf->data.data;
  data_conc->len  = assembly_buf->data.len;

  return TRUE;
}


/*
+---------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                    |
| STATE   : code                ROUTINE : concSMS_getFromAsBuffer     |
+---------------------------------------------------------------------+

  PURPOSE :  This functions gets data from the assembly buffer and
             resets the seg_count. The assembly buffer is still in use
             and the assembly is not completed.

*/
LOCAL UBYTE concSMS_getFromAsBuffer(T_SM_DATA_EXT  *data_conc,
                                    USHORT         ref_num,
                                    CHAR           *address)
{
  T_SM_ASSEMBLY *assembly_buf;

  TRACE_FUNCTION ("concSMS_getFromAsBuffer()");


  /* search for the element */
  assembly_buf = concSMS_getAsBuffer( ref_num, address );

  /* assemlby buffer not found */
  if (assembly_buf EQ NULL)
    return FALSE;

  assembly_buf->seg_count = 0;

  data_conc->data = assembly_buf->data.data;
  data_conc->len  = assembly_buf->data.len;

  return TRUE;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_getSegBuffer       |
+--------------------------------------------------------------------+

  PURPOSE : This functions searchs the segment buffer for ref_num
            and address.
*/
LOCAL T_SEG_BUF* concSMS_getSegBuffer( USHORT ref_num, CHAR *address )
{
  UBYTE i;

  TRACE_FUNCTION ("concSMS_getSegBuffer()");


  /* search for the element */
  for (i=0; i<MAX_BUF_ELEMS; i++)
  {
    if ((segBuf_list[i].ref_num EQ ref_num) AND
        (segBuf_list[i].in_use))
    {

      if ((address NEQ NULL) /* AND (address[0] NEQ '\0') */)
      {
        if (!strcmp(segBuf_list[i].address, address))
          return &segBuf_list[i];
      }
      else
      {
        return &segBuf_list[i];
      }
    }
  }

  return NULL;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_addToSegBuffer     |
+--------------------------------------------------------------------+

  PURPOSE : This functions adds one segment to the buffer and returns
            FALSE if no segment buffer is available or the current
            seg buffer is full.

*/
LOCAL UBYTE concSMS_addToSegBuffer ( USHORT ref_num,
                                     CHAR  *address,
                                     UBYTE  seq_num,
                                     UBYTE  rec_num,
                                     T_ACI_SMS_STAT status,
                                     T_SM_DATA_EXT  *data )
{
  T_SEG_BUF *segBuf = NULL;
  T_SEG_BUF_ELEM *segBufElem;
  USHORT count;
  UBYTE i;

  TRACE_FUNCTION ("concSMS_addToSegBuffer()");


  /* search for the segment buffer */
  segBuf = concSMS_getSegBuffer( ref_num, address );

  /* element not found */
  if (segBuf EQ NULL)
  {
    /* search for an unused list entry */
    for (i=0; i<MAX_BUF_ELEMS; i++)
    {
      if (segBuf_list[i].in_use EQ FALSE)
      {
        segBuf = &segBuf_list[i];
        break;
      }
    }

    /* no segment buffer available */
    if ( segBuf EQ NULL)
      return FALSE;

    /* initialise new buffer */
    segBuf->in_use  = TRUE;
    segBuf->ref_num = ref_num;
    if ( (address) AND (address[0] NEQ '\0') )
      strcpy(segBuf->address, address);
    else
      segBuf->address[0]  = '\0';
    segBuf->list    = new_list();
  }

  count = get_list_count(segBuf->list);
  if ( count >= CONC_MAX_SEGS )
  {
    /* clean segment buffer before it overflows */
    while (1)
    {
      segBufElem = remove_first_element(segBuf->list);
      if (segBufElem EQ NULL)
        break;
      ACI_MFREE(segBufElem->data.data);
      ACI_MFREE(segBufElem);
    }
    segBuf->in_use = FALSE;
    return FALSE;
  }

  /* create new segment buffer element */
  ACI_MALLOC(segBufElem, sizeof(T_SEG_BUF_ELEM));
  memset(segBufElem, 0, sizeof(T_SEG_BUF_ELEM));

  /* fill new buffer element */
  segBufElem->seq_num = seq_num;
  segBufElem->rec_num = rec_num;
  segBufElem->status  = status;

  /* alloc memory and copy user data to segment buffer */
  ACI_MALLOC(segBufElem->data.data, data->len);
  segBufElem->data.len = data->len;
  memcpy(segBufElem->data.data, data->data, data->len);

  /* insert element (segment) into the segment buffer */
  insert_list(segBuf->list, segBufElem);

  return TRUE;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_removeFromSegBuffer|
+--------------------------------------------------------------------+

  PURPOSE : This function finds and removes the segment with
            'seq_num' from the segment buffer.

*/
LOCAL T_SEG_BUF_ELEM* concSMS_removeFromSegBuffer ( USHORT ref_num,
                                                    CHAR*  address,
                                                    UBYTE  seq_num )
{
  T_SEG_BUF *segBuf = NULL;
  T_SEG_BUF_ELEM *segBufElem;

  USHORT count;

  TRACE_FUNCTION ("concSMS_removeFromSegBuffer()");


  /* search for the segment buffer */
  segBuf = concSMS_getSegBuffer( ref_num, address );


  /* segment buffer not found */
  if (segBuf EQ NULL)
    return NULL;

  segBufElem = remove_element(segBuf->list, seq_num, concSMS_findSeqNumSB);

  if (segBufElem EQ NULL)
  {
    return NULL; /* didn't find the segment buffer element for this seq_num */
  }

  count = get_list_count(segBuf->list);

  if (count EQ 0)
  {
    ACI_MFREE (segBuf->list);
    segBuf->list = NULL;
    segBuf->in_use = FALSE;
  }

  return segBufElem;

}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_getConcBuffer      |
+--------------------------------------------------------------------+

  PURPOSE : This functions searchs the concatenations buffer for
            ref_num and address.
*/
LOCAL T_CONC_BUF* concSMS_getConcBuffer( USHORT ref_num, CHAR *address )
{
  UBYTE i;

  TRACE_FUNCTION ("concSMS_getConcBuffer()");

  /* search for the element */
  for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
  {
    if ((concBuf_list[i].ref_num EQ ref_num) AND
        (concBuf_list[i].in_use))
    {
      if ((address NEQ NULL) /* AND (address[0] NEQ '\0') */)
      {
        if (!strcmp(concBuf_list[i].address, address))
        {
          return &concBuf_list[i];
        }
      }
      else
      {
        return &concBuf_list[i];
      }
    }
  }

  return NULL;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_addToConcatList    |
+--------------------------------------------------------------------+

  PURPOSE :
*/
LOCAL BOOL concSMS_addToConcatList ( USHORT ref_num,
                                     CHAR   *address,
                                     UBYTE  max_num,
                                     UBYTE  seq_num,
                                     UBYTE  rec_num,
                                     T_ACI_SMS_STAT status,
                                     UBYTE  mem)
{
  T_CONC_BUF *concBuf;
  T_CONC_BUF_ELEM *concBufElem;
  UBYTE i;

  TRACE_FUNCTION ("concSMS_addToConcatList()");


  /* search for concatenation buffer */
  concBuf = concSMS_getConcBuffer( ref_num, address );


  /* element not found */
  if (concBuf EQ NULL)
  {
    
    /* search for an unused list entry */
    for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
    {
      if (concBuf_list[i].in_use EQ FALSE)
      {
        concBuf = &concBuf_list[i];
        break;
      }
    }

    /* buffer is full */
    if ( concBuf EQ NULL)
      return FALSE;
    

    concBuf->in_use   = TRUE;
    concBuf->ref_num  = ref_num;

    if ( (address) AND (address[0] NEQ '\0') )
      strcpy(concBuf->address, address);
    else
      concBuf->address[0]  = '\0';

    concBuf->max_num  = max_num;
    concBuf->list     = new_list();
  }


  /* don't add elements with same seq_num to the Concatenation Buffer */
  concBufElem = find_element(concBuf->list,seq_num,concSMS_findSeqNumElemCB);
  if (concBufElem)
    return FALSE;
 


  /* create new conc. buffer element */
  ACI_MALLOC(concBufElem, sizeof(T_CONC_BUF_ELEM));

  /* increase total count of stored CSMS segments by 1*/
  concShrdPrm.elem_count++;

  concBufElem->seq_num = seq_num;
  concBufElem->rec_num = rec_num;
  concBufElem->status  = status;
  concBufElem->mem     = mem;

  /* insert element into the conc. buffer */
  insert_list(concBuf->list, concBufElem);

  concSMS_printConcatList();

  return TRUE;
}



/*
+---------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                    |
| STATE   : code                ROUTINE : concSMS_removeFromConcatList|
+---------------------------------------------------------------------+

  PURPOSE : This function removes and FREES the memory for the element.
*/
LOCAL T_ACI_LIST *concSMS_removeFromConcatList ( USHORT ref_num,
                                          CHAR   *address,
                                          UBYTE  rec_num )
{
  T_CONC_BUF *concBuf = NULL;
  T_CONC_BUF_ELEM *concBufElem;
  USHORT count;

  TRACE_FUNCTION ("concSMS_removeFromConcatList()");


  /* search for concatenation buffer */
  concBuf = concSMS_getConcBuffer( ref_num, address );

  /* concatenation buffer not found */
  if (concBuf EQ NULL)
  {
    TRACE_EVENT_P1("conc_buf NULL: rec: %d", rec_num);
    return NULL;
  }

  concBufElem = remove_element(concBuf->list, rec_num, concSMS_findRecNumElemCB);

  if (concBufElem EQ NULL)
  {
    TRACE_EVENT_P1("concBufElem NULL: rec: %d", rec_num);
    return NULL;
  }

  /* free memory for this element */
  ACI_MFREE(concBufElem);

  /* decrease total count of stored CSMS segments by 1*/
  concShrdPrm.elem_count--;

  count = get_list_count(concBuf->list);

  if (count EQ 0)
  {
    ACI_MFREE (concBuf->list);
    concBuf->list = NULL;
    concBuf->in_use = FALSE;
    return NULL;
  }
  return concBuf->list;
}


/*
+---------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                    |
| STATE   : code                ROUTINE : concSMS_sortConcatList      |
+---------------------------------------------------------------------+

  PURPOSE : This function sorts the concat. buffer acc. to its seq_num.
*/
LOCAL void concSMS_sortConcatList ( USHORT ref_num,
                                    CHAR   *address )
{
  T_CONC_BUF *concBuf = NULL;
  T_CONC_BUF_ELEM *concBufElem;
  UBYTE seq_num;
  UBYTE rec_num = 0;
  T_ACI_LIST *oldlist, *newlist, *current;
  USHORT count;

  TRACE_FUNCTION ("concSMS_sortConcatList()");


  /* search for concatenation buffer */
  concBuf = concSMS_getConcBuffer( ref_num, address );

  /* concatenation buffer not found */
  if (concBuf EQ NULL)
    return;

  newlist = new_list();
  oldlist = concBuf->list;

  count = get_list_count(oldlist);

  while (count)
  {
    seq_num = 255;
    current  = oldlist;
    while (current)
    {
      concBufElem = (T_CONC_BUF_ELEM*)current->msg;
      if ( concBufElem->seq_num < seq_num )
      {
        seq_num = concBufElem->seq_num;
        rec_num = concBufElem->rec_num;
      }
      current = current->next;
    }

    concBufElem = remove_element(oldlist, rec_num, concSMS_findRecNumElemCB);

    insert_list(newlist, concBufElem);

    count = get_list_count(oldlist);
  }
  if (concBuf->list)
  {
    ACI_MFREE (concBuf->list);
    concBuf->list = NULL;
  }
  concBuf->list = newlist;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_split              |
+--------------------------------------------------------------------+

  PURPOSE : return TRUE if splitting was done, otherwise FALSE
*/
LOCAL UBYTE concSMS_split (  T_ACI_SM_DATA*  tar_data,
                             T_SM_DATA_EXT*  src_data,
                             UBYTE           alphabet,
                             T_EXT_CMS_CMD_ID   id)
{
#ifndef _SIMULATION_
    T_TIME  time_val; /* Used for input to random generator */
#endif

  TRACE_FUNCTION ("concSMS_split ()");

  if (alphabet EQ 0x00)
  {
    /* 7-bit data coding scheme */
    if (src_data->len <= concShrdPrm.l_uncomp7bit_data)
    {
      tar_data->len = (UBYTE)src_data->len;
      memcpy ( tar_data->data, src_data->data, tar_data->len );
      return FALSE;
    }
    else
    {
      tar_data->len = concShrdPrm.l_uncomp7bit_data_conc;
      concShrdPrm.max_sms_len = concShrdPrm.l_uncomp7bit_data_conc;
    }
  }
  else
  {
    /* 8-bit data coding scheme */
    if (src_data->len <= concShrdPrm.l_uncomp8bit_data)
    {
      tar_data->len = (UBYTE)src_data->len;
      memcpy ( tar_data->data, src_data->data, tar_data->len );
      return FALSE;
    }
    else
    {
      tar_data->len = concShrdPrm.l_uncomp8bit_data_conc;
      concShrdPrm.max_sms_len = concShrdPrm.l_uncomp8bit_data_conc;
    }
  }

  /* copy first segment to 'tar_data' */
  memcpy ( tar_data->data, src_data->data, tar_data->len );

  concShrdPrm.udh.ref_num = (UBYTE)concSMS_findMaxRefNum(); /* Marcus: Issue 872: 03/10/2002 */
  concShrdPrm.udh.ref_num++;
  
  concShrdPrm.udh.max_num = (src_data->len+(concShrdPrm.max_sms_len-1)) / concShrdPrm.max_sms_len;
  concShrdPrm.udh.seq_num = 1;

  if (id EQ CMGS_CONC)
  {
#ifndef _SIMULATION_
    vsi_t_time (VSI_CALLER &time_val);
    srand((USHORT) time_val);         /* initialize random generator */

    /* For every conc sms going out, generate a random reference number and  
     * send it. Also when power cycled it will generate a new random number.
     */
    concShrdPrm.udh.ref_num = (UBYTE)rand();
#endif
    concShrdPrm.specPrm.concCMGS.data.len   = src_data->len;
    concShrdPrm.specPrm.concCMGS.data.data  = src_data->data;
    concShrdPrm.specPrm.concCMGS.offset     = tar_data->len;
    return TRUE;
  }

  if (id EQ CMGW_CONC)
  {
    concShrdPrm.specPrm.concCMGW.data.len  = src_data->len;
    concShrdPrm.specPrm.concCMGW.data.data = src_data->data;
    concShrdPrm.specPrm.concCMGW.offset    = tar_data->len;
    return TRUE;
  }

  return FALSE;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_fillUDH            |
+--------------------------------------------------------------------+

  PURPOSE :
*/
LOCAL void concSMS_fillUDH ( T_ACI_UDH_DATA* udh,
                             UBYTE ref_num,
                             UBYTE max_num,
                             UBYTE seq_num )
{
  /* fill user data header structure for 8-bit ref number */

  udh->len     = 0x05;

  /* Information Element Identifier */
  udh->data[0] = SMS_IEI_CONC_8BIT;

  /* Information Element Identifier Length */
  udh->data[1] = 0x03;

  /* Information Element Data */
  udh->data[2] = (UBYTE)(ref_num & 0x00FF);  /* since we use only 8-Bit ref number */
  udh->data[3] = max_num;
  udh->data[4] = seq_num;
}



#ifdef TI_PS_FF_CONC_SMS
/********************** Init Functions *********************************/


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_retrieveConcBuf    |
+--------------------------------------------------------------------+

  PURPOSE : This function searches for the concatenation buffer
            which has 'index' in its first element. Returns
            'CONC_ERROR' if index is not the first element or list is
            incomplete.

*/
LOCAL T_CONC_INIT_RETURN concSMS_retrieveConcBuf ( T_CONC_BUF **concBuf,
                                                   UBYTE        index,
                                                   UBYTE        mem)
{
  UBYTE i;
  T_CONC_BUF_ELEM *concBufElem;

  TRACE_FUNCTION ("concSMS_retrieveConcBuf ()");

  *concBuf = NULL;

  for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
  {
    concSMS_printConcatList();
    /* find conc. buffer element for this rec number */
    concBufElem = find_element(concBuf_list[i].list,
                               index,
                               concSMS_findRecNumElemCB);

    /* element was found and check if memory type of the first segment 
     * equals to the set memory type (mem1 or mem2) 
     */
    if ((concBufElem NEQ NULL) AND (concBufElem->mem EQ mem))

    {
      break;
    }
  }

  if (concBufElem EQ NULL)
  {
    /* no concatenation handler needed */
    return CONC_NOT_NEEDED;
  }

  *concBuf = &concBuf_list[i];


  /* check if rec number is the first segment (with seq_num == 1) */
  if ( ( concBufElem->seq_num EQ 1 ) AND 
         ( smsShrdPrm.status EQ CMGD_DEL_INDEX ) )
  {
    *concBuf = &concBuf_list[i];
    return CONC_NEEDED;

  }
  else if( smsShrdPrm.status > CMGD_DEL_INDEX )
  {
      /* The below check needs to be changed for deleting all 
      the concatmessages in case of DELET FLAG > 0. */

      *concBuf = &concBuf_list[i];
      return CONC_NEEDED;
  }
  /* rec number is not the first element in conc. buffer
   * allow reading of incomplete segments and tread them like "normal" SMS
   */
  return CONC_NOT_NEEDED;
}



/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_initSendFromMem    |
+--------------------------------------------------------------------+

  PURPOSE : This function initialises shared parameter for CMSS.

*/
GLOBAL T_CONC_INIT_RETURN concSMS_initSendFromMem ( T_ACI_CMD_SRC  srcId,
                                                    UBYTE         *index,
                                                    CHAR          *da,
                                                    T_ACI_TOA     *toda )
{
  T_CONC_BUF *concBuf    = NULL;
  T_CONC_BUF_ELEM* elem  = NULL;
  T_CONC_INIT_RETURN ret = CONC_ERROR;
  T_CONC_CMSS *prm       = &concShrdPrm.specPrm.concCMSS;


  TRACE_FUNCTION ("concSMS_initSendFromMem ()");

  ret = concSMS_retrieveConcBuf ( &concBuf, *index, smsShrdPrm.mem2);

  if (ret EQ CONC_ERROR)
  {
    /* Error: segment is not the first segment of the SM */
    return CONC_ERROR;
  }

  if (ret EQ CONC_NOT_NEEDED)
  {
    /* no conatenation handler needed */
    return CONC_NOT_NEEDED;
  }

  concShrdPrm.sentSegs = 0;
  concShrdPrm.srcId = srcId;

  if (da)
  {
    memcpy(prm->da, da, strlen(da));
    prm->da[strlen(da)] = '\0';
    prm->p_da = prm->da;
  }
  else
  {
    prm->p_da = NULL;
  }

  if (toda)
  {
    memcpy(&prm->toda, toda, sizeof(T_ACI_TOA));
    prm->p_toda = &prm->toda;
  }
  else
  {
    prm->p_toda = NULL;
  }

  /* save the first concatenated buffer element */
  prm->currConcBufListElem = concBuf->list;

  prm->skipStoSent = TRUE;

  /* skip segments with status SMS_STAT_StoSent */
  while (prm->currConcBufListElem)
  {
    elem = (T_CONC_BUF_ELEM*)prm->currConcBufListElem->msg;

    if (elem->status EQ SMS_STAT_StoSent)
    {
      prm->currConcBufListElem = prm->currConcBufListElem->next;
    }
    else
    {
      break;
    }
  }

  /*
   * All elements were set to SMS_STAT_StoSent. Assume that this message was
   * sent completly and should be sent for the second time.
   */
  if (prm->currConcBufListElem EQ NULL)
  {
    prm->skipStoSent = FALSE;

    /* save the first concatenated buffer element */
    prm->currConcBufListElem = concBuf->list;

    elem = (T_CONC_BUF_ELEM*)prm->currConcBufListElem->msg;
  }

  *index = elem ? elem->rec_num : NULL;

  if (elem NEQ NULL)
  {
    elem->status = SMS_STAT_StoSent;
  }

  return CONC_NEEDED;

}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_initReadFromMem    |
+--------------------------------------------------------------------+

  PURPOSE : This function initialises shared parameter for CMGR.

*/
GLOBAL T_CONC_INIT_RETURN concSMS_initReadFromMem ( T_ACI_CMD_SRC  srcId,
                                               UBYTE          index,
                                               T_ACI_SMS_READ rdMode )
{
  T_CONC_BUF *concBuf;
  T_CONC_INIT_RETURN ret;

  TRACE_FUNCTION ("concSMS_initReadFromMem ()");

  ret = concSMS_retrieveConcBuf ( &concBuf, index, smsShrdPrm.mem2);

  if (ret EQ CONC_ERROR)
  {
    /* Error: segment is not the first segment of the SM */
    return CONC_ERROR;
  }

  if (ret EQ CONC_NOT_NEEDED)
  {
    /* no conatenation handler needed */
    return CONC_NOT_NEEDED;
  }

  concShrdPrm.srcId  = srcId;
  concShrdPrm.specPrm.concCMGR.rdMode = rdMode;

  /* save the second concatenated buffer element */
  concShrdPrm.specPrm.concCMGR.currConcBufListElem = concBuf->list->next;

  return CONC_NEEDED;

}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_initDeleteFromMem  |
+--------------------------------------------------------------------+

  PURPOSE : This function initialises shared parameter for CMGD.

*/
GLOBAL T_CONC_INIT_RETURN concSMS_initDeleteFromMem ( T_ACI_CMD_SRC  srcId,
                                                 UBYTE          index )
{
  T_CONC_BUF *concBuf;
  T_CONC_INIT_RETURN ret;


  TRACE_FUNCTION ("concSMS_initDeleteFromMem ()");


  ret = concSMS_retrieveConcBuf ( &concBuf, index, smsShrdPrm.mem1);

  if (ret EQ CONC_ERROR)
  {
    /* Error: segment is not the first segment of the SM */
    return CONC_ERROR;
  }

  if (ret EQ CONC_NOT_NEEDED)
  {
    if (concBuf NEQ NULL)
    {
      RefNum_Del = concBuf->ref_num;
    }
    /*else if (*/
    else if (dFLAG EQ TRUE)
    {
     TRACE_EVENT("BUFFER FULL");
    }
    else 
    {
     RefNum_Del = 0xFF;
    }
    /* no conatenation handler needed */
    return CONC_NOT_NEEDED;
  }


  /* save the concatenation list */
  concShrdPrm.specPrm.concCMGD.currConcBufListElem = concBuf->list;

  concShrdPrm.specPrm.concCMGD.ref_num = concBuf->ref_num;

  concShrdPrm.specPrm.concCMGD.address = concBuf->address;

  concShrdPrm.srcId  = srcId;

  concShrdPrm.specPrm.concCMGD.error_count = 0;

  return CONC_NEEDED;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_initSend           |
+--------------------------------------------------------------------+

  PURPOSE : This function initialises shared parameter for CMGS.

*/
GLOBAL T_CONC_INIT_RETURN concSMS_initSend (
                                T_ACI_SM_DATA*  tar_data,
                                T_ACI_UDH_DATA* udh,
                                T_ACI_CMD_SRC   srcId,
                                CHAR*           da,
                                T_ACI_TOA*      toda,
                                T_SM_DATA_EXT*  src_data,
                                CHAR*           sca,
                                T_ACI_TOA*      tosca,
                                SHORT           isReply,
                                UBYTE           alphabet )
{
  UBYTE ret;
  T_CONC_CMGS *prm = &concShrdPrm.specPrm.concCMGS;

  TRACE_FUNCTION ("concSMS_initSend ()");


  ret = concSMS_split ( tar_data, src_data, alphabet, CMGS_CONC );

  if ( ret EQ FALSE )
    return CONC_NOT_NEEDED;

  concShrdPrm.srcId = srcId;

  if (da)
  {
    memcpy(prm->da, da, strlen(da));
    prm->da[strlen(da)] = '\0';
    prm->p_da = prm->da;
  }
  else
  {
    prm->p_da = NULL;
  }
  if (toda)
  {
    memcpy(&prm->toda, toda, sizeof(T_ACI_TOA));
    prm->p_toda = &prm->toda;
  }
  else
  {
    prm->p_toda = NULL;
  }

  prm->data.len  = src_data->len;
  prm->data.data = src_data->data;

  if (sca)
  {
    memcpy(prm->sca, sca, strlen(sca));
    prm->sca[strlen(sca)] = '\0';
    prm->p_sca = prm->sca;
  }
  else
  {
    prm->p_sca = NULL;
  }
  if (tosca)
  {
    memcpy(&prm->tosca, tosca, sizeof(T_ACI_TOA));
    prm->p_tosca = &prm->tosca;
  }
  else
  {
    prm->p_tosca = NULL;
  }

  prm->isReply = isReply;
  prm->sent_bytes = 0;

  concShrdPrm.sentSegs = 0;

  /* fill user data header structure */
  concSMS_fillUDH ( udh,
                    concShrdPrm.udh.ref_num,
                    concShrdPrm.udh.max_num,
                    concShrdPrm.udh.seq_num );

  return CONC_NEEDED;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_initStoreInMem     |
+--------------------------------------------------------------------+

  PURPOSE : This function initialises shared parameter for CMGW.

*/
GLOBAL T_CONC_INIT_RETURN concSMS_initStoreInMem (  T_ACI_SM_DATA* tar_data,
                                               T_ACI_UDH_DATA* udh,
                                               T_ACI_CMD_SRC  srcId,
                                               SHORT          index,
                                               CHAR*          address,
                                               T_ACI_TOA*     toa,
                                               T_ACI_SMS_STAT stat,
                                               UBYTE          msg_ref,
                                               T_SM_DATA_EXT* src_data,
                                               CHAR*          sca,
                                               T_ACI_TOA*     tosca,
                                               SHORT          isReply,
                                               UBYTE          alphabet )
{
  T_CONC_INIT_RETURN ret;
  T_CONC_CMGW *prm = &concShrdPrm.specPrm.concCMGW;

  TRACE_FUNCTION ("concSMS_initStoreInMem ()");


  ret = (T_CONC_INIT_RETURN)concSMS_split ( tar_data, src_data, alphabet, CMGW_CONC );

  if ( ret EQ FALSE )
  {
    return CONC_NOT_NEEDED;
  }

  concShrdPrm.srcId = srcId;

  if (address)
  {
    memcpy(prm->da, address, strlen(address));
    prm->da[strlen(address)] = '\0';
    prm->p_da = prm->da;
  }
  else
  {
    prm->p_da = NULL;
  }
  if (toa)
  {
    memcpy(&prm->toda, toa, sizeof(T_ACI_TOA));
    prm->p_toda = &prm->toda;
  }
  else
  {
    prm->p_toda = NULL;
  }

  if ( stat NEQ SMS_STAT_NotPresent)
  {
    prm->stat = stat;
  }
  else
  {
    prm->stat = SMS_STAT_StoUnsent;
  }

  prm->msg_ref = msg_ref;
  prm->data.len  = src_data->len;
  prm->data.data = src_data->data;

  if (sca)
  {
    memcpy(prm->sca, sca, strlen(sca));
    prm->sca[strlen(sca)] = '\0';
    prm->p_sca = prm->sca;
  }
  else
  {
    prm->p_sca = NULL;
  }
  if (tosca)
  {
    memcpy(&prm->tosca, tosca, sizeof(T_ACI_TOA));
    prm->p_tosca = &prm->tosca;
  }
  else
  {
    prm->p_tosca = NULL;
  }

  prm->isReply = isReply;

  /* fill user data header structure */
  concSMS_fillUDH ( udh,
                    concShrdPrm.udh.ref_num,
                    concShrdPrm.udh.max_num,
                    concShrdPrm.udh.seq_num );

  return CONC_NEEDED;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_initCommand        |
+--------------------------------------------------------------------+

  PURPOSE : This function initialises shared parameter for CMGC.

*/
GLOBAL T_CONC_INIT_RETURN concSMS_initCommand ( T_ACI_CMD_SRC   srcId,
                                           SHORT           fo,
                                           SHORT           ct,
                                           SHORT           pid,
                                           SHORT           mn,
                                           CHAR*           da,
                                           T_ACI_TOA*      toda,
                                           T_ACI_CMD_DATA* data )
{
  T_CONC_CMGC *prm = &concShrdPrm.specPrm.concCMGC;

  TRACE_FUNCTION ("concSMS_initCommand ()");


  if ( ct NEQ COMMAND_TYPE_DELETE)
    return CONC_NOT_NEEDED;

  if ((mn < concShrdPrm.first_mr) OR
      (mn > concShrdPrm.first_mr + concShrdPrm.sentSegs-1))
    return CONC_NOT_NEEDED;


  if ( mn NEQ concShrdPrm.first_mr)
  {
    /* Error: segment is not the first segment of the SM */
    return CONC_ERROR;
  }
  else
  {
    concShrdPrm.srcId = srcId;

    prm->command_count = 0;
    prm->fo = (UBYTE)fo;
    prm->ct = (UBYTE)ct;
    prm->pid = (UBYTE)pid;

    if (da)
    {
      memcpy(prm->da, da, strlen(da));
      prm->da[strlen(da)] = '\0';
      prm->p_da = prm->da;
    }
    else
    {
      prm->p_da = NULL;
    }

    if (toda)
    {
      memcpy(&prm->toda, toda, sizeof(T_ACI_TOA));
      prm->p_toda = &prm->toda;
    }
    else
    {
      prm->p_toda = NULL;
    }

    ACI_MALLOC(prm->data.data, MAX_SM_CMD_LEN);
    memcpy ( prm->data.data, data->data, data->len );
    prm->data.len = data->len;

  }
  return CONC_NEEDED;
}




/********************** RAT Callback Fucntions ****************************/


GLOBAL void rConcSMS_PlusCMSS (UBYTE mr, UBYTE numSeg)
{
  UBYTE index;
  T_CONC_BUF_ELEM* elem;
  T_CONC_CMSS *prm = &concShrdPrm.specPrm.concCMSS;

  TRACE_FUNCTION ("rConcSMS_PlusCMSS()");


  /* save the first message reference */
  if (concShrdPrm.sentSegs EQ 0)
  {
    concShrdPrm.first_mr = mr;
  }

  /* increment number of successfully sent elements */
  concShrdPrm.sentSegs++;

  /* get next concat. list element */
  prm->currConcBufListElem = prm->currConcBufListElem->next;

  if (prm->skipStoSent)
  {
    /* skip segments with status SMS_STAT_StoSent */
    while (prm->currConcBufListElem)
    {
      elem = (T_CONC_BUF_ELEM*)prm->currConcBufListElem->msg;

      if (elem->status EQ SMS_STAT_StoSent)
      {
        prm->currConcBufListElem = prm->currConcBufListElem->next;
      }
      else
      {
        break;
      }
    } /* while */
  }

  if (prm->currConcBufListElem NEQ NULL)
  {
    elem = (T_CONC_BUF_ELEM*)prm->currConcBufListElem->msg;
    index = elem->rec_num;

    /* set mem2 (memory to which writing and sending operations are made)
       temporary to the value stored in conc buffer */
    smsShrdPrm.mem2 = elem->mem;

    sAT_PlusCMSS_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, index, prm->p_da, prm->p_toda,
                    rConcSMS_PlusCMSS, rConcSMS_PlusCMS_CMSS);


    elem->status = SMS_STAT_StoSent;


  }
  else /* if (concShrdPrm.currConcBufListElem NEQ NULL) */
  {

#ifdef _CONC_TESTING_
    char *sa;
    ACI_MALLOC(sa,KEY + BYTE_LTH);
    sprintf(sa,"+CMSS: %d,%d",concShrdPrm.first_mr, concShrdPrm.sentSegs);
    io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
    ACI_MFREE(sa);
#endif

    rAT_PlusCMSS(concShrdPrm.first_mr, (UBYTE)(concShrdPrm.sentSegs));

     /* restore value for mem2 */
    smsShrdPrm.mem2 = concShrdPrm.mem_store;

    R_AT ( RAT_OK, (T_ACI_CMD_SRC) concShrdPrm.srcId ) ( AT_CMD_CMSS );
    UNSET_CONC;
  }
}

GLOBAL void rConcSMS_PlusCMGS (UBYTE mr, UBYTE numSeg)
{
  T_ACI_SM_DATA data;
  T_ACI_UDH_DATA udh;
  USHORT len_left;
  T_CONC_CMGS *prm = &concShrdPrm.specPrm.concCMGS;

  TRACE_FUNCTION ("rConcSMS_PlusCMGS()");


  /* save the first message reference */
  if (concShrdPrm.udh.seq_num EQ 1)
  {
    concShrdPrm.first_mr = mr;
  }

  /* increment number of successfully sent elements */
  len_left = prm->data.len - prm->offset;

  concShrdPrm.sentSegs++;

  if (len_left NEQ 0)
  {
    prm->sent_bytes += concShrdPrm.max_sms_len;

    if ( len_left > concShrdPrm.max_sms_len )
    {
      data.len  = concShrdPrm.max_sms_len;
    }
    else
    {
      data.len = (UBYTE)len_left;
    }

    memcpy (data.data, prm->data.data+prm->offset, data.len);
    prm->offset += data.len;

    concShrdPrm.udh.seq_num++;


    /* fill user data header structure */
    concSMS_fillUDH ( &udh,
                      concShrdPrm.udh.ref_num,
                      concShrdPrm.udh.max_num,
                      concShrdPrm.udh.seq_num );

    sAT_PlusCMGS_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, prm->p_da, prm->p_toda,
                    &data, &udh, prm->p_sca, prm->p_tosca,
                    prm->isReply, rConcSMS_PlusCMGS, rConcSMS_PlusCMS_CMGS);


  }
  else
  {

#ifdef _CONC_TESTING_
    char *sa;
    ACI_MALLOC(sa,KEY + BYTE_LTH);
    sprintf(sa,"+CMGS: %d,%d",concShrdPrm.first_mr, concShrdPrm.udh.seq_num);
    io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
    ACI_MFREE(sa);

    ACI_MFREE(prm->data.data);

#endif

    rAT_PlusCMGS (concShrdPrm.first_mr, (UBYTE)(concShrdPrm.udh.seq_num));
    R_AT ( RAT_OK, (T_ACI_CMD_SRC)concShrdPrm.srcId ) ( AT_CMD_CMGS );
    UNSET_CONC;
  }
}



GLOBAL void rConcSMS_PlusCMGR ( T_ACI_CMGL_SM*  sm,
                                T_ACI_CMGR_CBM* cbm )
{
  T_CONC_CMGR *prm = &concShrdPrm.specPrm.concCMGR;

  TRACE_FUNCTION ("rConcSMS_PlusCMGR ()");

  if (prm->currConcBufListElem NEQ NULL)
  {
    T_CONC_BUF_ELEM *elem;
    elem = (T_CONC_BUF_ELEM *)prm->currConcBufListElem->msg;

    /* set mem1 (memory from which messages are read and deleted)
    * temporary to the value stored in conc buffer */
    smsShrdPrm.mem1 = elem->mem;

    sAT_PlusCMGR_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, elem->rec_num,
                    (T_ACI_SMS_READ)concShrdPrm.specPrm.concCMGR.rdMode,
                    rConcSMS_PlusCMGR);

    prm->currConcBufListElem = prm->currConcBufListElem->next;

#ifdef _CONC_TESTING_
  rAT_PlusCMGR_Ext (sm, cbm);
#else
  rAT_PlusCMGR (sm, cbm);
#endif

  }
  else /* if (concShrdPrm.currConcBufListElem NEQ NULL) */
  {
#ifdef _CONC_TESTING_
  rAT_PlusCMGR_Ext (sm, cbm);
#else
  rAT_PlusCMGR (sm, cbm);
#endif

    /* restore value for mem1 */
    smsShrdPrm.mem1 = concShrdPrm.mem_store;


  R_AT ( RAT_OK, (T_ACI_CMD_SRC) concShrdPrm.srcId ) ( AT_CMD_CMGR );
    UNSET_CONC;
  }
}

GLOBAL void rConcSMS_PercentCMGMDU (void)
{
  T_CONC_CMGR *prm = &concShrdPrm.specPrm.concCMGR;

  TRACE_FUNCTION ("rConcSMS_PercentCMGMDU ()");

  if (prm->currConcBufListElem NEQ NULL)
  {
    T_CONC_BUF_ELEM *elem;
    elem = (T_CONC_BUF_ELEM *)prm->currConcBufListElem->msg;

    sAT_PercentCMGMDU_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, elem->rec_num,
                         rConcSMS_PercentCMGMDU);

    prm->currConcBufListElem = prm->currConcBufListElem->next;

  }
  else /* if (concShrdPrm.currConcBufListElem NEQ NULL) */
  {
    if( concShrdPrm.srcId NEQ CMD_SRC_LCL )
      R_AT ( RAT_OK, (T_ACI_CMD_SRC) concShrdPrm.srcId ) ( AT_CMD_P_CMGMDU );
    UNSET_CONC;
  }
}

GLOBAL void rConcSMS_PlusCMGW ( UBYTE index, UBYTE numSeg, UBYTE mem)
{
  T_ACI_SM_DATA data;
  T_ACI_UDH_DATA udh;
  USHORT len_left;
  T_CONC_CMGW *prm = &concShrdPrm.specPrm.concCMGW;

  static UBYTE first_rec;

  TRACE_FUNCTION ("rConcSMS_PlusCMGW ()");


  /* save the first index */
  if (concShrdPrm.udh.seq_num EQ 1)
  {
    first_rec = index;
  }

  concSMS_addToConcatList((USHORT)concShrdPrm.udh.ref_num,
                                  prm->p_da,
                                  concShrdPrm.udh.max_num,
                                  concShrdPrm.udh.seq_num,
                                  index,
                                  (T_ACI_SMS_STAT)prm->stat,
                                  mem);

  concSMS_printConcatList();

  len_left = prm->data.len - prm->offset;

  if (len_left NEQ 0)
  {
    prm->sent_bytes += concShrdPrm.max_sms_len;

    if ( len_left > concShrdPrm.max_sms_len )
    {
      data.len  = concShrdPrm.max_sms_len;
    }
    else
    {
      data.len = (UBYTE)len_left;
    }

    memcpy (data.data, prm->data.data+prm->offset, data.len);
    prm->offset += data.len;

    concShrdPrm.udh.seq_num++;

    /* fill user data header structure */
    concSMS_fillUDH ( &udh,
                      concShrdPrm.udh.ref_num,
                      concShrdPrm.udh.max_num,
                      concShrdPrm.udh.seq_num );

    sAT_PlusCMGW_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, CMGW_IDX_FREE_ENTRY, 
		    prm->p_da, prm->p_toda, (T_ACI_SMS_STAT)prm->stat, prm->msg_ref,
                    &data, &udh, prm->p_sca, prm->p_tosca,
                    prm->isReply, rConcSMS_PlusCMGW, rConcSMS_PlusCMS_CMGW);

  }
  else
  {

#ifdef _CONC_TESTING_
    char *sa;
    ACI_MALLOC(sa,KEY + BYTE_LTH);
    sprintf(sa,"+CMGW: %d,%d",first_rec, concShrdPrm.udh.seq_num);
    io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
    ACI_MFREE(sa);
#endif

    rAT_PlusCMGW (first_rec, concShrdPrm.udh.seq_num, mem);
    R_AT ( RAT_OK, (T_ACI_CMD_SRC)concShrdPrm.srcId ) ( AT_CMD_CMGW );
    UNSET_CONC;
  }
}






GLOBAL void rConcSMS_PlusCMGD ( )
{
  T_CONC_CMGD *prm = &concShrdPrm.specPrm.concCMGD;
  T_CONC_BUF_ELEM *elem;
  T_ACI_LIST *conc_list;

  TRACE_FUNCTION ("rConcSMS_PlusCMGD ()");


  elem = (T_CONC_BUF_ELEM *)prm->currConcBufListElem->msg;

  /* remove the old element from concatenation list and free its memory */
  conc_list = concSMS_removeFromConcatList(prm->ref_num, prm->address, elem->rec_num);

  concSMS_printConcatList();

  if (conc_list NEQ NULL)
  {
    TRACE_EVENT("conc_list not null");
    elem = (T_CONC_BUF_ELEM *)conc_list->msg;

    /* save the concatenation list */
    prm->currConcBufListElem= conc_list;
    
    /* set mem1 (memory from which messages are read and deleted)
       temporary to the value stored in conc buffer */
    smsShrdPrm.mem1 = elem->mem;

    sAT_PlusCMGD_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, elem->rec_num, smsShrdPrm.status,
                          rConcSMS_PlusCMGD, rConcSMS_PlusCMS_CMGD);

  }
  else /* if (concShrdPrm.currConcBufListElem NEQ NULL) */
  {
    if (concShrdPrm.full.Conc_Full EQ TRUE)
    {
      concSMS_AddtoconcBuff();
      concShrdPrm.full.Conc_Full = FALSE;
    }

    /* restore value for mem1 */
    smsShrdPrm.mem1 = concShrdPrm.mem_store;

    TRACE_EVENT("RAT_OK in rConcSMS_PlusCMGD");
    R_AT ( RAT_OK, (T_ACI_CMD_SRC) concShrdPrm.srcId ) ( AT_CMD_CMGD );
    UNSET_CONC;
  }
}


GLOBAL void rConcSMS_PlusCMGC ( UBYTE mr )
{
  UBYTE mn;
  T_CONC_CMGC *prm = &concShrdPrm.specPrm.concCMGC;

  TRACE_FUNCTION ("rConcSMS_PlusCMGC ()");

  /* save the first message reference */
  if (concShrdPrm.udh.seq_num EQ 1)
  {
    concShrdPrm.first_mr = mr;
  }

  prm->command_count++;

  if (prm->command_count < concShrdPrm.sentSegs)
  {
    mn = concShrdPrm.first_mr + prm->command_count;

    sAT_PlusCMGC_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, prm->fo, prm->ct,
                   prm->pid, mn, prm->p_da, prm->p_toda,
                   (T_ACI_CMD_DATA*)&prm->data, rConcSMS_PlusCMGC);
  }
  else
  {

#ifdef _CONC_TESTING_
    char *sa;
    ACI_MALLOC(sa,KEY + BYTE_LTH);
    sprintf(sa,"+CMGC: %d",concShrdPrm.first_mr /*, prm->command_count*/);
    io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
    ACI_MFREE(sa);
#endif

    ACI_MFREE( prm->data.data );
    rAT_PlusCMGC (concShrdPrm.first_mr/*, prm->command_count*/);
    R_AT ( RAT_OK,(T_ACI_CMD_SRC) concShrdPrm.srcId ) ( AT_CMD_CMGC );
    UNSET_CONC;
  }
}


GLOBAL void rConcSMS_PlusCMS_CMSS (T_ACI_AT_CMD cmdId, T_ACI_CMS_ERR err,
                                   T_EXT_CMS_ERROR *ce)
{
  T_EXT_CMS_ERROR conc_error;
#ifdef _CONC_TESTING_
    char *sa;
#endif

  TRACE_FUNCTION ("rConcSMS_PlusCMS_CMSS ()");


  conc_error.id = CMSS_CONC;
  conc_error.specErr.errConcCMSS.segs =
    concShrdPrm.udh.max_num - concShrdPrm.sentSegs;

#ifdef _CONC_TESTING_
  ACI_MALLOC(sa,KEY + BYTE_LTH);
  sprintf(sa,"+CMS ERROR: %d,%d",err, conc_error.specErr.errConcCMSS.segs);
  io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
  ACI_MFREE(sa);
  rCI_PlusCMS ( cmdId, err, NULL );
#endif

  /* restore value for mem2 */
  smsShrdPrm.mem2 = concShrdPrm.mem_store;

  rAT_PlusCMS (cmdId, err, &conc_error);
  UNSET_CONC;
}


GLOBAL void rConcSMS_PlusCMS_CMGS (T_ACI_AT_CMD cmdId, T_ACI_CMS_ERR err,
                                   T_EXT_CMS_ERROR *ce)
{
  T_EXT_CMS_ERROR conc_error;
#ifdef _CONC_TESTING_
    char *sa;
#endif

  TRACE_FUNCTION ("rConcSMS_PlusCMS_CMGS ()");


  conc_error.id = CMGS_CONC;
  conc_error.specErr.errConcCMGS.sent_chars =
    concShrdPrm.specPrm.concCMGS.sent_bytes;
  conc_error.specErr.errConcCMGS.ref_num    = concShrdPrm.udh.ref_num;
  conc_error.specErr.errConcCMGS.next_seg   = concShrdPrm.udh.seq_num;
  conc_error.specErr.errConcCMGS.max_num    = concShrdPrm.udh.max_num;

#ifdef _CONC_TESTING_
  ACI_MALLOC(sa,KEY + BYTE_LTH);
  sprintf(sa,"+CMS ERROR: %d,%d,%d,%d,%d",err,
                          conc_error.specErr.errConcCMGS.sent_chars,
                          conc_error.specErr.errConcCMGS.ref_num,
                          conc_error.specErr.errConcCMGS.next_seg,
                          conc_error.specErr.errConcCMGS.max_num);

  io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
  ACI_MFREE(sa);
  rCI_PlusCMS ( cmdId, err, NULL );
#endif

  rAT_PlusCMS (cmdId, err, &conc_error);
  UNSET_CONC;
}


GLOBAL void rConcSMS_PlusCMS_CMGW (T_ACI_AT_CMD cmdId, T_ACI_CMS_ERR err,
                                   T_EXT_CMS_ERROR *ce)
{
  T_EXT_CMS_ERROR conc_error;
#ifdef _CONC_TESTING_
  char *sa;
#endif

  TRACE_FUNCTION ("rConcSMS_PlusCMS_CMGW ()");


  conc_error.id = CMGW_CONC;
  conc_error.specErr.errConcCMGW.sent_chars =
    concShrdPrm.specPrm.concCMGW.sent_bytes;
  conc_error.specErr.errConcCMGW.ref_num    = concShrdPrm.udh.ref_num;
  conc_error.specErr.errConcCMGW.next_seg   = concShrdPrm.udh.seq_num;
  conc_error.specErr.errConcCMGW.max_num    = concShrdPrm.udh.max_num;

#ifdef _CONC_TESTING_
  ACI_MALLOC(sa,KEY + BYTE_LTH);
  sprintf(sa,"+CMS ERROR: %d,%d,%d,%d,%d",err,
                          conc_error.specErr.errConcCMGW.sent_chars,
                          conc_error.specErr.errConcCMGW.ref_num,
                          conc_error.specErr.errConcCMGW.next_seg,
                          conc_error.specErr.errConcCMGW.max_num);
  io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
  ACI_MFREE(sa);
  rCI_PlusCMS ( cmdId, err, NULL );
#endif

  rAT_PlusCMS (cmdId, err, &conc_error);
  UNSET_CONC;
}

GLOBAL void rConcSMS_PlusCMS_CMGD (T_ACI_AT_CMD cmdId, T_ACI_CMS_ERR err,
                                   T_EXT_CMS_ERROR *ce)
{
  T_CONC_CMGD *prm = &concShrdPrm.specPrm.concCMGD;
  T_CONC_BUF_ELEM *elem;
  T_EXT_CMS_ERROR conc_error;

  TRACE_FUNCTION ("rConcSMS_PlusCMS_CMGD ()");

  conc_error.id = EMPTY;

  prm->error_count++;
  if (prm->error_count EQ concShrdPrm.udh.max_num)
  {

#ifdef _CONC_TESTING_
    char *sa;
    ACI_MALLOC(sa,KEY + BYTE_LTH);
    sprintf(sa,"+CMS ERROR: %d",err);
    io_sendMessage(concShrdPrm.srcId, sa, ATI_NORMAL_OUTPUT);
    ACI_MFREE(sa);
    rCI_PlusCMS ( cmdId, err, NULL );
#endif

    elem = (T_CONC_BUF_ELEM *)prm->currConcBufListElem->msg;
    /* remove the old element from concatenation list and free its memory */
    concSMS_removeFromConcatList(prm->ref_num, prm->address, elem->rec_num);
    concSMS_printConcatList();

    /* restore value for mem1 */
    smsShrdPrm.mem1 = concShrdPrm.mem_store;

    rAT_PlusCMS ( cmdId, err, &conc_error);
    UNSET_CONC;
  }
  else
  {
    /* continue with the next segment */
    rConcSMS_PlusCMGD();
  }
}
#endif /* TI_PS_FF_CONC_SMS */




/*************** Functions which must be called by MFW ***************/






/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : SMS_getSMSType                 |
+--------------------------------------------------------------------+

  PURPOSE : This function must be called by the MFW to detect the SMS
            type from the information element identifier.

*/
GLOBAL T_SMS_TYPE SMS_getSMSType( T_ACI_UDH_DATA* udh, char *address, UBYTE detMode)
{
  USHORT ref_num = 0;
  UBYTE seq_num = 0;
  UBYTE max_num = 0;
  USHORT count;
  T_CONC_BUF* concBuf;

  TRACE_FUNCTION ("SMS_getSMSType()");
  TRACE_EVENT_P1("SMS_getSMSType, mode: %d", detMode);


  if (udh->len EQ 0)
  {
    /* SMS does not contain UDH --> normal SMS */
    return NORMAL;
  }

  /* check if IE is conc SMS */
  if ((udh->data[0] EQ SMS_IEI_CONC_8BIT) OR (udh->data[0] EQ SMS_IEI_CONC_16BIT))
  {
  if (udh->data[0] EQ SMS_IEI_CONC_8BIT)
    {
      ref_num = udh->data[2];
      max_num = udh->data[3];
      seq_num = udh->data[4];
    }

  if (udh->data[0] EQ SMS_IEI_CONC_16BIT)
    {
      ref_num = (udh->data[2] & 0x00FF) << 8u;
      ref_num += udh->data[3];
      max_num = udh->data[4];
      seq_num = udh->data[5];
    }

    switch (detMode)
    {
    
    case MODE1:
    /* This mode is for rAT_PlusCMT. No Concatenation buffer is needed at all */
      break;

    case MODE2:
    /* This mode is for rAT_PlusCMTI. This mode requires the allocation of a new 
     * Concatenation buffer (later in concSMS_Collect). Make sure that in case 
     * the conc buffer is full no new CSMS is handled anymore 
     */
      concBuf = concSMS_getConcBuffer( ref_num, address );

      /* if a new conc buffer is be needed, check if available */
      if (concBuf EQ NULL)
      {
        if (concSMS_concBufferAvail() EQ FALSE)
        {
          return NORMAL_IND_CSMS;
        }
         
        /* Limit the maximum number of CSMS segments to MAX_SEG_TOTAL. Check 
         * only if a new Concatenation buffer is required.
         */
        if (concShrdPrm.elem_count+max_num > MAX_SEG_TOTAL)
        {
          return NORMAL_IND_CSMS;
        }
      }
      break;

    case MODE3:
    /* This mode is for rAT_PlusCMGL, rAT_PlusCMGR, sms_store_new_msg_info and 
     * sms_store_new_msg_info. Only segments that have been previously stored 
     * in the Concatenation buffer can be handled as CSMS. 
     */
      concBuf = concSMS_getConcBuffer( ref_num, address );
      if (concBuf EQ NULL)
      {
        return NORMAL_IND_CSMS;
      }

      /* check if conc buffer is incomplete */
      count = get_list_count(concBuf->list);
      if ((count < concBuf->max_num) AND (count < CONC_MAX_SEGS))
      {
        return NORMAL_IND_CSMS;
      }
      break;

    default:
      TRACE_ERROR("Wrong detection mode in SMS_getSMSType");
      return UNKNOWN;
    }

    /* check if sequence number is in range */
    if (seq_num <= CONC_MAX_SEGS)
    {
      return CONCATE;
    }
    else
    {
      return NORMAL_IND_CSMS;
    }
  }
  else
  {
    /* unknown IE in UDH --> no CSMS */
    return UNKNOWN;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_GetFirstIndex      |
+--------------------------------------------------------------------+

  PURPOSE : This function provides MFW with the first index of a given
            concatenated SMS (identified by its message reference).

            returns first index: (0 means no message found...)
*/
#define FIRST_SEQ_NUM (1)

GLOBAL T_CONC_BUF_ELEM *concSMS_GetFirstIndex_ext ( USHORT msg_ref, char *address  )
{


  T_CONC_BUF      *concBuf;
  T_CONC_BUF_ELEM *concBufElem;

  TRACE_FUNCTION ("concSMS_GetFirstIndex()");

  /* search for concatenation buffer */
  concBuf = concSMS_getConcBuffer( msg_ref, address );

  if( concBuf EQ NULL )
  {
    TRACE_EVENT_P1("ERROR: unknown msg_ref: 0x%04x", msg_ref);
    return( NULL );
  }

  /* search for the first sequence */
  concBufElem = find_element(concBuf->list, FIRST_SEQ_NUM, concSMS_findSeqNumElemCB);

  if( concBufElem EQ NULL )
  {
    TRACE_EVENT("ERROR: first sequence not found");
    return( NULL );
  }

  TRACE_EVENT_P1("first rec_num: %d", concBufElem->rec_num);
  TRACE_EVENT_P1 ("concSMS_GetFirstIndex_ext(), rec_num=%d", concBufElem->rec_num);

  /* return index of first segment */
  return(concBufElem);

}

GLOBAL UBYTE concSMS_GetFirstIndex ( USHORT msg_ref, char *address  )
{
  T_CONC_BUF_ELEM *concBufElem = concSMS_GetFirstIndex_ext(msg_ref, address);

  if( concBufElem EQ NULL )
  {
    TRACE_EVENT("ERROR: first sequence not found");
    return(0);
  }
  else
  return(concBufElem->rec_num);

}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_GetMsgRef          |
+--------------------------------------------------------------------+

  PURPOSE : This function provides MFW with the message reference of a given
            concatenated SMS (decoded from the header).

            returns msg ref.
*/

GLOBAL USHORT concSMS_GetMsgRef ( T_ACI_CMGL_SM  *sm )
{
  USHORT ref_num;

  TRACE_FUNCTION ("concSMS_GetMsgRef()");

  if( sm EQ NULL )
  {
    TRACE_ERROR("sm is NULL");
    return 0;
  }

    /* 8-bit reference number */
  if (sm->udh.data[0] EQ SMS_IEI_CONC_8BIT)
  {
    ref_num = sm->udh.data[2];
  }

  /* 16-bit reference number */
  else if (sm->udh.data[0] EQ SMS_IEI_CONC_16BIT)
  {
    /* MSB */
    ref_num = (sm->udh.data[2] & 0x00FF) << 8u;   /* 23.040 9.1.2.1 */

    /* LSB */
    ref_num |= sm->udh.data[3];
  }
  else
  {
    TRACE_ERROR("sm->udh.data unknown");
    return 0;
  }

  TRACE_EVENT_P1("ref_num: 0x%04x", ref_num);

  return(ref_num);
}

/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_Collect            |
+--------------------------------------------------------------------+

  PURPOSE : This function must be called by the MFW. The function
            reassembles the received SM segments.

            Set 'isStored' to:
                            TRUE,  if rAT_PlusCMTI() was called
                            FALSE, if rAT_PlusCMT()  was called
                            FALSE, if rAT_PlusCMGR() was called

*/
GLOBAL T_CONC_ASSEMBLY_RETURN concSMS_Collect ( T_SM_DATA_EXT *data_conc,
                                               T_ACI_CMGL_SM  *sm,
                                               UBYTE          isStored,
                                               T_ACI_SMS_STOR mem_aci)
{
  USHORT ref_num = 0;
  UBYTE  max_num = 0;
  UBYTE  seq_num = 0;
  UBYTE  rec_num;
  T_SM_ASSEMBLY  *assembly_elem;
  T_SEG_BUF_ELEM *segBufElem;
  T_SM_DATA_EXT  data;
  T_ACI_SMS_STAT status;
  UBYTE next_exp_num;
  UBYTE ret;
  static UBYTE i = 0;
  UBYTE mem_psa;

 

  CHAR *address;

  TRACE_FUNCTION ("concSMS_Collect()");

  /* initialize data_conc->len */
  data_conc->len = 0; /* ACI-SPR-16372 */
  /* extract parameters from user data header */

  /* 8-bit reference number */
  if (sm->udh.data[0] EQ SMS_IEI_CONC_8BIT)
  {
    ref_num = sm->udh.data[2];
    max_num = sm->udh.data[3];
    seq_num = sm->udh.data[4];
  }

  /* 16-bit reference number */
  else if (sm->udh.data[0] EQ SMS_IEI_CONC_16BIT)
  {
    /* MSB */
    ref_num = (sm->udh.data[2] & 0x00FF) << 8u;    /* 23.040 9.1.2.1 */

    /* LSB */
    ref_num += sm->udh.data[3];

    max_num = sm->udh.data[4];
    seq_num = sm->udh.data[5];
  }

  rec_num = sm->msg_ref;
  status  = sm->stat;
  address = sm->adress;

  data.data = sm->data.data;
  data.len  = sm->data.len;

  TRACE_EVENT_P1("rec_num:%d", rec_num);
  TRACE_EVENT_P1("concSMS_GetFirstIndex():%d",
                  concSMS_GetFirstIndex (ref_num, sm->adress));
  TRACE_EVENT_P1("ref_num:0x%04x", ref_num);
  TRACE_EVENT_P1("concSMS_GetMsgRef():0x%04x", concSMS_GetMsgRef ( sm ));
  TRACE_EVENT_P1("max_num:%d", max_num);
  TRACE_EVENT_P1("seq_num:%d", seq_num);

  /* try to get an existing assembly buffer */
  assembly_elem = concSMS_getAsBuffer(ref_num, address);

  /* if no assembly buffer found, then assume that is a new conc. SM */
  if (assembly_elem EQ NULL)
  {
    next_exp_num = 1;
  }
  else
  {
    next_exp_num = assembly_elem->next_exp_num;
  }


  /* the received seq_num is not valid */
  if ( (seq_num EQ 0) OR (seq_num > max_num) OR (seq_num < next_exp_num) )
  {
    return CONC_ERR_UNKN;
  }



  /*
   * If CMTI, then add every received segment to concatenation buffer.
   * The segments in the concatenation buffer were also stored in non-volatile
   * memory.
   */
  if (isStored)
  {
     if (cmhSMS_getMemPsa(mem_aci, &mem_psa) EQ FALSE)
    {
       return CONC_ERR_UNKN;
    }

    ret = concSMS_addToConcatList(ref_num, address, max_num,
                                  seq_num, rec_num, status, mem_psa);

    if (ret EQ FALSE)
    {
      dFLAG = TRUE;
      RefNum_Del = ref_num;
      concShrdPrm.full.Conc_Full = TRUE;
      concShrdPrm.full.RefNum = ref_num;
      memcpy(Addres,address,sizeof(Addres));
      concShrdPrm.full.MaxNum = max_num;
      concShrdPrm.full.SeqNum[i] = seq_num;
      concShrdPrm.full.RecNum[i] = rec_num;
       concShrdPrm.full.MemType[i] = mem_psa;
      i++;
      concShrdPrm.full.Numsegs = i;
      /* error: concatenation buffer full */
      return CONC_ERR_BUF_FULL;
    }
  }
  i = 0;
  if ( seq_num > next_exp_num )
  {
    /*
     * the received segment is not in sequence
     *  -> add it to the segment buffer
     */
    ret = concSMS_addToSegBuffer(ref_num, address, seq_num,
                                 rec_num, status, &data);
    if (ret EQ FALSE)
    {
      /* error: segment buffer full */
      return CONC_ERR_BUF_FULL;
    }
    return CONC_CONTINUED;
  }
  else
  {
    /*
     * the received segment is in sequence
     */

    while (1)
    {

      /* add segment to assembly buffer */
      assembly_elem = concSMS_addToAsBuffer (ref_num, address, max_num, &data);


      /* free data memory only for data from segment buffer */
      if (data.data NEQ sm->data.data)
      {
        ACI_MFREE (data.data);
      }

      /* error: no assembly buffer available */
      if (assembly_elem EQ NULL)
      {
        return CONC_ERR_BUF_FULL;
      }

      /* assembly of concatenated SM is completed */
      if (assembly_elem->next_exp_num EQ max_num+1)
      {
        concSMS_removeFromAsBuffer(data_conc, ref_num, address);
        concSMS_sortConcatList(ref_num, address);
        return CONC_COMPLETED;
      }

      /* maximum reached in assembly buffer , assembly is NOT completed */
      if (assembly_elem->seg_count EQ CONC_MAX_SEGS)
      {
        concSMS_getFromAsBuffer(data_conc, ref_num, address);
        concSMS_sortConcatList(ref_num, address);
        return CONC_COMPLETED;
      }

      /* search and remove the next segment from segment buffer */
      segBufElem = concSMS_removeFromSegBuffer(ref_num,
                                               address,
                                               assembly_elem->next_exp_num);
      if ( segBufElem EQ NULL )
      {
        /* no segment found */
        return CONC_CONTINUED;
      }

      /* for adding to concatenation list */
      rec_num   = segBufElem->rec_num;
      status    = (T_ACI_SMS_STAT)segBufElem->status;

      /* for adding to assembly buffer */
      data.data = segBufElem->data.data;
      data.len  = segBufElem->data.len;

      ACI_MFREE(segBufElem);
    } /* while */
  } /* if ( seq_num EQ next_exp_num ) */
}



GLOBAL void concSMS_Init()
{
  concShrdPrm.isConcatenated = FALSE;

  memset (&concShrdPrm, 0, sizeof(T_CONC_SHRD_PRM) );

  memset (&assembly_list, 0, sizeof(T_SM_ASSEMBLY)*MAX_BUF_ELEMS);
  memset (&segBuf_list,   0, sizeof(T_SEG_BUF)    *MAX_BUF_ELEMS);
  memset (&concBuf_list,  0, sizeof(T_CONC_BUF)   *MAX_CONC_BUF_ELEMS);

  concShrdPrm.l_uncomp8bit_data      = L_UNCOMP_8BIT_DATA;
  concShrdPrm.l_uncomp7bit_data      = L_UNCOMP_7BIT_DATA;
  concShrdPrm.l_uncomp8bit_data_conc = L_UNCOMP_8BIT_DATA_CONC;
  concShrdPrm.l_uncomp7bit_data_conc = L_UNCOMP_7BIT_DATA_CONC;
  concShrdPrm.elem_count = 0;
#ifdef _CONC_TESTING_
#ifndef _SIMULATION_
  concSMS_InitForTesting();
#endif
#endif
}


/* only for test purposes */
GLOBAL void concSMS_InitForTesting()
{
  concShrdPrm.concTesting            = TRUE;
  concShrdPrm.l_uncomp8bit_data      = L_UNCOMP_8BIT_DATA_TST;
  concShrdPrm.l_uncomp7bit_data      = L_UNCOMP_7BIT_DATA_TST;
  concShrdPrm.l_uncomp8bit_data_conc = L_UNCOMP_8BIT_DATA_CONC_TST;
  concShrdPrm.l_uncomp7bit_data_conc = L_UNCOMP_7BIT_DATA_CONC_TST;
}


/*
+--------------------------------------------------------------------+
| PROJECT :                     MODULE  : CONC_SMS                   |
| STATE   : code                ROUTINE : concSMS_delAllIncompleteMsg|
+--------------------------------------------------------------------+

  PURPOSE : This function must be called by the MFW to clean all
            segment buffers, assembly buffers and remove all
            incompleted SM from concat. buffer and non-volatile memory.

*/
GLOBAL void concSMS_delAllIncompleteMsg( )
{
  T_SEG_BUF       *segBuf = NULL;
  T_SEG_BUF_ELEM  *segBufElem;
  T_CONC_BUF      *concBuf = NULL;
  T_CONC_BUF_ELEM *concBufElem;
  T_SM_ASSEMBLY   *assembly_elem;
  USHORT count;

  UBYTE i;

  TRACE_FUNCTION ("concSMS_delAllIncompleteMsg()");


  concShrdPrm.srcId = CMD_SRC_LCL;

  for (i=0; i<MAX_BUF_ELEMS; i++)
  {
    /* delete assembly buffer */
    if (assembly_list[i].in_use)
    {
      assembly_elem = &assembly_list[i];
      assembly_elem->in_use = FALSE;
      ACI_MFREE(assembly_elem->data.data);
    }


    /* delete segment buffer */
    if (segBuf_list[i].in_use)
    {
      segBuf = &segBuf_list[i];

      /* remove element from segment buffer */
      segBufElem = remove_first_element(segBuf->list);

      if (segBufElem NEQ NULL)
      {
        /* free memory */
        ACI_MFREE(segBufElem->data.data);
        ACI_MFREE(segBufElem);
      }
      else
      {
        segBuf->in_use = FALSE;
      }
    }
  } /* for */


  for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
  {
    /* find concat. buffer in use */
    if (concBuf_list[i].in_use)
    {

      /* get number of segments in this concat. buffer */
      count = get_list_count(concBuf_list[i].list);

      /* conc buffer is incomplete */
      if ((count < concBuf_list[i].max_num) AND (count < CONC_MAX_SEGS))
      {
        if (count EQ 0)
        {
          /* last element of one conc buffer was deleted */
          concBuf_list[i].in_use = FALSE;
          R_AT ( RAT_OK,(T_ACI_CMD_SRC) concShrdPrm.srcId ) ( AT_CMD_CMGC );
          continue;
        }
        concBuf = &concBuf_list[i];
        break;
      }
    }
  }

  if (concBuf)
  {
    /* remove element from concat. buffer */
    concBufElem = remove_first_element(concBuf->list);

    if (concBufElem)
    {

      /* delete segment from non-volatile memory */
#ifdef TI_PS_FF_CONC_SMS
      sAT_PlusCMGD_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, concBufElem->rec_num, 
               smsShrdPrm.status, concSMS_delAllIncompleteMsg, rConcSMS_PlusCMS_CMGD);
#else
      sAT_PlusCMGD_Gl(concShrdPrm.srcId, concBufElem->rec_num, smsShrdPrm.status,
                      concSMS_delAllIncompleteMsg, rAT_PlusCMS);
#endif /* TI_PS_FF_CONC_SMS */

      /* free memory */
      ACI_MFREE(concBufElem);
       /* decrease total count of stored CSMS segments by 1*/
      concShrdPrm.elem_count--;
    }
  }
}



/************** Help Functions ******************************/


/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CONC_SMS                |
|                                 ROUTINE : concSMS_printConcatList |
+-------------------------------------------------------------------+

  PURPOSE :
*/

LOCAL void concSMS_printConcatList ()
{
  T_ACI_LIST    *current;
  UBYTE i;
  T_CONC_BUF *concBuf;
  T_CONC_BUF_ELEM *elem;


  TRACE_EVENT("Concatenation List:");
  for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
  {
    if (concBuf_list[i].in_use EQ TRUE)
    {
      concBuf = &concBuf_list[i];
      TRACE_EVENT_P2("   ref_num: 0x%04x , max_num: %u",
                      concBuf->ref_num, concBuf->max_num);
      current = concBuf->list;
      while (current)
      {
        elem = (T_CONC_BUF_ELEM*) current->msg;
        TRACE_EVENT_P4("   seq_num: %d , rec_num: %d , status: %d, mem= %d",
                       elem->seq_num, elem->rec_num, elem->status, elem->mem);
        current = current->next;
      }
    }
  }
}

/* Marcus: Issue 872: 03/10/2002
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CONC_SMS                |
|                                 ROUTINE : concSMS_findMaxRefNum   |
+-------------------------------------------------------------------+

  PURPOSE : Find the highest value of concBuf_list[i].ref_num
*/

LOCAL USHORT concSMS_findMaxRefNum(void)
{
  UBYTE i;
  USHORT ref_num = 0;
  TRACE_FUNCTION("concSMS_findMaxRefNum()");
  for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
  {
    if (concBuf_list[i].in_use EQ TRUE)
    {
      if ((concBuf_list[i].ref_num & 0x00ff)> ref_num)     /* since we use only 8-Bit ref_num */
        ref_num = concBuf_list[i].ref_num & 0x00ff;
    }
  }
  return ref_num;
}


GLOBAL void concSMS_clearIncompleteMsg()
{
  
  T_SEG_BUF       *segBuf = NULL;
  T_SEG_BUF_ELEM  *segBufElem;
  T_SM_ASSEMBLY   *assembly_elem;
  T_CONC_BUF      *concBuf = NULL;
  T_CONC_BUF_ELEM *concBufElem;
  UBYTE i;
  

 
  TRACE_FUNCTION ("concSMS_clearIncompleteMsg()");


  if (RefNum_Del EQ concShrdPrm.full.RefNum)
  {
    concShrdPrm.full.Conc_Full = FALSE;
    concShrdPrm.full.RefNum = 0xFF;
  }


  for (i=0; i<MAX_BUF_ELEMS; i++)
  {
    /* delete assembly buffer */
    if (assembly_list[i].in_use AND assembly_list[i].ref_num EQ RefNum_Del)
    {
      assembly_elem = &assembly_list[i];
      assembly_elem->in_use = FALSE;
      ACI_MFREE(assembly_elem->data.data);
      break;
    }


    /* delete segment buffer */
    if (segBuf_list[i].in_use AND segBuf_list[i].ref_num EQ RefNum_Del)
    {
      
      segBuf = &segBuf_list[i];
     

      /*remove element from segment buffer */
      segBufElem = remove_first_element(segBuf->list);

      if (segBufElem NEQ NULL)
      {     
        segBuf->in_use = FALSE;
        /* free memory */
        ACI_MFREE(segBufElem->data.data);
        ACI_MFREE(segBufElem);
        
        
      }
      else
      {
        
        segBuf->in_use = FALSE;
        
      }
      break;
    }
  
  }

  for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
  {
    /* find concat. buffer in use */
    if (concBuf_list[i].in_use AND concBuf_list[i].ref_num EQ RefNum_Del)
    {
        concBuf_list[i].in_use = FALSE;
        concBuf = &concBuf_list[i];            
        break;
     
    }
  }

  if (concBuf)
  {   
    /* remove element from concat. buffer */
    concBufElem = remove_first_element(concBuf->list);

    if (concBufElem)
    {       
      /* free memory */
      ACI_MFREE(concBufElem);
      /* decrease total count of stored CSMS segments by 1*/
      concShrdPrm.elem_count--;

    }
  }
}



GLOBAL void concSMS_AddtoconcBuff(void)
{
  UBYTE i;


  TRACE_FUNCTION("concSMS_AddtoconcBuff()");

  for (i=0;i<concShrdPrm.full.Numsegs;i++)
  {
    concSMS_addToConcatList(concShrdPrm.full.RefNum,Addres,
                  concShrdPrm.full.MaxNum,concShrdPrm.full.SeqNum[i],
                  concShrdPrm.full.RecNum[i],SMS_STAT_RecRead,concShrdPrm.full.MemType[i]);
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : CONC_SMS                |
|                                 ROUTINE : concSMS_DeleteConcList  |
+-------------------------------------------------------------------+

  PURPOSE  : This function is used to delete the concatinated SMS, when
             the CMGD command contails the delete flag greater than ZERO.

*/

GLOBAL void concSMS_DeleteConcList ( )
{
  T_CONC_CMGD *prm = &concShrdPrm.specPrm.concCMGD;
  T_CONC_BUF_ELEM *elem;
  T_ACI_LIST *conc_list;

  TRACE_FUNCTION ("concSMS_DeleteConcList()");


  elem = (T_CONC_BUF_ELEM *)prm->currConcBufListElem->msg;

  /* remove the old element from concatenation list and free its memory */
  conc_list = concSMS_removeFromConcatList(prm->ref_num, prm->address, elem->rec_num);

  concSMS_printConcatList();

  if (conc_list NEQ NULL)
  {
    TRACE_EVENT("conc_list not null");
    elem = (T_CONC_BUF_ELEM *)conc_list->msg;

    /* save the concatenation list */
    prm->currConcBufListElem= conc_list;  
  }
  else /* if (concShrdPrm.currConcBufListElem NEQ NULL) */
  {
    TRACE_EVENT("Concat List is NULL : concSMS_DeleteConcList");
    UNSET_CONC;
  }
}

#ifdef TI_PS_FF_CONC_SMS
GLOBAL void rConcSMS_PercentCMGR ( T_ACI_CMGL_SM*  sm,
                                   T_ACI_CMGR_CBM* cbm )
{
  T_CONC_CMGR *prm = &concShrdPrm.specPrm.concCMGR;

  TRACE_FUNCTION ("rConcSMS_PercentCMGR ()");

  if (prm->currConcBufListElem NEQ NULL)
  {
    T_CONC_BUF_ELEM *elem;
    elem = (T_CONC_BUF_ELEM *)prm->currConcBufListElem->msg;

    sAT_PercentCMGR_Gl((T_ACI_CMD_SRC)concShrdPrm.srcId, elem->rec_num,
                   (T_ACI_SMS_READ) concShrdPrm.specPrm.concCMGR.rdMode,
                    rConcSMS_PercentCMGR);

    prm->currConcBufListElem = prm->currConcBufListElem->next;

#ifdef _CONC_TESTING_
  rAT_PercentCMGR_Ext (sm, cbm);
#else
  rAT_PercentCMGR (sm, cbm);
#endif

  }
  else /* if (concShrdPrm.currConcBufListElem NEQ NULL) */
  {
#ifdef _CONC_TESTING_
  rAT_PercentCMGR_Ext (sm, cbm);
#else
  rAT_PercentCMGR (sm, cbm);
#endif

  R_AT ( RAT_OK, (T_ACI_CMD_SRC)concShrdPrm.srcId ) ( AT_CMD_P_CMGR );
    UNSET_CONC;
  }
}   
    
#endif /* TI_PS_FF_CONC_SMS */   
/* 
 * This functions checks if the conc can be extended. If the maximum 
 * capacity is reached the CSMS will be handled as normal (single) SMS.
 */
BOOL concSMS_concBufferAvail()
{
  USHORT count=0;
  int i;

  TRACE_FUNCTION("concSMS_concBufferAvail");

  /* check if there is a free conc buffer available */
  for (i=0; i<MAX_CONC_BUF_ELEMS; i++)
  {  
    if (concBuf_list[i].in_use EQ TRUE)
    {
      count++;
    }
  }
  if (count >= MAX_CONC_BUF_ELEMS)
  {
    return FALSE; /* all conc buffer in use, no free available */
  }

  return TRUE;
}