view src/g23m-aci/aci/conc_sms.c @ 516:1ed9de6c90bd

src/g23m-gsm/sms/sms_for.c: bogus malloc removed The new error handling code that was not present in TCS211 blob version contains a malloc call that is bogus for 3 reasons: 1) The memory allocation in question is not needed in the first place; 2) libc malloc is used instead of one of the firmware's proper ways; 3) The memory allocation is made inside a function and then never freed, i.e., a memory leak. This bug was caught in gcc-built FreeCalypso fw projects (Citrine and Selenite) because our gcc environment does not allow any use of libc malloc (any reference to malloc produces a link failure), but this code from TCS3.2 is wrong even for Magnetite: if this code path is executed repeatedly over a long time, the many small allocations made by this malloc call without a subsequent free will eventually exhaust the malloc heap provided by the TMS470 environment, malloc will start returning NULL, and the bogus code will treat it as an error. Because the memory allocation in question is not needed at all, the fix entails simply removing it.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 22 Jul 2018 06:04:49 +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;
}