view src/g23m-gsm/alr2/alr_pch.c @ 303:f76436d19a7a default tip

!GPRS config: fix long-standing AT+COPS chance hanging bug There has been a long-standing bug in FreeCalypso going back years: sometimes in the AT command bring-up sequence of an ACI-only MS, the AT+COPS command would produce only a power scan followed by cessation of protocol stack activity (only L1 ADC traces), instead of the expected network search sequence. This behaviour was seen in different FC firmware versions going back to Citrine, and seemed to follow some law of chance, not reliably repeatable. This bug has been tracked down and found to be specific to !GPRS configuration, stemming from our TCS2/TCS3 hybrid and reconstruction of !GPRS support that was bitrotten in TCS3.2/LoCosto version. ACI module psa_mms.c, needed only for !GPRS, was missing in the TCS3 version and had to be pulled from TCS2 - but as it turns out, there is a new field in the MMR_REG_REQ primitive that needs to be set correctly, and that psa_mms.c module is the place where this initialization needed to be added.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 08 Jun 2023 08:23:37 +0000
parents 3a14ee9a9843
children
line wrap: on
line source

/*
+-----------------------------------------------------------------------------
|  Project :  GSM-PS
|  Modul   :  ALR_PCH
+-----------------------------------------------------------------------------
|  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 PCH_Control.
+-----------------------------------------------------------------------------
*/

#ifndef ALR_PCH_C
#define ALR_PCH_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 =====================================================*/

/*==== VARIABLES ==================================================*/
UBYTE page_mode_before_hplmn_search = PGM_NORMAL;
/*==== CONSTANTS ==================================================*/
#define IMSI_ODD_FLAG     8
#define IMSI_EVEN_FLAG    0
#define IDENT_TYPE_MON    0
#define IDENT_TYPE_IMSI   1
#define IDENT_TYPE_IMEI   2
#define IDENT_TYPE_IMEISV 3
#define IDENT_TYPE_TMSI   4
#define END_MARK          0xF0

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

#define TRACING

#if defined (TRACING)
#define ALR_TRACE_PCH(a)  ALR_TRACE(a)
#else
#define ALR_TRACE_PCH(a)
#endif

#ifdef TRACING
#define ALR_TRACE_PCH_CONFIG(b,a,c,t,p,m)  \
          TRACE_EVENT_P6 ("MFRMS: %d AG_RES: %d COMB: %d GRP: %d PGRP: %d PI: %d",b,a,c,t,p,m)
#define ALR_TRACE_PCH_PGM(p,x) \
          TRACE_EVENT_P2 ("new_pgm: %d cur_pgm: %d",p,x)
#define ALR_TRACE_PCH_IMSI() \
        { for (i=0; i<alr_data->pch_data.imsi[0]+1;i++) { \
          TRACE_EVENT_P2 ("imsi[%d]=%x",i,alr_data->pch_data.imsi[i]);} }
#else
#define ALR_TRACE_PCH_CONFIG(b,a,c,t,p,m)
#define ALR_TRACE_PCH_PGM(p,x)
#define ALR_TRACE_PCH_IMSI()
#endif

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

  PURPOSE : Initialize PCH Control Process.

*/
GLOBAL void pch_init (void)
{
  alr_data->pch_data.saved_page_mode = PGM_REORG;
  alr_data->pch_data.reorg_bcch_reading = FALSE;
}

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

  PURPOSE : Process signal pch_start from SDL process
            Main_Control.

*/
static const UBYTE PAG_BLOCK_TABLE [2][8] =
{
  /* not combined ccch */
  9,8,7,6,5,4,3,2,
  /* combined ccch     */
  3,2,1,1,1,1,1,1
};

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

  PURPOSE : Configutes L1 for paging.

