/*
+-----------------------------------------------------------------------------
|  Project :  GSM-PS
|  Modul   :  ALR_CBCH
+-----------------------------------------------------------------------------
|  Copyright 2002 Texas Instruments Berlin, AG
|                 All rights reserved.
|
|                 This file is confidential and a trade secret of Texas
|                 Instruments Berlin, AG
|                 The receipt of or possession of this file does not convey
|                 any rights to reproduce or disclose its contents or to
|                 manufacture, use, or sell anything it may describe, in
|                 whole, or in part, without the specific written consent of
|                 Texas Instruments Berlin, AG.
+-----------------------------------------------------------------------------
|  Purpose :  This Modul defines the SDL process CBCH_control.
+-----------------------------------------------------------------------------
*/

#ifndef ALR_CBCH_C
#define ALR_CBCH_C

#define ENTITY_PL

/*==== INCLUDES ===================================================*/
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "typedefs.h"
#include "pconst.cdg"
#include "mconst.cdg"
#include "message.h"
#include "ccdapi.h"
#include "vsi.h"
#include "custom.h"
#include "gsm.h"
#include "prim.h"
#include "cnf_alr.h"
#include "mon_alr.h"
#include "pei.h"
#include "tok.h"

#include "pcm.h"
#ifdef GPRS
#include "alr_gprs.h"
#endif
#include "alr.h"
#include "alr_em.h"

/*==== EXPORT =====================================================*/
/*==== PRIVAT =====================================================*/
LOCAL void   cb_read_cbch               (UBYTE  flags);
LOCAL void   cb_check_ci_lai_plmn       (void);
LOCAL void   cb_remove_message          (USHORT i);
LOCAL void   cb_first_block             (UBYTE  status);
LOCAL UBYTE* cb_create_schedule_entries (UBYTE* msg_ptr, BOOL read_old_msg);
LOCAL void   cb_create_schedule_map     (UBYTE  old_cb_msg);
LOCAL UBYTE  cb_check_old_msg           (void);
LOCAL UBYTE  cb_check_data_coding_scheme(UBYTE  dcs_id);
LOCAL UBYTE  cb_check_message_id        (USHORT msg_id);
LOCAL void   cb_store_cbch_msg          (void);
LOCAL void   cb_error_ind               (void);
LOCAL void   cb_cbch_message            (UBYTE  length);
LOCAL void   cb_null_message            (void);
LOCAL void   cb_schedule_message        (void);
LOCAL void   cb_next_scheduled_msg      (void);
LOCAL UBYTE  cb_sat_check_message_id    (USHORT msg_id);
LOCAL void   cb_sat_cbch_message        (UBYTE length);
LOCAL void cb_stop_next_blocks (void);
LOCAL void cb_read_next_3_blocks(void);
void set_cbch_state( UBYTE extended, UBYTE new_state);
UBYTE get_cbch_state( UBYTE extended);

#if defined(FF_HOMEZONE)
LOCAL UBYTE cb_check_homezone (UBYTE length);
#endif  /* FF_HOMEZONE */

/*
 * support indication of geographical scope change to MMI
 * it is important that these defitions match the GS definition from GSM 03.41
 * the MMI uses this also to compare with real CBCH messages!
 */
#define CB_GEOGR_CHANGE_NONE 0x00
#define CB_GEOGR_CHANGE_CELL 0xC0
#define CB_GEOGR_CHANGE_LAC  0x80
#define CB_GEOGR_CHANGE_PLMN 0x40

#define CB_GEOGR_SCOPE_CELLI 0x00
#define CB_GEOGR_SCOPE_PLMN  0x01
#define CB_GEOGR_SCOPE_LAC   0x02
#define CB_GEOGR_SCOPE_CELLN 0x03

#define CBCH_NO_MSG           0
#define CBCH_NORM_MSG         1
#define CBCH_SCHEDULE_MSG     2
#define CBCH_SAT_MSG          3
#define CBCH_LAST_BLOCK    0x10

#define CBCH_BLOCK_0          0
#define CBCH_BLOCK_1          1
#define CBCH_BLOCK_2          2
#define CBCH_BLOCK_3          3
#define CBCH_FIRST_SCHED_MSG  8
#define CBCH_NULL_MSG      0x0F
#define CBCH_SCHED            2

#define MAX_UPDATE_NO   16
#define MAX_UPDATE_NO_DIFF    8

#if 1 || defined (WIN32)
#define TRACING
#endif

#define CB_SERIAL_NO(ph)            ((USHORT)\
                                    ((((T_CBMSG_HEADER*)(ph))->serial_no1)<<8)+\
                                    (((T_CBMSG_HEADER*)(ph))->serial_no2))

#define CB_GS_CODE(ph)              (((UBYTE)(((T_CBMSG_HEADER*)(ph))->serial_no1))>>6)

#define CB_MSG_CODE(ph)             ((USHORT)\
                                    (((((T_CBMSG_HEADER*)(ph))->serial_no1)&0x3f)<<4)+\
                                    ((((T_CBMSG_HEADER*)(ph))->serial_no2)>>4))

#define CB_UPDATE_NO(ph)            ((((T_CBMSG_HEADER*)(ph))->serial_no2)&0x0f)

#define CB_MESSAGE_ID(ph)           ((USHORT)\
                                    ((((T_CBMSG_HEADER*)(ph))->msg_id3)<<8)+\
                                    (((T_CBMSG_HEADER*)(ph))->msg_id4))
#if 0
#define CB_MESSAGE_ID(ph)           ((((USHORT)(((T_CBMSG_HEADER*)(ph))->msg_id3))<<8)+\
                                      ((USHORT)(((T_CBMSG_HEADER*)(ph))->msg_id4)))

#endif /* 0 */
#define CB_DCS(ph)                  (((T_CBMSG_HEADER*)(ph))->dcs)

#define CB_PAGE(ph)                 (((T_CBMSG_HEADER*)(ph))->page>>4)
#define CB_PAGETOTAL(ph)            (((T_CBMSG_HEADER*)(ph))->page&0x0f)


#if defined (TRACING)
#define ALR_TRACE_CB(a)             TRACE_EVENT("CBCH: " a)
#define ALR_TRACE_CB_MODUS(m)       TRACE_EVENT_P1("CBCH: modus=%d", m)
#define ALR_TRACE_CB_ERROR(e)       TRACE_EVENT_P1("CBCH: error=%d", e)
#define ALR_TRACE_CB_BLOCK_NO(b)    TRACE_EVENT_P2("CBCH: block=%02x no=%u", b, b&0x0f)
#define ALR_TRACE_CB_MSGHEAD(ph)    if (CB_MSG_CODE(ph) < 1000) {\
                                      TRACE_EVENT_P7("CBCH ID=%3u, gs=%u, msg=%4u, upd=%u, dcs=%02x, p=%u/%u",\
                                        CB_MESSAGE_ID(ph), CB_GS_CODE(ph),  \
                                        CB_MSG_CODE(ph), CB_UPDATE_NO(ph), \
                                        CB_DCS(ph), CB_PAGE(ph), CB_PAGETOTAL(ph));\
                                    }else{\
                                      TRACE_EVENT_P7("CBCH ID=%3u, gs=%u, msg=%04x, upd=%u, dcs=%02x, p=%u/%u",\
                                        CB_MESSAGE_ID(ph), CB_GS_CODE(ph),  \
                                        CB_MSG_CODE(ph), CB_UPDATE_NO(ph), \
                                        CB_DCS(ph), CB_PAGE(ph), CB_PAGETOTAL(ph));\
                                    }

#define ALR_TRACE_CB_MSGID(m)         TRACE_EVENT_P1("CBCH: ID=%u", m)
#define ALR_TRACE_CB_START_READ(f)    TRACE_EVENT_P1("CBCH: Start Read %d", f)
#define ALR_TRACE_CB_SCHEDULE(a1,a2)  TRACE_EVENT_P2("CBCH: schedule i=%d result=%d", a1,a2)
#define ALR_TRACE_CB_HZREQ(a1,a2)     TRACE_EVENT_P2("CBCH: Homezone ID=%u, t=%u", a1,a2)
#define ALR_TRACE_CB_SCOPE_CHANGE(i,s)  TRACE_EVENT_P2 ("CBCH: Geogr. scope change %u, deleting msg of scope %u",\
                                          i,s);
