view src/aci2/aci/conc_sms.c @ 662:8cd8fd15a095

SIM speed enhancement re-enabled and made configurable TI's original code supported SIM speed enhancement, but Openmoko had it disabled, and OM's disabling of speed enhancement somehow caused certain SIM cards to start working which didn't work before (OM's bug #666). Because our FC community is much smaller in year 2020 than OM's community was in their day, we are not able to find one of those #666-affected SIMs, thus the real issue they had encountered remains elusive. Thus our solution is to re-enable SIM speed enhancement and simply wait for if and when someone runs into a #666-affected SIM once again. We provide a SIM_allow_speed_enhancement global variable that allows SIM speed enhancement to be enabled or disabled per session, and an /etc/SIM_spenh file in FFS that allows it to enabled or disabled on a non-volatile basis. SIM speed enhancement is now enabled by default.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 24 May 2020 05:02:28 +0000
parents 93999a60b835
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 "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 )
{
  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));
  concBufElem->seq_num = seq_num;
  concBufElem->rec_num = rec_num;
  concBufElem->status  = status;

  /* 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);

  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)
{
  static UBYTE sref_num = 0; 

  /* Temp variable for the value returned by concSMS_findMaxRefNum() */
  UBYTE t_num = 0; 

  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 */
  t_num = 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)
  {
    /* sref_num  assigned  the  value  depending  on  the  value  returned by 
       concSMS_findMaxRefNum() and the value already stored  in  the sref_num
       so that for every sent conc SMS reference number value will be different */

    sref_num=((sref_num+1 > t_num)? (sref_num) : t_num)+1;
    concShrdPrm.udh.ref_num = sref_num;
    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;
}




/********************** 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 i;
  T_CONC_BUF_ELEM *concBufElem;
  USHORT count;

  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 found */
    if (concBufElem NEQ NULL)
    {
      break;
    }
  }

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

  *concBuf = &concBuf_list[i];
   count = get_list_count((*concBuf)->list);


  /* 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];
    count = get_list_count((*concBuf)->list);

    /* check if concat. list is complete */
    if (count EQ (*concBuf)->max_num)
    {
      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];
      count = get_list_count((*concBuf)->list);
      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 );

  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 );

  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 );

  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 = 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;

    sAT_PlusCMSS_Gl(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));
    R_AT ( RAT_OK,   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(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,   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;

    sAT_PlusCMGR_Gl(concShrdPrm.srcId, elem->rec_num,
                    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

  R_AT ( RAT_OK,   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(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,   concShrdPrm.srcId ) ( AT_CMD_P_CMGMDU );
    UNSET_CONC;
  }
}

GLOBAL void rConcSMS_PlusCMGW ( UBYTE index, UBYTE numSeg )
{
  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,
                                  NULL,
                                  concShrdPrm.udh.max_num,
                                  concShrdPrm.udh.seq_num,
                                  index,
                                  prm->stat);

  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(concShrdPrm.srcId, CMGW_IDX_FREE_ENTRY, prm->p_da,
                    prm->p_toda, 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);
    R_AT ( RAT_OK, 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;
    
    sAT_PlusCMGD_Gl(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;
    }
    TRACE_EVENT("RAT_OK in rConcSMS_PlusCMGD");
    R_AT ( RAT_OK,   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(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, 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

  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();

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





/*************** 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 )
{
  TRACE_FUNCTION ("SMS_getSMSType()");

  if (udh->len EQ 0)
    return NORMAL;

  if (udh->data[0] EQ SMS_IEI_CONC_8BIT)
    return CONCATE;

  if (udh->data[0] EQ SMS_IEI_CONC_16BIT)
    return CONCATE;

  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 UBYTE concSMS_GetFirstIndex ( 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( 0 );
  }

  /* 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( 0 );
  }

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

  /* return index of first segment */
  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 )
{
  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;
 

  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)
  {
    ret = concSMS_addToConcatList(ref_num, address, max_num,
                                  seq_num, rec_num, status);

    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;
      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;
      }

      /* 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;
      }

      /* 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;
      }

      /* 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    = 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;
#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)
      {
        if (count EQ 0)
        {
          /* last element of one conc buffer was deleted */
          concBuf_list[i].in_use = FALSE;
          R_AT ( RAT_OK, 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 */
      sAT_PlusCMGD_Gl(concShrdPrm.srcId, concBufElem->rec_num, smsShrdPrm.status,
                      concSMS_delAllIncompleteMsg, rConcSMS_PlusCMS_CMGD);

      /* free memory */
      ACI_MFREE(concBufElem);
    }
  }
}



/************** 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_P3("   seq_num: %d , rec_num: %d , status: %d",
                       elem->seq_num, elem->rec_num, elem->status);
        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);
    }
  }
}



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],1);
  }
}

/*
+-------------------------------------------------------------------+
| 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;
  }
}
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(concShrdPrm.srcId, elem->rec_num,
                    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,   concShrdPrm.srcId ) ( AT_CMD_P_CMGR );
    UNSET_CONC;
  }
}