*/
GLOBAL void pch_configure (T_MPH_IDLE_REQ *idle, UBYTE page_mode)
{
  UBYTE pag_blocks_per_mfr;

  if(idle NEQ NULL)
  {

    alr_data->pch_data.dlt                      = idle->dlt;
    alr_data->pch_data.act_dlt                  = idle->dlt;

    ALR_EM_SET_EM_ACT_DLT;

    pag_blocks_per_mfr                          = PAG_BLOCK_TABLE [idle->comb_ccch][idle->bs_ag_blocks_res];
    /*
     * pl_idle.bs_pa_mfrms has a range from 2-9.
     * MPH_IDLE_REQ codes them from 0-7
     */
    alr_data->pch_data.pl_idle.bs_pa_mfrms      = (UBYTE)(idle->bs_pa_mfrms + 2);
    alr_data->pch_data.pl_idle.bs_ag_blks_res   = idle->bs_ag_blocks_res;
    alr_data->pch_data.pl_idle.bcch_combined    = idle->comb_ccch;
    alr_data->pch_data.pl_idle.ccch_group       = (UBYTE)(idle->tn / 2);
    alr_data->pch_data.pl_idle.page_group       = idle->pg;
    alr_data->pch_data.pl_idle.page_block_index = (UBYTE)(idle->pg % pag_blocks_per_mfr);

    ALR_TRACE_PCH_CONFIG(idle->bs_pa_mfrms+2, idle->bs_ag_blocks_res,
                         idle->comb_ccch, idle->tn/2, idle->pg,
                         idle->pg % pag_blocks_per_mfr);
  }

  /*
   * During cell reselection reading of PCH is started hard coded with
   * page mode PGM_REORG because of the lack of parameters to calculate
   * the right paging group. Detection of SI3 during cell reselection
   * triggers the function pch_config_resel() to reconfigure PCH reading,
   * detection of a changed page mode during cell reselection is handled
   * by function pch_check_page_mode_cr() which needs to know whether SI3
   * is read
   */
  if (GET_STATE (STATE_MA) EQ MA_CELL_RESELECTION)
    alr_data->pch_data.si3_read = FALSE;


  /* Ensure that L1 does not get initialised with PAGING EXTENDED */
  switch( page_mode )
  {
    case SAVED_PGM:
      if( alr_data->pch_data.saved_page_mode EQ PGM_EXTENDED )
      {
        alr_data->pch_data.saved_page_mode = PGM_REORG;
      }
      page_mode = alr_data->pch_data.saved_page_mode;
      /*lint -fallthrough*/
    default:
      alr_data->pch_data.pl_idle.page_mode = page_mode;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_PCH                    |
| STATE   : code                ROUTINE : pch_save_pgm               |
+--------------------------------------------------------------------+
  PURPOSE : Configure Paging
*/
GLOBAL void pch_save_pgm(UBYTE mode)
{
  if(mode)
    alr_data->pch_data.saved_page_mode = mode;
  else
    alr_data->pch_data.saved_page_mode = alr_data->pch_data.pl_idle.page_mode;
}


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

  PURPOSE : Configure Paging

*/

GLOBAL void pch_start_ccch_req (void)
{
  PALLOC(pl_idle, MPHC_START_CCCH_REQ);
  memset(pl_idle, 0, sizeof(T_MPHC_START_CCCH_REQ));

  ALR_EM_PAGE_MODE_CHANGE;

  switch (alr_data->pch_data.pl_idle.page_mode)
  {
    case PGM_REORG:
      ALR_TRACE_PCH ("config REORG");
      /* dummy values */
      pl_idle->bs_pa_mfrms      = 2;
      pl_idle->bs_ag_blks_res   = 7;
      pl_idle->bcch_combined    = 0;
      pl_idle->ccch_group       = 0;
      pl_idle->page_group       = 0;
      pl_idle->page_block_index = 0;
      pl_idle->page_mode        = PGM_REORG;
      break;
    case PGM_REORG_CS:
      ALR_TRACE_PCH ("config REORG_CS");

      memcpy (pl_idle, &alr_data->pch_data.pl_idle,
              sizeof (T_MPHC_START_CCCH_REQ));
      /*
       * if the page_mode is PGM_REORG_CS then
       * we have to change this to PGM_REORG
       * before we send it to L1
       */
      pl_idle->page_mode        = PGM_REORG;
     break;
    case PGM_EXTENDED:
      ALR_TRACE_PCH ("config EXT");

      memcpy (pl_idle, &alr_data->pch_data.pl_idle,
              sizeof (T_MPHC_START_CCCH_REQ));
      pl_idle->page_mode        = PGM_EXTENDED;
      break;
   case PGM_REORG_NC_SYNC:
     /*this case is the same as the default - except for the TRACE*/
      ALR_TRACE_PCH ("config PGM_REORG_NC_SYNC (NORMAL)");

      memcpy (pl_idle, &alr_data->pch_data.pl_idle,
              sizeof (T_MPHC_START_CCCH_REQ));
      pl_idle->page_mode        = PGM_NORMAL;
     break;
   default:
      ALR_TRACE_PCH ("config NORMAL");

      memcpy (pl_idle, &alr_data->pch_data.pl_idle,
              sizeof (T_MPHC_START_CCCH_REQ));
      pl_idle->page_mode        = PGM_NORMAL;
     break;
  }

  SET_STATE(STATE_PCH,PCH_ACTIVE);
  ma_pch_start_ccch_req(pl_idle);

  TRACE_EVENT_P1("reorg_bcch_reading = %d",alr_data->pch_data.reorg_bcch_reading);
  if(alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG OR
     alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG_CS OR
     alr_data->pch_data.reorg_bcch_reading EQ TRUE)
  { /*
     * if we're going into reorg paging we also need to read the BCCH
     * to read possibly changed channel configuration
     * XXX but not at PGM_REORG_NC_SYNC (NORMAL)
     */
    ma_scell_full_nbcch();
  }
  else
  { /*
     * otherwise we stop the reading of the BCCH and start the periodic read.
     * XXX but not if we have a PBCCH
     */
#ifdef GPRS
    if(alr_data->gprs_data.pbcch EQ FALSE)
#endif
      sc_start_periodic();
  }
}




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

  PURPOSE : Get new mobile identity information from RR. Build IMSI
            pattern and store TMSI.

*/
GLOBAL void pch_identity_req (T_MPH_IDENTITY_REQ *mph_identity_req)
{
  UBYTE i;

  /* the IMSI in the identity request is coded as a 15byte long array
     and is stored for later usage in pch_data as the message representation
     of the IMSI according to GSM4.08 10.5.1.4 */

  if (mph_identity_req->mid.len_imsi EQ 0)
  {
    /*
     * limited service, no paging
     */
    memset (alr_data->pch_data.imsi, 0, IMSI_LEN);
    alr_data->pch_data.v_tmsi   = FALSE;
    alr_data->pch_data.tmsi     = 0L;
  }
  else
  {
    /*
     * The IMSI is available
     */
    /* store length */
    alr_data->pch_data.imsi[0] = (UBYTE)((mph_identity_req->mid.len_imsi + 2) / 2);

    if (mph_identity_req->mid.len_imsi & 1)
    {
      ALR_TRACE_PCH ("IMSI is odd");
      /*
       * odd number of digits
       * first digit + odd flag + identity type IMSI
       */
      alr_data->pch_data.imsi[1] = (UBYTE)((mph_identity_req->mid.imsi[0] << 4) +
                                            IMSI_ODD_FLAG +
                                            IDENT_TYPE_IMSI);
    }
    else
    {
      ALR_TRACE_PCH ("IMSI is even");
      /*
       * even number of digits
       * first digit + even flag + identity type IMSI
       */
      alr_data->pch_data.imsi[1] = (UBYTE)((mph_identity_req->mid.imsi[0] << 4) +
                                            IMSI_EVEN_FLAG +
                                            IDENT_TYPE_IMSI);
    }
    /*
     * fill in the rest of digits
     */
    for (i=1;i<mph_identity_req->mid.len_imsi;i++)
    {
      if (i & 1)
        alr_data->pch_data.imsi[(i/2)+2] = (UBYTE)(END_MARK + mph_identity_req->mid.imsi[i]);
      else
      {
        alr_data->pch_data.imsi[(i/2)+1] &= ~END_MARK;  /* remove end mark */
        alr_data->pch_data.imsi[(i/2)+1] = (UBYTE)(alr_data->pch_data.imsi[(i/2)+1] +
                                                   (mph_identity_req->mid.imsi[i] << 4));
      }
    }
    alr_data->pch_data.imsi_mod_1000 = (SHORT)
      ( (mph_identity_req->mid.imsi[mph_identity_req->mid.len_imsi-1] +
         mph_identity_req->mid.imsi[mph_identity_req->mid.len_imsi-2] * 10 +
         mph_identity_req->mid.imsi[mph_identity_req->mid.len_imsi-3] * 100 ) % 1000);

    ALR_TRACE_PCH_IMSI();

    /*
     * copy TMSI
     */
    alr_data->pch_data.v_tmsi = mph_identity_req->mid.v_tmsi;
    alr_data->pch_data.tmsi   = mph_identity_req->mid.tmsi;
#ifdef GPRS
    gprs_alr_store_ptmsi(mph_identity_req->mid.v_ptmsi,
                         mph_identity_req->mid.ptmsi);
    gprs_alr_store_ptmsi2(mph_identity_req->mid.v_ptmsi2,
                          mph_identity_req->mid.ptmsi2);
#endif
  }
}

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

  PURPOSE : Incrementation of downlink timeout counter after receiving
            a valid PCH block.

*/
GLOBAL void pch_increment_dlt (void)
{
  if (alr_data->pch_data.act_dlt <
      alr_data->pch_data.dlt)
  {
#if !defined NTRACE
    trc_mon_counter_idle (alr_data->pch_data.act_dlt,
                          alr_data->pch_data.dlt);
#endif /* (!defined NTRACE) */
    alr_data->pch_data.act_dlt++;

    ALR_EM_SET_EM_ACT_DLT;

  }
}

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

  PURPOSE : Decrementation of downlink timeout counter after receiving
            an invalid PCH block.

*/
GLOBAL void pch_decrement_dlt (void)
{
  if (alr_data->pch_data.act_dlt > 4)
  {
#if !defined NTRACE
    trc_mon_counter_idle (alr_data->pch_data.act_dlt,
                          alr_data->pch_data.dlt);
#endif /* (!defined NTRACE) */

    alr_data->pch_data.act_dlt -= 4;

    ALR_EM_SET_EM_ACT_DLT;

  }
  else
  {
    alr_data->pch_data.act_dlt = alr_data->pch_data.dlt;
#if !defined NTRACE
    trc_mon_counter_idle (alr_data->pch_data.act_dlt,
                          alr_data->pch_data.dlt);
#endif

    ALR_EM_SET_EM_ACT_DLT;

    ALR_TRACE_PCH ("downlink fail");

    ma_error_ind (CS_DOWN_LINK_FAIL, alr_data->serving_cell);
  }
}


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

  PURPOSE : Check the page mode of an incoming unacknowledged
            message.
*/
static const UBYTE PAGE_MODE_CHANGE [3][4] =
       {
         /* old mode = paging normal         */
         NONE,            SWAP_TO_EXTEND, SWAP_TO_REORG, NONE,
         /* old mode = extended paging       */
         SWAP_TO_NORMAL,  NONE,           SWAP_TO_REORG, NONE,
         /* old mode = paging reorganisation */
         SWAP_TO_NORMAL,  SWAP_TO_EXTEND, NONE,          NONE
       };


GLOBAL void pch_check_page_mode (T_MPHC_DATA_IND  *data_ind)
{
  UBYTE page_mode;
  UBYTE cur_page_mode = alr_data->pch_data.pl_idle.page_mode;

  page_mode = (UBYTE)(data_ind->l2_frame.content [3] & 3);

  ALR_TRACE_PCH_PGM(page_mode, alr_data->pch_data.pl_idle.page_mode);

  /*if HPLMN search is going on and we should be in REORG, we are really
  in REORG_NC_SYNC (NORMAL). Otherwise we cannot synchronize to the NC's*/
  if(alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG_NC_SYNC)
    cur_page_mode = PGM_REORG;

  if(alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG_CS)
    cur_page_mode = PGM_REORG;

  switch (PAGE_MODE_CHANGE [cur_page_mode][page_mode])
  {
    case SWAP_TO_NORMAL:
      ALR_TRACE_PCH ("SWAP_TO_NORMAL");
      alr_data->pch_data.pl_idle.page_mode = PGM_NORMAL;
      pch_start_ccch_req ();
      break;
    case SWAP_TO_EXTEND:
      ALR_TRACE_PCH ("SWAP_TO_EXT");
      alr_data->pch_data.pl_idle.page_mode = PGM_EXTENDED;
      pch_start_ccch_req ();
      break;
    case SWAP_TO_REORG:
      ALR_TRACE_PCH ("SWAP_TO_REORG");
      alr_data->pch_data.reorg_bcch_reading = TRUE;
      alr_data->pch_data.si_bitmap = 0;
      alr_data->pch_data.pl_idle.page_mode = PGM_REORG_CS;
      pch_start_ccch_req ();
      break;
    default:
      break;
  }
}

GLOBAL void pch_check_page_mode_cr (T_MPHC_DATA_IND  *data_ind)
{
  UBYTE page_mode;
  UBYTE cur_page_mode = alr_data->pch_data.saved_page_mode;
  UBYTE swap = TRUE;

  page_mode = (UBYTE)(data_ind->l2_frame.content [3] & 3);
  if(alr_data->pch_data.saved_page_mode EQ PGM_REORG_CS)
    cur_page_mode = PGM_REORG;

  switch (PAGE_MODE_CHANGE [cur_page_mode][page_mode])
  {
    case SWAP_TO_NORMAL:
      ALR_TRACE_PCH ("CR:SWAP_TO_NORMAL");
      alr_data->pch_data.saved_page_mode = PGM_NORMAL;
      break;

    case SWAP_TO_EXTEND:
      ALR_TRACE_PCH ("CR:SWAP_TO_EXT");
      alr_data->pch_data.saved_page_mode = PGM_EXTENDED;
      break;

    case SWAP_TO_REORG:
      ALR_TRACE_PCH ("CR:SWAP_TO_REORG");
      alr_data->pch_data.saved_page_mode = PGM_REORG_CS;
      break;

    default:
      swap = FALSE;
      break;
  }

  if (swap AND alr_data->pch_data.si3_read AND
      alr_data->pch_data.last_start_ccch_req.bs_pa_mfrms NEQ NOT_PRESENT_8BIT)
  {
    PALLOC(pl_idle, MPHC_START_CCCH_REQ);
    memcpy ( pl_idle,
             &(alr_data->pch_data.last_start_ccch_req),
             sizeof(T_MPHC_START_CCCH_REQ));
    pl_idle->page_mode = alr_data->pch_data.saved_page_mode;
    ma_pch_start_ccch_req (pl_idle);
  }

}


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

  PURPOSE : The function checks a paging request type 1 message.

*/
GLOBAL void pch_check_pag_1 (T_MPHC_DATA_IND *data_ind)
{
  UBYTE *frame  = data_ind->l2_frame.content;
  ULONG  tmsi;
  UBYTE  i;
  UBYTE  channel_needed;
  UBYTE *frame_start  = data_ind->l2_frame.content;
  //ALR_TRACE_PCH ("p1");
  /*
   * Check only if IMSI available (len NEQ 0),
   * ti and pd = 0x06 and
   * l2 pseudolength is greater than 5 bytes
   */
  if (alr_data->pch_data.imsi[0] AND
      frame[1] EQ 0x06 AND
      frame[0] > 0x15)
  {
    /*
     * store channel needed type
     */
    channel_needed = frame[3];
    /*
     * check type of identity for mobile identity 1
     */
    switch (frame [5] & 7)
    {
      case 1:
        //ALR_TRACE_PCH ("p1 IMSI");
        /*
         * IMSI
         */
        if (!memcmp (alr_data->pch_data.imsi, &frame[4],
                     alr_data->pch_data.imsi[0]+1))
        {
          //ALR_TRACE_PCH ("p1 IMSI match");
          /*
           * IMSI matches
           */
#ifdef GPRS
          if(!gprs_alr_check_packet_paging(frame_start,1))
#endif
            ma_pch_paging_ind (1, (UBYTE)((channel_needed & 0x30)>>4));
#ifdef GPRS
          else if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
            ma_pch_paging_ind (ID_IMSI, CN_PACKET);
#endif
          return;
        }
        break;

      case 4:
        //ALR_TRACE_PCH ("p1 TMSI");
        /*
         * TMSI
         */
        if (alr_data->pch_data.v_tmsi)
        {
          tmsi = 0L;
          for (i=0;i<frame[4]-1;i++)
            tmsi += frame[i+6] << ((8*(frame[4]-2-i)));
          if (alr_data->pch_data.tmsi EQ tmsi)
          {
            //ALR_TRACE_PCH ("p1 TMSI match");
            /*
             * TMSI matches
             */
            ma_pch_paging_ind (4, (UBYTE)((channel_needed & 0x30)>>4));
            return;
          }
        }
#ifdef GPRS
        if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
        {
          tmsi = 0L;
          for (i=0;i<frame[4]-1;i++)
            tmsi += frame[i+6] << ((8*(frame[4]-2-i)));
          if(gprs_alr_check_ptmsi(tmsi)) return;
        }
#endif
       break;
    }
    /*
     * check type of identity for mobile identity 2
     * set frame pointer to start of mobile identity 2 (id tag)
     * old frame pointer (frame) +
     * offset pseudo length etc (4) +
     * length mobile identity 1 (frame [4]) +
     * plus 1 byte for length field
     */
    frame = frame + 4 + frame[4] + 1;
    if (frame[0] EQ 0x17)
    {
      /*
       * mobile identity 2 is available
       */
      switch (frame [2] & 7)
      {
        case 1:
          //ALR_TRACE_PCH ("p1 IMSI2");
          /*
           * IMSI
           */
          if (!memcmp (alr_data->pch_data.imsi, &frame[1],
                       alr_data->pch_data.imsi[0]+1))
          {
            //ALR_TRACE_PCH ("p1 IMSI2 match");
            /*
             * IMSI matches
             */
#ifdef GPRS
          if(! gprs_alr_check_packet_paging(frame_start,2))
#endif
            ma_pch_paging_ind (1, (UBYTE)((channel_needed & 0xC0)>>6));
#ifdef GPRS
          else if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
            ma_pch_paging_ind (ID_IMSI, CN_PACKET);
#endif
            return;
          }
          break;
        case 4:
          // ALR_TRACE_PCH ("p1 TMSI2");
          /*
           * TMSI
           */
          if (alr_data->pch_data.v_tmsi)
          {
            tmsi = 0L;
            for (i=0;i<frame[1]-1;i++)
              tmsi += frame[i+3] << ((8*(frame[1]-2-i)));
            if (alr_data->pch_data.tmsi EQ tmsi)
            {
              ALR_TRACE_PCH ("p1 IMSI2");
              /*
               * TMSI matches
               */
              ma_pch_paging_ind (4, (UBYTE)((channel_needed & 0xC0)>>6));
              return;
            }
          }
#ifdef GPRS
        if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
        {
          tmsi = 0L;
          for (i=0;i<frame[4]-1;i++)
            tmsi += frame[i+6] << ((8*(frame[4]-2-i)));
          if(gprs_alr_check_ptmsi(tmsi)) return;
        }
#endif
          break;
      }
    }
  }
  //ALR_TRACE_PCH("end p1");
}


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

  PURPOSE : The function checks a paging request type 2 message.

*/

GLOBAL void pch_check_pag_2 (T_MPHC_DATA_IND *data_ind)
{
  UBYTE *frame  = data_ind->l2_frame.content;
  ULONG  tmsi;
  UBYTE  i;
  UBYTE  channel_needed;

  /*
   * Check only if IMSI available (len NEQ 0) and
   * ti and pd = 0x06 and
   * l2 pseudolength is greater than 5 bytes
   */
  if (alr_data->pch_data.imsi[0] AND
      frame[1] EQ 0x06 AND
      frame[0] > 0x15)
  {
    /*
     * store channel needed type
     */
    channel_needed = frame[3];
    /*
     * check mobile identity 1 only if TMSI is available
     */
    if (alr_data->pch_data.v_tmsi)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+4] << (8*(3-i));
      if (alr_data->pch_data.tmsi EQ tmsi)
      {
        /*
         * TMSI matches
         */
        ma_pch_paging_ind (4, (UBYTE)((channel_needed & 0x30)>>4));
        return;
      }
    }
#ifdef GPRS
    if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+4] << (8*(3-i));
      if(gprs_alr_check_ptmsi(tmsi)) return;
    }