#else  /* TRACING */
#define ALR_TRACE_CB(a)
#define ALR_TRACE_CB_MODUS(m)
#define ALR_TRACE_CB_ERROR(e)
#define ALR_TRACE_CB_BLOCK_NO(b)
#define ALR_TRACE_CB_MSGID(m)
#define ALR_TRACE_CB_START_READ(f)
#define ALR_TRACE_CB_SCHEDULE(a1,a2)
#define ALR_TRACE_CB_HZREQ(a1,a2)
#endif  /* TRACING */

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

#if 0
#if 0
#define TRACE_ALIGN_P(ps,m,t)  TRACE_EVENT_P5(" %08x %3u.%u %08x=%u " \
                                "T_" #t " " #m, \
                                (BYTE*)&(m), (BYTE*)&(m)-(BYTE*)ps, \
                                sizeof (m),
                                sizeof (m) > 1 ? (USHORT)(m) : (UBYTE)(m), m \
                              )
#else
#define TRACE_ALIGN_P(ps,m,t)  TRACE_EVENT_P4 (" %08x %3u.%u %08x " \
                                "T_" #t " " #m, \
                                (BYTE*)&(m), (BYTE*)&(m)-(BYTE*)ps, \
                                sizeof (m), m\
                                )
#endif
int test_alignment (T_CBMSG_HEADER *ph)
{
  TRACE_ALIGN_P (ph, ph->serial_no1, CBMSG_HEADER);
  TRACE_ALIGN_P (ph, ph->serial_no2, CBMSG_HEADER);
  TRACE_ALIGN_P (ph, ph->msg_id3, CBMSG_HEADER);
  TRACE_ALIGN_P (ph, ph->msg_id4, CBMSG_HEADER);
  TRACE_ALIGN_P (ph, ph->dcs, CBMSG_HEADER);
  TRACE_ALIGN_P (ph, ph->page, CBMSG_HEADER);
}
#endif

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_init                    |
+--------------------------------------------------------------------+

  PURPOSE : Initialize CBCH Control Process.
*/