#endif
    /*
     * check mobile identity 2 only if TMSI is available
     */
    if (alr_data->pch_data.v_tmsi)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+8] << (8*(3-i));
      if (alr_data->pch_data.tmsi EQ tmsi)
      {
        /*
         * TMSI matches
         */
        ma_pch_paging_ind (4, (UBYTE)((channel_needed & 0xC0)>>6));
        return;
      }
    }
#ifdef GPRS
    if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+8] << (8*(3-i));
      if(gprs_alr_check_ptmsi(tmsi)) return;
    }
#endif

    if (frame[12] EQ 0x17)
    {
      /*
       * mobile identity 3 is available
       * calculation of channel needed for
       * mobile identity 3 from the rest octet.
       */
      channel_needed = frame [ frame[13]+14 ];
      if (channel_needed & 0x80)
        channel_needed = (UBYTE)((channel_needed >> 5) & 3);
      else
        channel_needed = 0;

      switch (frame [14] & 7)
      {
        case 1:
          /*
           * IMSI
           */
          if (!memcmp (alr_data->pch_data.imsi, &frame[13],
                       alr_data->pch_data.imsi[0]+1))
          {
            /*
             * IMSI matches
             */
#ifdef GPRS
     if(! gprs_alr_check_packet_paging_2(frame,3))
#endif
       ma_pch_paging_ind (1, (UBYTE) channel_needed);
#ifdef GPRS
     else if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
       ma_pch_paging_ind (ID_IMSI, CN_PACKET);
#endif
            return;
          }
          break;
        case 4:
          /*
           * TMSI
           */
          if (alr_data->pch_data.v_tmsi)
          {
            tmsi = 0L;
            for (i=0;i<frame[13]-1;i++)
              tmsi += frame[i+15] << ((8*(frame[13]-2-i)));
            if (alr_data->pch_data.tmsi EQ tmsi)
            {
              /*
               * TMSI matches
               */
              ma_pch_paging_ind (4, channel_needed);
              return;
            }
#ifdef GPRS
            if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
            {
              tmsi = 0L;
              for (i=0;i<frame[13]-1;i++)
                tmsi += frame[i+15] << ((8*(frame[13]-2-i)));
              if(gprs_alr_check_ptmsi(tmsi)) return;
            }
#endif
          }
          break;
      }
    }
  }
}

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

  PURPOSE : The function checks a paging request type 3 message.

*/

GLOBAL void pch_check_pag_3 (T_MPHC_DATA_IND *data_ind)
{
  UBYTE *frame  = data_ind->l2_frame.content;
  ULONG  tmsi;
  UBYTE  i;
  UBYTE  channel_needed;

  /*
   * Check only if IMSI available (len NEQ 0) and
   * ti and pd = 0x06 and
   * l2 pseudolength is greater than 5 bytes
   */
  if (alr_data->pch_data.imsi[0] AND
      frame[1] EQ 0x06 AND
      frame[0] > 0x15)
  {
    /*
     * store channel needed type
     */
    channel_needed = frame[3];
    /*
     * check mobile identity 1 only if TMSI is available
     */
    if (alr_data->pch_data.v_tmsi)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+4] << (8*(3-i));
      if (alr_data->pch_data.tmsi EQ tmsi)
      {
        /*
         * TMSI matches
         */
        ma_pch_paging_ind (4, (UBYTE)((channel_needed & 0x30)>>4));
        return;
      }
    }
#ifdef GPRS
    if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+4] << (8*(3-i));
      if(gprs_alr_check_ptmsi(tmsi)) return;
    }
#endif

    /*
     * check mobile identity 2 only if TMSI is available
     */
    if (alr_data->pch_data.v_tmsi)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+8] << (8*(3-i));
      if (alr_data->pch_data.tmsi EQ tmsi)
      {
        /*
         * TMSI matches
         */
        ma_pch_paging_ind (4, (UBYTE)((channel_needed & 0xC0)>>6));
        return;
      }
    }