GLOBAL void cb_init (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  ALR_TRACE_CB ("cb_init()");
  alr_data->state[STATE_NORMAL_CBCH] = CBCH_NULL;
  alr_data->state[STATE_EXT_CBCH] = CBCH_NULL;
  pcbch->modus   = NOT_PRESENT_8BIT;
  pcbch->old_cbch_msg.cnt = 0;
#if defined(FF_HOMEZONE)
  pcbch->homezone = CBCH_HZ_OFF;
  pcbch->hz_timeout = 0;
#endif  /* FF_HOMEZONE */
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_stop                    |
+--------------------------------------------------------------------+

  PURPOSE : Stop CBCH Control Process.
*/

GLOBAL void cb_stop (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE normal_cbch, extended_cbch;

  normal_cbch = GET_STATE(STATE_NORMAL_CBCH);
  extended_cbch = GET_STATE(STATE_EXT_CBCH);

#if defined(FF_HOMEZONE)
  if (pcbch->homezone EQ CBCH_HZ_PENDING)
    return; /* donīt stop if homezone request is pending */
#endif  /* FF_HOMEZONE */

  if ( 
        (normal_cbch NEQ CBCH_NULL AND normal_cbch NEQ CBCH_IDLE) 
        OR
        (extended_cbch NEQ CBCH_NULL AND extended_cbch NEQ CBCH_IDLE) 
     )
  {
    /*
     * if CBCH process is really active, initialize
     * CBCH process and stop CBCH process in layer 1.
     */
    pcbch->msg_type = CBCH_NO_MSG;

    SET_STATE(STATE_NORMAL_CBCH, CBCH_IDLE);
    SET_STATE(STATE_EXT_CBCH, CBCH_IDLE);

    ALR_EM_STOP_CBCH_READING;

    ma_cb_stop_cbch_req();
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_start                   |
+--------------------------------------------------------------------+

  PURPOSE : Re-configure layer 1 and go back to CBCH reading without
            scheduling information.
*/

LOCAL void cb_start_no_drx_reading (UBYTE config)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  if (config)
  {
    /*
     * configure layer 1
     */
    PALLOC (config_cbch, MPHC_CONFIG_CBCH_REQ);

    /*
     * copy the stored configuration received earlier from RR and send to layer 1.
     */
    memcpy (config_cbch, &pcbch->mphc_config_cbch, sizeof (T_MPHC_CONFIG_CBCH_REQ));
    ma_cb_config_cbch(config_cbch);
  }

  /*
   * start reading in layer 1 for normal and extended CBCH.
   * The initial mode is no Scheduling information available.
   */
  cb_read_cbch (CBCH_NO_SCHED | CBCH_READ_NORM);
  cb_read_cbch (CBCH_NO_SCHED  | CBCH_READ_EXT);

  SET_STATE(STATE_NORMAL_CBCH, CBCH_NO_DRX);
  SET_STATE(STATE_EXT_CBCH, CBCH_NO_DRX);
  
#if defined(FF_HOMEZONE)
  if (pcbch->homezone EQ CBCH_HZ_IDLE)
  {
    pcbch->homezone = CBCH_HZ_PENDING;
    /* start timer new */
    if (pcbch->hz_timeout)
      TIMERSTART(TIM_HOMEZONE, pcbch->hz_timeout);
    ALR_TRACE_CB ("HOMEZONE is pending");
  }
#endif /* FF_HOMEZONE */
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_start                   |
+--------------------------------------------------------------------+

  PURPOSE : Start CBCH Control Process.
*/

GLOBAL void cb_start (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  if (    (GET_STATE(STATE_NORMAL_CBCH) EQ CBCH_IDLE)
      AND (     (pcbch->modus NEQ NOT_PRESENT_8BIT)
            OR  (pcbch->sat_enabled)
#if defined(FF_HOMEZONE)
            OR  (pcbch->homezone EQ CBCH_HZ_IDLE)
#endif /* FF_HOMEZONE */
          )
      )
  {
    /*
     * if a definition of needed message identifiers
     * and data coding schemes is available, then configure
     * layer 1
     */
    ALR_TRACE_CB ("restart");
    cb_start_no_drx_reading (TRUE);
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : TIL_CBCH                   |
| STATE   : code                ROUTINE : cb_mph_cbch_req            |
+--------------------------------------------------------------------+

  PURPOSE : Configuration of the layer 1 process for CBCH.

            The compile-switch is not defined with the standard TI-layer 1.

            But there shall be a little modification possible (and it is
            done by some source code customer of layer 1), which does not
            need any storing in ALR. Then the compile switch must be set

  REMARK  : Setting of compile switch has problems with function cbch_start ???

*/

GLOBAL void cb_mph_cbch_req  (T_cbch * cbch)
{
  USHORT i;
  T_CBCH_DATA      *pcbch = &alr_data->cbch_data;
  T_cbch_desc      *pcbch_desc = &pcbch->mphc_config_cbch.cbch_desc;
  T_chan_sel       *pchan_sel = &pcbch_desc->chan_sel;
  T_cbch_freq_list *pcbch_freq_list = &pcbch->mphc_config_cbch.cbch_freq_list;

  ALR_TRACE_CB ("MPH_CBCH_REQ");

  ALR_EM_CONFIGURE_CBCH_CHANNEL;

#if defined(FF_HOMEZONE)
  if (pcbch->homezone NEQ CBCH_HZ_OFF)
    pcbch->homezone = CBCH_HZ_IDLE; /* starts HZ request again */
#endif /* FF_HOMEZONE */

  /*   check which messages have to be deleted
   *   even if the new cell has CBCH disabled! We still need to
   *   to delete the corresponding messages and inform the MMI!
   */
  if (pcbch->modus EQ CBCH_ACCEPT)
    cb_check_ci_lai_plmn ();

  if (cbch->stat EQ STAT_ACT)
  {
    /*
     * A CBCH channel is available
     */

#if !defined (L1_STORES_CBCH_CONFIG)
    /*
     * Layer 1 does not store any CBCH configuration (default)
     * first set the channel configuration
     */
    if (cbch->h EQ H_FREQ)
    {
      /*
       * with frequency hopping, set hopping flag, maio, hsn and hopping list.
       */
      pchan_sel->h = HOPPING;
      pchan_sel->rf_channel.maio = cbch->maio;
      pchan_sel->rf_channel.hsn  = cbch->hsn;

      i=0;
      pcbch_freq_list->rf_chan_cnt=0;
      while ((i < 64) AND (cbch->ma[i] NEQ NOT_PRESENT_16BIT))
      {
        pcbch_freq_list->rf_chan_no.radio_freq[i] = ARFCN_TO_L1(cbch->ma[i]);
        i++;
        pcbch_freq_list->rf_chan_cnt++;
      }
    }
    else
    {
      /*
       * Layer 1 uses a union definition. This is not applicable for the Condat-SAP model.
       * Therefore the rf channel number is configured by maio and hsn. In general a Avoid the union !!!
       */
      pchan_sel->h = NO_HOPPING;
      pchan_sel->rf_channel.maio = (UBYTE)(ARFCN_TO_L1(cbch->arfcn) & 0xFF);
      pchan_sel->rf_channel.hsn  = (UBYTE)((ARFCN_TO_L1(cbch->arfcn) >> 8) & 0xFF);
      memset (pcbch_freq_list, 0, sizeof (T_cbch_freq_list));
    }

    /*
     * configure the CBCH channel type (SDCCH/8 or SDCCH/4)
     */
    if (cbch->ch < CH_SDCCH_8_0)
      pcbch_desc->channel_type = CH_SDCCH_4;
    else
      pcbch_desc->channel_type = CH_SDCCH_8;

    pcbch_desc->sub_channel   = 0;    /* not relevant */
    pcbch_desc->timeslot_no  = cbch->tn;
    pcbch_desc->tsc          = cbch->tsc;

#else
    /*
     * Compile option set: L1 stores actual CBCH configuration.
     * So it is not necessary to store the configuration in ALR.
     * Just parse the configuration to Layer 1.
     */
    {
      PALLOC (config_cbch, MPHC_CONFIG_CBCH);

      if (cbch->h EQ H_FREQ)
      {
        /*
         * with frequency hopping, set hopping flag, maio, hsn and hopping list.
         */
        config_cbch->cbch_desc.chan_sel.h = HOPPING;
        config_cbch->cbch_desc.chan_sel.rf_channel.maio = cbch->maio;
        config_cbch->cbch_desc.chan_sel.rf_channel.hsn  = cbch->hsn;

        i=0;
        config_cbch->cbch_freq_list.rf_chan_cnt=0;
        while (cbch->ma[i] NEQ NOT_PRESENT_16BIT AND i < 64)
        {
          config_cbch->cbch_freq_list.rf_chan_no.hop_chan[i] = ARFCN_TO_L1(cbch->ma[i]);
          i++; config_cbch->cbch_freq_list.rf_chan_cnt++;
        }
      }
      else
      {
        /*
         * Layer 1 uses a union definition. This is not applicable for the Condat-SAP model.
         * Therefore the rf channel number is configured by maio and hsn. In general a Avoid the union !!!
         */
        config_cbch->cbch_desc.chan_sel.h = NO_HOPPING;
        config_cbch->cbch_desc.chan_sel.rf_channel.maio = ARFCN_TO_L1(cbch->arfcn) & 0xFF;
        config_cbch->cbch_desc.chan_sel.rf_channel.hsn  = (ARFCN_TO_L1(cbch->arfcn) >> 8) & 0xFF;
        memset (&config_cbch->cbch_freq_list, 0, sizeof (T_cbch_freq_list));
      }

      /*
       * configure the CBCH channel type (SDCCH/8 or SDCCH/4)
       */
      if (cbch->ch < CH_SDCCH_8_0)
        config_cbch->cbch_desc.channel_type = CH_SDCCH_4;
      else
        config_cbch->cbch_desc.channel_type = CH_SDCCH_8;

      config_cbch->cbch_desc.sub_channel    = 0;    /* not relevant */
      config_cbch->cbch_desc.timeslot_no    = cbch->tn;
      config_cbch->cbch_desc.tsc            = cbch->tsc;

      ma_cb_config_cbch(config_cbch);
    }
#endif

    if (    (pcbch->modus NEQ NOT_PRESENT_8BIT)
         OR (pcbch->sat_enabled)
#if defined(FF_HOMEZONE)
         OR (pcbch->homezone EQ CBCH_HZ_IDLE)
#endif /* FF_HOMEZONE */
       )
    {
      /*
       * MMI has requested data
       * Start with NO_DRX mode
       * Read each first CBCH block
       */
#if defined (L1_STORES_CBCH_CONFIG)
      cb_start_no_drx_reading (FALSE);
#else  /* L1_STORES_CBCH_CONFIG */
      ALR_TRACE_CB ("switch on, send config");
      cb_start_no_drx_reading (TRUE);
#endif  /* L1_STORES_CBCH_CONFIG */
    }
    else
    {
      UBYTE   *si3 = &alr_data->ma_data.sys_info_3[0];
      UBYTE   *lac = &alr_data->cbch_data.old_cid_plmn_lac[0];

      ALR_TRACE_CB ("switch on, wait for MMI");
      /*
       * wait for MMI request
       */
      SET_STATE(STATE_NORMAL_CBCH, CBCH_IDLE);
      SET_STATE(STATE_EXT_CBCH, CBCH_IDLE);
      /*
       * update old_cid_plmn_lac
       */
      memcpy (lac, &si3[2], 7);
    }
  }
  else
  {
    /*
     * The new configuration indicates no CBCH, so stop the CBCH process in Layer 1
     */
    ALR_TRACE_CB ("switch off");

    cb_stop();
    SET_STATE(STATE_NORMAL_CBCH, CBCH_NULL);
    SET_STATE(STATE_EXT_CBCH, CBCH_NULL);
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : AKR_CBCH                   |
| STATE   : code                ROUTINE : cb_mmi_cbch_req            |
+--------------------------------------------------------------------+

  PURPOSE : MMI sends a definition of the expected message identifier
            and data coding schemes for the CBCH messages.

*/

GLOBAL void cb_mmi_cbch_req (T_MMI_CBCH_REQ *cbch)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  ALR_TRACE_CB ("MMI req");

#if defined(FF_HOMEZONE)
  if (cbch->modus NEQ CBCH_HOMEZONE)
  {
    pcbch->modus = cbch->modus;
    TRACE_EVENT_P1 ("CBCH: homezone=%u", pcbch->homezone);
  }
  else if (cbch->msg_id[0] EQ NOT_PRESENT_16BIT)
  {
    /*
     * switch off automatically homezone request
     * - homezone state to off
     * - clear old message
     * - stop timer
     */
     pcbch->msg_hz_id = NOT_PRESENT_16BIT;
     pcbch->homezone = CBCH_HZ_OFF;
     TIMERSTOP (TIM_HOMEZONE);
  }
  else
  {
    /*
     * switch on automatically homezone request
     * - set homezone state to idle whatever are before
     * - set new homezone id
     * - clear old message
     */
    pcbch->msg_hz_id = cbch->msg_id[0];
    pcbch->dcs_hz_id = cbch->dcs_id[0];
    pcbch->hz_timeout = cbch->dcs_id[1]*1000;
    ALR_TRACE_CB_HZREQ (pcbch->msg_hz_id,
      pcbch->hz_timeout);
    if (pcbch->homezone EQ CBCH_HZ_OFF)
    {
      UBYTE cbch_state = GET_STATE (STATE_NORMAL_CBCH);
      pcbch->homezone = CBCH_HZ_IDLE;
      if (cbch_state NEQ CBCH_NULL)
      {/* CBCH system is prepared */
        if (cbch_state EQ CBCH_IDLE)
        { /* start timer only if CBCH system is prepared */
          TIMERSTART (TIM_HOMEZONE, 1000);
        }
        else
        { /* CBCH system is already active -> wait for HZ message */
          pcbch->homezone = CBCH_HZ_PENDING;
          TIMERSTART (TIM_HOMEZONE, pcbch->hz_timeout);
          ALR_TRACE_CB ("HOMEZONE is pending");
        }
      }
    }
    return;
  }
#else  /* FF_HOMEZONE */
  pcbch->modus = cbch->modus;
#endif  /* FF_HOMEZONE */
  /*
   * Storing of Parameter and clearing of
   * the received CB memory
   */
  memcpy (&pcbch->msg_id[0], cbch->msg_id, sizeof (pcbch->msg_id));
  memcpy (&pcbch->dcs_id[0], cbch->dcs_id, sizeof (pcbch->dcs_id));

#if !defined(NTRACE)
{
  int i;

  ALR_TRACE_CB_MODUS(cbch->modus);

  for (i=0;
    (i < MAX_IDENTS) AND (pcbch->msg_id[i] NEQ NOT_PRESENT_16BIT);
    i++)
  {
    TRACE_EVENT_P2 ("CBCH: id[%u]=%u", i, pcbch->msg_id[i]);
  }
  for (i=0;
    (i < MAX_IDENTS) AND (pcbch->dcs_id[i] NEQ NOT_PRESENT_8BIT);
    i++)
  {
    TRACE_EVENT_P2 ("CBCH:dcs[%u]=%u", i, pcbch->dcs_id[i]);
  }
}
#endif /* !NTRACE */

  /* old messages should only be erased when MMI turns CBCH off */
  if (pcbch->modus EQ MMI_CBCH_STOP)
  {
    /* also erases the count of old message */
    memset (&pcbch->old_cbch_msg, 0, sizeof (T_CB_MSG));
    memset (&pcbch->msg[0], 0, CBCH_MSG_SIZE);
  }

  /*
    TRACE_EVENT_P2 ("CBCH: STATE_MA=%u STATE_CBCH=%u", GET_STATE(STATE_MA), GET_STATE (STATE_CBCH));
  */

#ifdef GPRS
  if ((GET_STATE(STATE_MA) EQ MA_IDLE AND !alr_data->gprs_data.pbcch) OR
      (alr_data->gprs_data.pbcch AND alr_data->gprs_data.pim))
#else
  if (GET_STATE(STATE_MA) EQ MA_IDLE)
#endif
  {
    /*
     * layer 1 is only configured, if ALR is in idle state, else the configuration
     * is delayed until the cell is selected.
     */
    switch (GET_STATE (STATE_NORMAL_CBCH))
    {
      case CBCH_NULL:
        /*
         * no CBCH channel is available
         */
        break;

      case CBCH_IDLE:
        if (pcbch->modus EQ MMI_CBCH_STOP AND
            !pcbch->sat_enabled)
        {
          /*
           * CBCH download is not enabled and the MMI indicates stop of
           * the CBCH channel, then stop CBCH process in layer 1.
           */
          cb_stop();
        }
        else
        {
          /*
           * new definitions of message identifier or data coding schemes
           * then re-configure layer 1 and go back to CBCH reading without
           * scheduling information.
           */
          cb_start_no_drx_reading (TRUE);
        }
        break;

      default:
        /*
         * we already received a MMI CBCH REQ before
         */
        if (pcbch->modus EQ MMI_CBCH_STOP AND
            !pcbch->sat_enabled)
        {
          /*
           * CBCH download is not enabled and the MMI indicates stop of
           * the CBCH channel, then stop CBCH process in layer 1.
           */
          cb_stop();
          SET_STATE(STATE_NORMAL_CBCH, CBCH_IDLE);
          SET_STATE(STATE_EXT_CBCH, CBCH_IDLE);
        }
        break;
    }
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : TIL_CBCH                   |
| STATE   : code                ROUTINE : cb_mmi_sat_cbch_req        |
+--------------------------------------------------------------------+

  PURPOSE : Reception of a list of identifiers for SIM TOOLKIT
            CBCH Download.

*/

GLOBAL void cb_mmi_sat_cbch_req (T_MMI_SAT_CBCH_DWNLD_REQ * cbch_req)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  ALR_TRACE_CB ("MMI SAT req");

  /*
   * clear the list of needed message identifier for CBCH download
   * and fill it with the definiton of MMI.
   */
   memset (pcbch->msg_id_sat, NOT_PRESENT_16BIT,
           sizeof(pcbch->msg_id_sat));
   memcpy (pcbch->msg_id_sat, cbch_req->msg_id,
           sizeof(cbch_req->msg_id[0])*(cbch_req->count));

   /*
    * CBCH download is enabled
    */
   pcbch->sat_enabled = 1;

   switch (GET_STATE (STATE_NORMAL_CBCH))
   {
     case CBCH_NULL:
       /*
        * No CBCH channel is available.
        */
       break;

     default:
       {
         /*
          * configure layer 1 for catching the CBCH download messages
          */
         PALLOC (config_cbch, MPHC_CONFIG_CBCH_REQ);
         memcpy (config_cbch, &pcbch->mphc_config_cbch, sizeof (T_MPHC_CONFIG_CBCH_REQ));
         ma_cb_config_cbch(config_cbch);
         cb_read_cbch (CBCH_NO_SCHED | CBCH_READ_NORM);
         cb_read_cbch (CBCH_NO_SCHED | CBCH_READ_EXT);
         SET_STATE(STATE_NORMAL_CBCH, CBCH_NO_DRX);
         SET_STATE(STATE_EXT_CBCH, CBCH_NO_DRX);         
       }
       break;
   }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : TIL_CBCH                   |
| STATE   : code                ROUTINE : cb_data_ind                |
+--------------------------------------------------------------------+

  PURPOSE : A CBCH message is received.

*/

GLOBAL void cb_data_ind (T_MPHC_DATA_IND  *cbch_ind)
{
  UBYTE block_no;
  UBYTE block_description;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE * expected_seq_number  = &(pcbch->expected_seq_number[0]);

  /*
   * check whether the CBCH message has been received on the
   * normal CBCH channel or the extended CBCH channel
   */
  if (cbch_ind->tc >= 4)
  {
    pcbch->msg_is_extended = CBCH_READ_EXT;
    expected_seq_number  = &(pcbch->expected_seq_number[1]);
  }
  else if(cbch_ind->tc < 4)
    pcbch->msg_is_extended = CBCH_READ_NORM;

  if (cbch_ind->error_flag EQ VALID_BLOCK)
  {
    /*
     * if the CBCH block is valid, calculate the block number
     */
    block_description = cbch_ind->l2_frame.content[0];
    block_no = (UBYTE)(block_description & 0x0F);
    if (block_no < CBCH_NULL_MSG)
    {
      ALR_TRACE_CB_BLOCK_NO (block_description);
    }
    TRACE_EVENT_P2("expected_block_number = %d, schedule_length =%d", (*expected_seq_number), pcbch->schedule_length[0]);
    switch(block_no)
    {
      case CBCH_BLOCK_0:
        memcpy (&pcbch->msg[0], &cbch_ind->l2_frame.content[1], CBCH_BLOCK_SIZE);
#if defined(FF_HOMEZONE)
        if (!cb_check_homezone ((UBYTE)(CBCH_BLOCK_SIZE*(block_no+1))))
#endif  /* FF_HOMEZONE */
          cb_first_block(block_description);
        break;

      case CBCH_BLOCK_1:
      case CBCH_BLOCK_2:
      case CBCH_BLOCK_3:
       /*
         * check for sequence number 
         */
       if(*expected_seq_number NEQ block_no)
       {
         ALR_TRACE_CB("Sequence error");
         cb_error_ind();        
       }
       else
       {
         memcpy (&pcbch->msg[CBCH_BLOCK_SIZE*(block_no & 3)],
          &cbch_ind->l2_frame.content[1], CBCH_BLOCK_SIZE);

         (*expected_seq_number)++;
         if( *expected_seq_number > CBCH_BLOCK_3 )
           *expected_seq_number = CBCH_BLOCK_0;
         
         
         if ( (block_description & CBCH_LAST_BLOCK)  OR  (block_no EQ CBCH_BLOCK_3)  )
         {
           /*
             * last block of msg
             */
           if(pcbch->msg_type EQ CBCH_NORM_MSG)
             /*
               * +1, because parameter is length not offset
               */
             cb_cbch_message((UBYTE)(CBCH_BLOCK_SIZE*(block_no+1)));
           else if(pcbch->msg_type EQ CBCH_SCHEDULE_MSG)
             cb_schedule_message();
           else if(pcbch->msg_type EQ CBCH_SAT_MSG)
             cb_sat_cbch_message((UBYTE)(CBCH_BLOCK_SIZE*(block_no+1)));
         }
       }
       break;

      case CBCH_FIRST_SCHED_MSG:
        /*
         * first SCHEDULE block
         */

        ALR_TRACE_CB ("SCHEDULE");
        memcpy (&pcbch->msg[0],  &cbch_ind->l2_frame.content[1], CBCH_BLOCK_SIZE);
        cb_read_next_3_blocks();

        pcbch->msg_type = CBCH_SCHEDULE_MSG;
        break;

      case CBCH_NULL_MSG:
        /*
         * NULL Message
         */

        /*
        ALR_TRACE_CB ("null msg");
        */
        cb_null_message ();
        break;

      default:
        ALR_TRACE_CB ("unknown");
        cb_error_ind();
        break;
    }

    ALR_EM_RECEIVE_CBCH_MESSAGE;

  }
  else
  {
    /*
     * Invalid block
     */
    /*
    ALR_TRACE_CB_ERROR(cbch_ind->error_flag);
    */

    pcbch->msg_type = CBCH_NO_MSG;
    cb_error_ind ();
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_first_block             |
+--------------------------------------------------------------------+

  PURPOSE : Reception of the first CBCH block.

*/

LOCAL void cb_first_block (UBYTE status)
{
  UBYTE  result;
  T_CBMSG_HEADER  *p_cbh;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  /*
   * extract message id and data coding scheme from the message
   */
  p_cbh = (T_CBMSG_HEADER*)(&pcbch->msg[0]);

  ALR_TRACE_CB_MSGHEAD (p_cbh);

  /*
   * check the incoming message:
   * 1. matches the message identifier
   * 2. matches the data coding scheme
   * 3. is this message not received yet
   */
  result =  cb_check_message_id (CB_MESSAGE_ID(p_cbh));
  result += cb_check_data_coding_scheme (CB_DCS(p_cbh));
  result += cb_check_old_msg ();

  if (result EQ 3)
  {
    /*
     * all checks are passed
     */
    if (! pcbch->sat_enabled OR
        ! cb_sat_check_message_id(CB_MESSAGE_ID(p_cbh)))
    {
      /*
       * it is not a CBCH download message or
       * CBCH download is not enabled
       */
      if (status & CBCH_LAST_BLOCK)
      {
        /*
         * the first is the only block, then forward it
         * to MMI.
         */
        cb_cbch_message (CBCH_BLOCK_SIZE*(CBCH_BLOCK_0+1));
      }
      else
      {
        /*
         * store the block and request the three remaining
         * blocks in layer 1.
         */
        pcbch->msg_type = CBCH_NORM_MSG;
        cb_read_next_3_blocks();
      }
    }
    else
    {
      /*
       * It is a CBCH download message and CBCH download is enabled.
       */
      if (status & CBCH_LAST_BLOCK)
      {
        /*
         * the first block is the only block, so forward the
         * message to MMI.
         */
        cb_sat_cbch_message (CBCH_BLOCK_SIZE*(CBCH_BLOCK_0+1));
      }
      else
      {
        /*
         * store the data and request the three remaining blocks.
         */
        pcbch->msg_type = CBCH_SAT_MSG;
        cb_read_next_3_blocks();
      }
    }
  }
  else
  {
    /*
     * the message has not passed the checks.
     * So it can be only a CBCH download message.
     */
    if(! pcbch->sat_enabled OR
       ! cb_sat_check_message_id(CB_MESSAGE_ID(p_cbh)))
    {
      /*
       * SAT is not enabled or it is not a CBCH download
       * message. So start reading of next scheduled message.
       */
      memset (&pcbch->msg[0], 0, 88);
      cb_next_scheduled_msg();

    }
    else
    {
      /*
       * it is CBCH download message and SAT is enabled
       */
      if (status & CBCH_LAST_BLOCK)
      {
        /*
         * the first block is the last one, so send the
         * message to MMI.
         */
        cb_sat_cbch_message (CBCH_BLOCK_SIZE*(CBCH_BLOCK_0+1));
      }
      else
      {
        /*
         * store the first block and request the remaining three.
         */
        pcbch->msg_type = CBCH_SAT_MSG;
        cb_read_next_3_blocks();
      }
    }
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : TIL_CBCH                   |
| STATE   : code                ROUTINE : cb_sat_cbch_message        |
+--------------------------------------------------------------------+

  PURPOSE : Reception of a complete CBCH message for CBCH download.

*/

LOCAL void cb_sat_cbch_message (UBYTE length)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  PALLOC (mmi_cbch_ind, MMI_SAT_CBCH_DWNLD_IND);

  ALR_EM_CBCH_MESSAGE_COMPLETE;

  TRACE_FUNCTION ("cb_sat_cbch_message()");

  /*
   * copy the content of the message into the primitive.
   */
  memcpy (mmi_cbch_ind->cbch_msg, pcbch->msg, 88);
  mmi_cbch_ind->cbch_len = length;

  /*
   * send to MMI
   */
  PSENDX(MMI, mmi_cbch_ind);

  /*
   * prepare for next reception
   */
  pcbch->msg_type = CBCH_NO_MSG;
  pcbch->expected_seq_number[0] = CBCH_BLOCK_0;
  pcbch->expected_seq_number[1] = CBCH_BLOCK_0;
  /*
   * start reading of next scheduled message.
   */
  cb_next_scheduled_msg();
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : TIL_CBCH                   |
| STATE   : code                ROUTINE : cb_schedule_message        |
+--------------------------------------------------------------------+

  PURPOSE : Reception of a SCHEDULE message.

*/

LOCAL void cb_schedule_message (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE extended = pcbch->msg_is_extended;
  UBYTE *p_sched_msg = &pcbch->scheduled_sched_msg[extended];

  ALR_EM_RECEIVE_SCHEDULE_MESSAGE;

  switch ( get_cbch_state(extended) )
  {
    case CBCH_NO_DRX:
      /*
       * change from NO DRX mode (no knowledge about Scheduling)
       * to DRX mode.
       */
      
      set_cbch_state (extended, CBCH_DRX);
      

      /*
       * check whether it is a scheduled schedule message or not.
       */
      if(pcbch->msg[0] EQ 1)
        *p_sched_msg = 1;
      else
        *p_sched_msg = 0;

      /*
       * create schedule map with new and old messages
       */
      cb_create_schedule_map (TRUE);

      /*
       * forward the schedule map to layer 1.
       */
      cb_read_cbch((UBYTE)(CBCH_SCHED | extended));

      break;

    case CBCH_DRX:
      if (pcbch->msg[0] EQ 1)
      {
        /*
         * scheduled SCHEDULE message
         */
         if(*p_sched_msg <= 2)
            (*p_sched_msg)++;

         if (*p_sched_msg > 1)
           /*
            * we have read a full schedule period
            * so we dont need to read the old messages
            */
           cb_create_schedule_map (FALSE);
         else
           cb_create_schedule_map (TRUE);

        cb_read_cbch((UBYTE)(CBCH_SCHED | extended));
      }      
      break;
    default:
      break;
  }
  /*
   * prepare for next reception
   */
  pcbch->msg_type = CBCH_NO_MSG;
  pcbch->expected_seq_number[0] = CBCH_BLOCK_0;
  pcbch->expected_seq_number[1] = CBCH_BLOCK_0;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_null_message            |
+--------------------------------------------------------------------+

  PURPOSE : Reception of a NULL message.

*/
LOCAL void cb_null_message (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE extended = pcbch->msg_is_extended;

  ALR_EM_RECEIVE_NULL_MESSAGE;

  switch ( get_cbch_state(extended) )
  {
    case CBCH_DRX:
      if (pcbch->schedule_length[extended] EQ 0)
      {
        /*
         * instead of the scheduled SCHEDULE message
         * a NULL message has received.
         * Restarting with NO DRX mode
         */
        set_cbch_state (extended, CBCH_NO_DRX);
        cb_read_cbch ((UBYTE)(CBCH_NO_SCHED | extended));
      }
      else
      {
        cb_next_scheduled_msg();
      }
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_error_ind               |
+--------------------------------------------------------------------+

  PURPOSE : Indicates an invalid block.
                   1) with error_flag =TRUE
                   2) invalid sequence number
                   3) out of sequence block
*/

LOCAL void cb_error_ind (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE extended = pcbch->msg_is_extended;
                     
  if( pcbch->expected_seq_number[extended] NEQ CBCH_BLOCK_0)
  {
    /*
      * stop the next blocks
      */
    cb_stop_next_blocks();      
  }
  switch ( get_cbch_state(extended) )
  {
    case CBCH_DRX:
      if (pcbch->schedule_length[extended] EQ 0)
      {
        /*
         * instead of the scheduled SCHEDULE message
         * an invalid message has been received.
         * Restarting with NO DRX mode
         */
        set_cbch_state (extended, CBCH_NO_DRX);
        cb_read_cbch ((UBYTE)(CBCH_NO_SCHED | extended));
      }
      else
        /*
         * Go back to FIRST DRX mode.
         */
      {
        pcbch->scheduled_sched_msg[extended] = 1;       
       cb_next_scheduled_msg();
      }
      break;
    default:
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_ind                     |
+--------------------------------------------------------------------+

  PURPOSE : Indication of the CBCH message.

*/

LOCAL void cb_ind (UBYTE length)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  PALLOC (mmi_cbch_ind, MMI_CBCH_IND);

  ALR_EM_CBCH_MESSAGE_COMPLETE;

  /*
   * copy the message content to the primitive.
   */
  memcpy (mmi_cbch_ind->cbch_msg, pcbch->msg, CBCH_MSG_SIZE);
  mmi_cbch_ind->cbch_len = length;

  ma_cb_mmi_cbch_ind(mmi_cbch_ind);

  {
    T_CBMSG_HEADER *p_cbh = (T_CBMSG_HEADER*)(&pcbch->msg[0]);
    ALR_TRACE_CB_MSGHEAD (p_cbh);
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_read_next_3_blocks         |
+--------------------------------------------------------------------+

  PURPOSE : Starts read of next 3 blocks.

*/

LOCAL void cb_read_next_3_blocks(void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE extended = pcbch->msg_is_extended;

  if( extended EQ CBCH_READ_EXT)
    ma_cb_info_req (CBCH_EXT_BLOCK234);
  else
    ma_cb_info_req (CBCH_NORM_BLOCK234);

  pcbch->expected_seq_number[extended] = CBCH_BLOCK_1;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_stop_next_blocks        |
+--------------------------------------------------------------------+

  PURPOSE : Stop next blocks.

*/

LOCAL void cb_stop_next_blocks (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  /*
   * stop the next blocks
   */

  /*
    * Upper layers / L1 Circuit Switched interfaces
    * S922.doc , Ver 1.9
    * Section 11.3 MPHC_CBCH_INFO_REQ 
    *
    * This message can be used to update on fly L1 
    * with new tb_bitmap which then replaces the current one. 
    * In such a case, 0 in the bitmap means abort any pending reading of TB1/2/3 or TB5/6/7.
    *
    * A previous "schedule_length=0" is used to specify "No schedule present".
    * In such a case, L1 starts reading continuously TB0 for normal CBCH or TB4 for Extended CBCH.
    */
  ma_cb_info_req (0);
  
  pcbch->msg_type = CBCH_NO_MSG;
  pcbch->expected_seq_number[0] = CBCH_BLOCK_0;
  pcbch->expected_seq_number[1] = CBCH_BLOCK_0;
}

#if defined(FF_HOMEZONE) && defined(INDICATE_HZ_ERROR)

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_ind_hz_err              |
+--------------------------------------------------------------------+

  PURPOSE : Indication of the CBCH message and stop the next blocks.

*/

LOCAL void cb_ind_hz_err (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  PALLOC (mmi_cbch_ind, MMI_CBCH_IND);

  memset (mmi_cbch_ind->cbch_msg, 0xff, CBCH_MSG_SIZE);
  mmi_cbch_ind->cbch_msg[2] = pcbch->msg_hz_id >> 8;
  mmi_cbch_ind->cbch_msg[3] = pcbch->msg_hz_id & 0xff;
  mmi_cbch_ind->cbch_len = 0; /* error indication */
  ALR_TRACE_CB ("cb_ind_hz_err()");

  ma_cb_mmi_cbch_ind(mmi_cbch_ind);
}
#endif /* FF_HOMEZONE */

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_cbch_message            |
+--------------------------------------------------------------------+

  PURPOSE : Reception of a complete CBCH message.

*/

LOCAL void cb_cbch_message (UBYTE length)
{
  /*
   * Indicate the message
   */
  cb_ind (length);
  /*
   * stop the next blocks
   */
  cb_stop_next_blocks ();
  /*
   * store the cbch message to avoid double reception
   */
  cb_store_cbch_msg ();
  /*
   * start reading of the next scheduled message
   */
  cb_next_scheduled_msg();  
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_store_cbch_msg          |
+--------------------------------------------------------------------+

  PURPOSE : Store key parameter of a CBCH message to avoid
            forwarding of a CBCH message twice.

*/
LOCAL void cb_store_cbch_msg (void)
{
  USHORT x;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  ALR_TRACE_CB ("Store msg");

  /*
   *  It is not permitted to save the cbch message,
   *  if the following pattern are in the
   *  lacation area identification (bcch:sys_info_3) :
   *                   [4]  0x00
   *                   [5]  0xF1
   *                   [6]  0x10
   *
   *  that means FTA in testhouse
   */
  if (alr_data->ma_data.sys_info_3[4] EQ 0     AND
      alr_data->ma_data.sys_info_3[5] EQ 0xF1  AND
      alr_data->ma_data.sys_info_3[6] EQ 0x10)
    return;

  /*
   * If storage is full, remove the first one.
   */
  if (pcbch->old_cbch_msg.cnt EQ MAX_OLD_CB)
    cb_remove_message (0);

  /*
   * store the new one
   */
  x = pcbch->old_cbch_msg.cnt++;
  memcpy (&pcbch->old_cbch_msg.header[x],
          &pcbch->msg, sizeof (T_CBMSG_HEADER));
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_check_message_id        |
+--------------------------------------------------------------------+

  PURPOSE : Check Message Identifier of the first CBCH block.

*/

LOCAL UBYTE cb_check_message_id (USHORT msg_id)
{
  USHORT i;
  UBYTE  result = TRUE;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  /*
   * if no message identifier are defined for comparision
   * make a shortcut and return immediately. 
   * MODUS CBCH_ACCEPT,but no msg_id defined,accept NO messages
   * MODUS CBCH_IGNORE,but no msg_id define, accept all messages
   * MODUS CBCH_STOP,but no msg_id defined,  accept NO messages
   * MODUS CBCH_HOMEZONE,but no msg_id defined, accept NO messages/ 
   */
  if ( pcbch->msg_id[0] EQ NOT_PRESENT_16BIT )
  {
    if (pcbch->modus EQ CBCH_IGNORE) 
    {
      return TRUE;
    }
    else
    {
      return FALSE;
    }
  }

  /*
   * initialise result
   * if the msg_id is not found and MMI specified
   * a positive range(CBCH_ACCEPT) return 0
   */
  result = (UBYTE)(pcbch->modus EQ CBCH_ACCEPT ? FALSE : TRUE);

  /*
   * message identifier are stored as a pair to express a range
   */
  for (i=0; i < MAX_IDENTS-1; i+=2)
  {
    /*
     * end of list reached
     */
    if (pcbch->msg_id[i] EQ NOT_PRESENT_16BIT)
      break;

    /*
     * check whether the message identifier is inside or outside
     * the range. Result depends on what MMI has requested.
     */
    if ((pcbch->msg_id[i]   <= msg_id) AND
        (pcbch->msg_id[i+1] >= msg_id))
      result = (UBYTE)(pcbch->modus EQ CBCH_ACCEPT ? TRUE : FALSE);
  }

  return result;
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_sat_check_message_id    |
+--------------------------------------------------------------------+

  PURPOSE : Check Message Identifier of the first CBCH block for CBCH
            download purposes.

*/

LOCAL UBYTE cb_sat_check_message_id (USHORT msg_id)
{
  USHORT i;
  UBYTE  result;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  /*
   * initialize result
   */
  result = FALSE;

  /*
   * for all stored message identifier of CBCH download
   */
  for (i = 0; i < MAX_IDENTS_SAT; i++)
  {
    /*
     * end of list reached
     */
    if (pcbch->msg_id_sat[i] EQ NOT_PRESENT_16BIT)
      break;

    /*
     * if message identifier matches
     */
    if (pcbch->msg_id_sat[i] EQ msg_id)
    {
      result = TRUE;
      break;
    }
  }

  return result;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_check_data_coding_scheme|
+--------------------------------------------------------------------+

  PURPOSE : Check Data Coding Scheme of the first CBCH block.

*/
LOCAL UBYTE cb_check_data_coding_scheme (UBYTE dcs_id)
{
  USHORT i;
  UBYTE  result = TRUE;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  /*
   * if the list is empty, result is positive
   */
  if (pcbch->dcs_id [0] EQ NOT_PRESENT_8BIT)
    return result;

  /*
   * initialise result
   * if the dcs_id is not found and MMI specified
   * a positive range(CBCH_ACCEPT) return 0
   */
  result = (UBYTE)(pcbch->modus EQ CBCH_ACCEPT ? 0 : 1);


  for (i=0;i<MAX_IDENTS-1;i+=2)
  {
    /*
     * end of list reached
     */
    if (pcbch->dcs_id[i] EQ NOT_PRESENT_8BIT)
      break;

      /*
       * Data Coding Scheme identifier matches
       */
      if (pcbch->dcs_id[i] <= dcs_id AND
          pcbch->dcs_id[i+1] >= dcs_id)
        result = (UBYTE)(pcbch->modus EQ CBCH_ACCEPT ? 1 : 0);
  }

  return result;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_check_old_msg           |
+--------------------------------------------------------------------+

  PURPOSE : Check against old messages.

*/
LOCAL UBYTE cb_check_old_msg (void)
{
  int i;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  T_CBMSG_HEADER  *p_cbh = (T_CBMSG_HEADER*)&pcbch->msg[0];

  ALR_TRACE_CB ("compare with stored msg");
  for (i=0; i < pcbch->old_cbch_msg.cnt; i++)
  {
    ALR_TRACE_CB_MSGHEAD (&pcbch->old_cbch_msg.header[i])

    if (memcmp (&pcbch->old_cbch_msg.header[i], p_cbh, 6) EQ 0)
    {
      return FALSE; /* already stored message */
    }
    else
    {
      /* if the difference is only in the update number,
       * then we need to delete the old version of the message (so that the different
       * versions do not fill up the storage) and we can even abandon the comparison;
       * a message that is resent with a different update number should only use
       * one entry in the list; otherwise it may throw out other messages that should be kept;
       * think of German network that sends every minute a new update; would use up 15 entries
       *
       * some change, now check if it is only the update number that has changed
       * (memcmp covers 4 header bytes: msg_id3,  msg_id4, dcs and page
       *  => ignore lint warning)
       */
      if ((CB_GS_CODE(&pcbch->old_cbch_msg.header[i]) == CB_GS_CODE(p_cbh)) AND
          (CB_MSG_CODE(&pcbch->old_cbch_msg.header[i]) == CB_MSG_CODE(p_cbh)) AND
          !memcmp (&(((T_CBMSG_HEADER*)&pcbch->old_cbch_msg.header[i])->msg_id3),
                     &p_cbh->msg_id3, 4)) /*lint !e420 (Warning -- Apparent access beyond array for function 'memcmp(const void *, const void *, unsigned int)', argument 3 (size=4) exceeds argument 1 (size=1)) */
      { /* all these fields match, therefore the change must have been only in the update number */

        SHORT old_update_nr, new_update_nr;
        old_update_nr = CB_UPDATE_NO(&pcbch->old_cbch_msg.header[i]);
        new_update_nr = CB_UPDATE_NO(p_cbh);
        /*
          * 3GPP 03 41
          * Any Update Number eight or less higher (modulo 16) than the last received Update Number 
          * will be considered more recent, and shall be treated as a new message
          */          
        if((!new_update_nr) OR 
           ((new_update_nr - old_update_nr + MAX_UPDATE_NO) % MAX_UPDATE_NO <= MAX_UPDATE_NO_DIFF))
        {
           ALR_TRACE_CB ("change update num, delete old message");
           /* remove the old message so that we do not use up another entry for this new version of the message */
           cb_remove_message (i);
           /* skip out of loop because we found the match  */
           break;
        }
        else
          return FALSE;
      }
    }
  }

  return TRUE;  /* new message */
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_read_cbch               |
+--------------------------------------------------------------------+

  PURPOSE : Starts reading of the first block of CBCH.
            The parameter defines if the extended or the normal CBCH
            should be read and if scheduling should be used.

*/

LOCAL void cb_read_cbch (UBYTE flags)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE extended = (UBYTE)(flags & CBCH_READ_EXT);
  ULONG first_block = pcbch->schedule_map[extended][0];
  ULONG second_block = pcbch->schedule_map[extended][1];
  
  PALLOC (sched_req, MPHC_CBCH_SCHEDULE_REQ);
 
  ALR_TRACE_CB_START_READ(flags);

  sched_req->cbch_select     = extended;

  ALR_EM_START_CBCH_READING;

  if(flags & CBCH_SCHED)
  {
    UBYTE n = pcbch->begin_schedule[extended] -1;
    
    sched_req->schedule_length = pcbch->end_schedule[extended] - 
                                 pcbch->begin_schedule[extended] +1;

    /*
     * the MPHC SAP uses first_blocks_1 as USHORT although the internal data base 
     * of ALR uses for convinience an array of 2 ULONG for first_blocks_0 and 
     * first_blocks_1; ergo: do a cast for first_blocks_1 here
     */

    if(  pcbch->begin_schedule[extended] EQ 1)
    {
      sched_req->first_blocks_0  = first_block;
      sched_req->first_blocks_1  = (USHORT)second_block;
    }
    else if(pcbch->begin_schedule[extended] < 32)
    {
      sched_req->first_blocks_0  = (first_block >> n) | ((second_block << (32 - n)));
      sched_req->first_blocks_1  = (USHORT) (second_block >> n);
    }
    else
    {
      sched_req->first_blocks_0 = (USHORT)(second_block >> (n - 32));
      sched_req->first_blocks_1  = 0;      
    }    
  }
  else
  {
    sched_req->schedule_length = 0;
  }

  ma_cb_sched_req(sched_req);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_check_ci_lai_plmn       |
+--------------------------------------------------------------------+

  PURPOSE : Checking the Cell Identity and the Location Area Identification
            to handle the geographical zone parameter.
*/

LOCAL void cb_check_ci_lai_plmn (void)
{
  USHORT  i;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE   keep = 0x00;
  UBYTE   *si3 = &alr_data->ma_data.sys_info_3[0];
  UBYTE   *lac = &pcbch->old_cid_plmn_lac[0];
  UCHAR  gs_scope_change_ind = CB_GEOGR_CHANGE_NONE;

  /*
   *  it is not permitted to save the cbch message,
   *  if the following pattern are in the
   *  location area identification (bcch:sys_info_3) :
   *                   [4]  0x00
   *                   [5]  0xF1
   *                   [6]  0x10
   *
   *   that means FTA in testhouse.
   */
  if ((si3[4] EQ 0x00) AND
      (si3[5] EQ 0xF1) AND
      (si3[6] EQ 0x10)
     )
    return;

  if ((si3[4] NEQ lac[2]) OR
      (si3[5] NEQ lac[3]) OR
      (si3[6] NEQ lac[4])
     )
  {
    /*
     *  The PLNM has changed
     */
    gs_scope_change_ind = CB_GEOGR_CHANGE_PLMN;
    pcbch->old_cbch_msg.cnt = 0; /* clear all messages */
  }
  else if ((si3[7] NEQ lac[5]) OR
           (si3[8] NEQ lac[6])
          )
  {
    /*
     *  The location area identification has changed
     */
    keep = CB_GEOGR_SCOPE_PLMN; /* keep PLMN wide messages */
    gs_scope_change_ind = CB_GEOGR_CHANGE_LAC;
  }
  else if ((si3[2] NEQ lac[0]) OR
           (si3[3] NEQ lac[1])
          )
  {
    /*
     *  The Cell Identity has changed
     */

    /* indicate cell change to MMI only if we have received any CBCH broadcast messages */
    if (pcbch->old_cbch_msg.cnt > 0)
		  gs_scope_change_ind = CB_GEOGR_CHANGE_CELL;
    keep = CB_GEOGR_SCOPE_CELLN; /* keep PLMN and Location Area wide messages */
  }

  if (keep)
  { /* cell or lac change occurred!, so delete all old cell wide or lac wide messages */
    for (i=0; i<pcbch->old_cbch_msg.cnt; i++)
    {
      T_CBMSG_HEADER  *p_old_cbh = (T_CBMSG_HEADER*)(&pcbch->old_cbch_msg.header[i]);
      /*
       * check all stored messages, use geographical scope parameter
       */
      if (((CB_GS_CODE(p_old_cbh) & keep) EQ 0) OR           /* old changed msgs */
          (CB_GS_CODE(p_old_cbh) EQ CB_GEOGR_SCOPE_CELLI) OR /* old cell wide msgs (immediate) */
          (CB_GS_CODE(p_old_cbh) EQ CB_GEOGR_SCOPE_CELLN)    /* old cell wide msgs (normal) */
         )
      {
        ALR_TRACE_CB_SCOPE_CHANGE (gs_scope_change_ind, CB_GS_CODE(p_old_cbh));
        cb_remove_message (i--);
      }
    }
  }

  /*
   * update old_cid_plmn_lac
   */
  memcpy (lac, &si3[2], 7);

}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_remove_message          |
+--------------------------------------------------------------------+

  PURPOSE : Removes the indicated CBCH message from the internal
            storage.

*/
LOCAL void cb_remove_message (USHORT i)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  if ((pcbch->old_cbch_msg.cnt) AND
      (i < pcbch->old_cbch_msg.cnt))
  {
    ALR_TRACE_CB ("remove msg");
    ALR_TRACE_CB_MSGHEAD (&pcbch->old_cbch_msg.header[i]);

    /*
     * decrement number of stored messages.
     */
    pcbch->old_cbch_msg.cnt--;
    memmove (&pcbch->old_cbch_msg.header[i],
             &pcbch->old_cbch_msg.header[i+1],
             (pcbch->old_cbch_msg.cnt-i) * sizeof(T_CBMSG_HEADER));
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_create_schedule_map     |
+--------------------------------------------------------------------+

  PURPOSE : Creation of a schedule map.

*/
LOCAL void cb_create_schedule_map (UBYTE old_cb_msg)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE  extended = pcbch->msg_is_extended;
  UBYTE* msg_ptr;

  /*
   * Store begin and end slot number
   */
  pcbch->begin_schedule [extended] = (UBYTE)(pcbch->msg [0] & 0x3F);
  pcbch->end_schedule   [extended] = pcbch->msg [1];

  /*
   * clear schedule bitmap
   */
  pcbch->schedule_length[extended] = 0;
  memset (&pcbch->schedule_map[extended][0], 0,
        2*sizeof (pcbch->schedule_map[extended][0]));
  /*
   * Check all new entries,
   * msg_ptr to first message description
   */
  msg_ptr = &pcbch->msg[8];

  msg_ptr = cb_create_schedule_entries (msg_ptr, 1);

  if (old_cb_msg)
  {
    cb_create_schedule_entries (msg_ptr, FALSE);
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_create_schedule_entries |
+--------------------------------------------------------------------+

  PURPOSE : Creation of schedule entries.

*/
LOCAL UBYTE* cb_create_schedule_entries (UBYTE* msg_ptr, BOOL flag)
{
  unsigned    i;
  BOOL        result;
  UBYTE       x;
  USHORT      msg_id;
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE       extended = pcbch->msg_is_extended;

  pcbch->last_slot_read[extended] = 0;
  for (i=0; i < pcbch->end_schedule[extended]; i++)
  {
    result = (pcbch->msg [2 + ((i+1)>>3)] & (1 <<(8-((i+1)&7))));

    ALR_TRACE_CB_SCHEDULE(i+1,result);

    if ((flag AND result) OR (!flag AND !result))
    {
      /*
       * result and bit are set, that means the msg:
       *  - was not send during the previous schedule period
       *  - or the msg was sent unscheduled during the last period
       *  - or is free of charge, reading advised
       * result and bit are both not set , that means the msg:
       *  - the msg doesn't match the above
       *  - these msgs are read only during the first 2 schedule periods
       */
      if (*msg_ptr & 0x80)
      {
        /*
         * First transmission of an SMSCB within the
         * Schedule Period
         */

        msg_id = (UBYTE)((*msg_ptr) & 0x7F);
        msg_ptr++;
        msg_id = (USHORT)(msg_id + *msg_ptr);
        if (cb_check_message_id (msg_id))
        {
          /*
           * Message Identifier is expected
           * then set slot bit in schedule map
           */
          pcbch->schedule_map[extended][i>>5] |= 1 << (i&31);
          if( (i+1) >= pcbch->begin_schedule[extended] )
          {
            pcbch->schedule_length[extended]++;
            pcbch->last_slot_read[extended] = (i+1);
          }
        }
      }
      else
      {
        if ((*msg_ptr & 0xC0) EQ 0)
        {
          /*
           * Retransmission indication
           * Extract original message slot number
           */
          x = (UBYTE)(*msg_ptr & 0x3F);
          if (x)
          {
            x--;
            if (pcbch->schedule_map[extended][x>>5] & (1 << (x&31)) )
            {
              /*
               * original message will be read, then set also
               * repetition of message in schedule map
               */
              pcbch->schedule_map[extended][i>>5] |= 1 << (i&31);
              if( (i+1) >= pcbch->begin_schedule[extended] )
              {
                pcbch->schedule_length[extended]++;
                pcbch->last_slot_read[extended] = (i+1);
              }
            }
          }
        }
        else
        {
          if (*msg_ptr EQ 0x41)
          {
            /*
             * Free Message Slot, reading advised
             * Set bit in schedule map
             */
            pcbch->schedule_map[extended][i>>5] |= 1 << (i&31);
            if( (i+1) >= pcbch->begin_schedule[extended] )
            {
              pcbch->schedule_length[extended]++;
              pcbch->last_slot_read[extended] = (i+1);
            }
          }
        }
      }
      /*
       * increment message description pointer
       */
      msg_ptr++;
    }
  }
  return msg_ptr;
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : cb_next_scheduled_msg      |
+--------------------------------------------------------------------+

  PURPOSE : Looks for end of scheduling period.

*/
LOCAL void cb_next_scheduled_msg(void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;
  UBYTE extended = pcbch->msg_is_extended;

  if ((get_cbch_state(extended)) EQ CBCH_DRX )
  {
    if( pcbch->schedule_length[extended] EQ 0 )
    {
      /*
        * instead of the scheduled SCHEDULE message
        * an invalid message has been received.
        * Restarting with NO DRX mode
        */
      set_cbch_state (extended, CBCH_NO_DRX);
      cb_read_cbch ((UBYTE)(CBCH_NO_SCHED | extended));
      return;
    }
    pcbch->schedule_length[extended]--;

    if(pcbch->schedule_length[extended] EQ 0)
    {
     /*
       * clear schedule map
       * all message slots have been read
       *
       * NO need to reconfigure L1 to re-read schedule message 
       * that follows slot 48
       */
      memset (&pcbch->schedule_map[extended][0], 0,
        2*sizeof (pcbch->schedule_map[extended][0]));
      TRACE_EVENT("Scheduled Schedule message expected");

    }
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : get_cbch_state      |
+--------------------------------------------------------------------+

  PURPOSE : Returns the state of STATE_EXT_CBCH or STATE_NORMAL_CBCH
                   depending upon the input parameter "extended"

*/

UBYTE get_cbch_state( UBYTE extended)
{
  if( extended)
    return GET_STATE(STATE_EXT_CBCH);
  else
    return GET_STATE(STATE_NORMAL_CBCH);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_CBCH                   |
| STATE   : code                ROUTINE : set_cbch_state      |
+--------------------------------------------------------------------+

  PURPOSE : Sets  the state of STATE_EXT_CBCH or STATE_NORMAL_CBCH
                   depending upon the input parameter "extended"

*/

void set_cbch_state( UBYTE extended, UBYTE new_state)
{
  if(extended)
  {
    SET_STATE(STATE_EXT_CBCH,new_state);
  }
  else
    SET_STATE(STATE_NORMAL_CBCH,new_state);
}


#if defined(FF_HOMEZONE)
GLOBAL void cb_tim_homezone (void)
{
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  if (pcbch->homezone EQ CBCH_HZ_IDLE)
  {
    if (GET_STATE(STATE_MA) EQ MA_IDLE)
    {
      ALR_TRACE_CB ("call cb_start()");
      cb_start ();
    }
    else
    {
      TIMERSTART (TIM_HOMEZONE, 1000);
    }
  }
  else if (pcbch->homezone EQ CBCH_HZ_PENDING)
  {
    /*
     * mark as received, indicate error, stop CBCH reading
     */
    ALR_TRACE_CB ("cb_ind_hz_err()");
    pcbch->homezone = CBCH_HZ_RECEIVED;
#if defined(INDICATE_HZ_ERROR)
    cb_ind_hz_err ();
#endif  /* INDICATE_HZ_ERROR */
    if (    (pcbch->modus EQ NOT_PRESENT_8BIT)
        AND (!pcbch->sat_enabled))
      cb_stop ();
  }
}

LOCAL UBYTE cb_check_homezone (UBYTE length)
{
  UBYTE  result = FALSE;/* no special handling of homezone CBCH message */
  T_CBCH_DATA *pcbch = &alr_data->cbch_data;

  if (pcbch->homezone EQ CBCH_HZ_PENDING)
  {
    T_CBMSG_HEADER  *p_cbh;
    p_cbh = (T_CBMSG_HEADER*)(&pcbch->msg[0]);

    if (    (CB_MESSAGE_ID (p_cbh) EQ pcbch->msg_hz_id)
        AND (CB_DCS (p_cbh)        EQ pcbch->dcs_hz_id)
       )
    {
      /* one-time request of homezone CBCH message after cell reselection */
      TIMERSTOP (TIM_HOMEZONE);
      pcbch->homezone = CBCH_HZ_RECEIVED;
      ALR_TRACE_CB_MSGHEAD (p_cbh);
      ALR_TRACE_CB ("homezone msg received");
      cb_ind (length);
      result = TRUE;

      if (    (pcbch->modus EQ NOT_PRESENT_8BIT)
          AND (!pcbch->sat_enabled))
      {
        ALR_TRACE_CB ("no other msg req.");
        cb_stop ();
      }
      else
        cb_stop_next_blocks ();
    }
  }

  return result;
}
#endif  /* FF_HOMEZONE */

#endif