#ifdef GPRS
    if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+8] << (8*(3-i));
      if(gprs_alr_check_ptmsi(tmsi)) return;
    }
#endif
    /*
     * calculation of channel needed for
     * mobile identity 3 and 4 from the rest octet.
     */
    channel_needed = frame [ 20 ];
    if (channel_needed & 0x80)
      channel_needed = (UBYTE)((channel_needed & 0x78) >> 3);
    else
      channel_needed = 0;
    /*
     * check mobile identity 3 only if TMSI is available
     */
    if (alr_data->pch_data.v_tmsi)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+12] << (8*(3-i));
      if (alr_data->pch_data.tmsi EQ tmsi)
      {
        /*
         * TMSI matches
         */
        ma_pch_paging_ind (4, (UBYTE)(channel_needed >> 2));
        return;
      }
#ifdef GPRS
    if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+12] << (8*(3-i));
      if(gprs_alr_check_ptmsi(tmsi)) return;
    }
#endif
    }
    /*
     * check mobile identity 4 only if TMSI is available
     */
    if (alr_data->pch_data.v_tmsi)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+16] << (8*(3-i));
      if (alr_data->pch_data.tmsi EQ tmsi)
      {
        /*
         * TMSI matches
         */
        ma_pch_paging_ind (4, (UBYTE)(channel_needed & 3));
        return;
      }
    }
#ifdef GPRS
    if(GET_STATE(STATE_MA) NEQ MA_CON_EST)
    {
      tmsi = 0L;
      for (i=0;i<4;i++)
        tmsi += frame[i+16] << (8*(3-i));
      if(gprs_alr_check_ptmsi(tmsi)) return;
    }
#endif
  }
}

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

  PURPOSE : The function stops paging.

*/
GLOBAL void pch_stop(void)
{
  if(GET_STATE(STATE_PCH) EQ PCH_ACTIVE)
  {
    SET_STATE(STATE_PCH,PCH_NULL);
    if(alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG OR
       alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG_CS)
    {
      ALR_TRACE_PCH ("stop sc for pch");
      ma_stop_scell_bcch_req();
    }

    ma_pch_stop();
  }
}

/*used for S13 - to stop BCCH reading*/

GLOBAL UBYTE pch_mode_reorg(void)
{
  if(alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG OR
     alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG_CS OR
     alr_data->pch_data.pl_idle.page_mode EQ PGM_REORG_NC_SYNC)
    return TRUE;
  else
    return FALSE;
}
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_PCH                    |
| STATE   : code                ROUTINE : pch_no_of_paging_blocks    |
+--------------------------------------------------------------------+

  PURPOSE : The function configures the paging reading during cell
            reselection when reveived a SI3
*/

LOCAL SHORT pch_no_of_paging_blocks (UBYTE ccch_conf,
                                     UBYTE bs_ag_blks_res,
                                     UBYTE bs_pa_mfrms)
{
  /* in according to GSM 4.08 section 10.5.2.11, table 10.5.33 */
  if (ccch_conf EQ COMB_CCCH_COMB)
  {
    /*
     * combined CCCH,
     *
     * number of paging blocks = (3 - BS_AG_BLKS_RES) * BS_PA_MFRMS
     *
     * Maximum function only for security reasons, BCCH coding range is 0..7,
     * but allowed is only 0..2.
     */
    return (( (1 >  (UBYTE)(3 - bs_ag_blks_res) ? 1 : (UBYTE)(3 - bs_ag_blks_res)) ) *
            ((UBYTE)(2 + bs_pa_mfrms)));
  }
  else
  {
    /*
     * non-combined CCCH,
     *
     * number of paging blocks = (9 - BS_AG_BLKS_RES) * BS_PA_MFRMS
     */
    return ((9 - bs_ag_blks_res) *
            (2 + bs_pa_mfrms));
  }
}
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6103)       MODULE  : ALR_PCH                    |
| STATE   : code                ROUTINE : pch_config_resel           |
+--------------------------------------------------------------------+

  PURPOSE : The function configures the L1 PCH reading during cell
            reselection when reveived an SI3
*/

GLOBAL void pch_config_resel (T_MPHC_DATA_IND *data_ind)
{
  UBYTE *p_ctrl = &(data_ind->l2_frame.content[SI_CONTENTS_MSG_T+8]);
  UBYTE ccch_conf,
        bs_ag_blks_res,
        bs_pa_mfrms,
        pg,
        pag_blocks_per_mfr;
  SHORT n, b;
  PALLOC(pl_idle, MPHC_START_CCCH_REQ);
  memset(pl_idle, 0, sizeof(T_MPHC_START_CCCH_REQ));

  bs_ag_blks_res = (UBYTE)(((*p_ctrl) & 0x38) >> 3);
  ccch_conf      = (((*p_ctrl) & 0x07) EQ 0x01) ? COMB_CCCH_COMB : COMB_CCCH_NOT_COMB;
  p_ctrl++;
  bs_pa_mfrms    = (UBYTE)(((*p_ctrl) & 0x07));
/*  TRACE_EVENT_P5("pch_config_resel IE: %02x %02x bs_ag_blks_res=%u ccch_conf=%u bs_pa_mfrms=%u",
                  data_ind->l2_frame.content[SI_CONTENTS_MSG_T+8],
                  *p_ctrl,
                  bs_ag_blks_res,
                  ccch_conf,
                  bs_pa_mfrms);*/

  n = pch_no_of_paging_blocks (ccch_conf, bs_ag_blks_res, bs_pa_mfrms);
  b = ((ccch_conf / 2) + 1) * n;
  pg = (UBYTE) ((alr_data->pch_data.imsi_mod_1000 % b) % n);
  pag_blocks_per_mfr = PAG_BLOCK_TABLE [ccch_conf][bs_ag_blks_res];

  pl_idle->bs_ag_blks_res   = bs_ag_blks_res;
  pl_idle->bs_pa_mfrms      = (UBYTE)(bs_pa_mfrms + 2);
  pl_idle->bcch_combined    = ccch_conf;
  pl_idle->ccch_group       = (UBYTE) ((alr_data->pch_data.imsi_mod_1000 % b) / n);
  pl_idle->page_group       = pg;
  pl_idle->page_block_index = (UBYTE)(pg % pag_blocks_per_mfr);
  pl_idle->page_mode        = (alr_data->pch_data.saved_page_mode EQ PGM_REORG_CS)
                              ? PGM_REORG
                              : alr_data->pch_data.saved_page_mode;

/*  TRACE_EVENT_P3("n=%u b=%u page_mode=%u", n, b, pl_idle->page_mode);*/
  ma_pch_start_ccch_req(pl_idle);

  alr_data->pch_data.si3_read = TRUE;
}
#endif