diff src/g23m-gprs/grr/grr_pgf.c @ 1:fa8dc04885d8

src/g23m-*: import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 16 Oct 2020 06:25:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/g23m-gprs/grr/grr_pgf.c	Fri Oct 16 06:25:50 2020 +0000
@@ -0,0 +1,1195 @@
+/*
++-----------------------------------------------------------------------------
+|  Project :  GPRS (8441)
+|  Modul   :  GRR
++-----------------------------------------------------------------------------
+|  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 module implements local functions for service PG of
+|             entity GRR.
++-----------------------------------------------------------------------------
+*/
+
+#ifndef GRR_PGF_C
+#define GRR_PGF_C
+#endif
+
+#define ENTITY_GRR
+
+/*==== INCLUDES =============================================================*/
+
+#include "typedefs.h"    /* to get Condat data types */
+
+#include "vsi.h"        /* to get a lot of macros */
+#include "macdef.h"
+#include "gprs.h"
+#include "gsm.h"        /* to get a lot of macros */
+#include "ccdapi.h"     /* to get CCD API */
+#include "cnf_grr.h"    /* to get cnf-definitions */
+#include "mon_grr.h"    /* to get mon-definitions */
+#include "prim.h"       /* to get the definitions of used SAP and directions */
+#include "message.h"
+#include "grr.h"        /* to get the global entity definitions */
+
+#include "grr_f.h"      /* */
+#include "grr_pgf.h"    /* */
+
+#include "grr_ctrls.h"    /* */
+#include "grr_meass.h"
+#include "grr_css.h"
+#include "grr_em.h"     /*for Engineering mode*/
+
+#include <string.h>     /* for memcpy*/
+#include <stdio.h>     /* for memcpy*/
+
+/*==== CONST ================================================================*/
+
+const USHORT NC2_NON_DRX_TABLE[8] = {0, 240, 480, 720, 960, 1200, 1440, 1920};
+
+/*==== LOCAL VARS ===========================================================*/
+static UBYTE NON_DRX_TABLE[] = {0, 1, 2, 4, 8, 16, 32, 64};
+
+/*==== GLOBAL VARS ===========================================================*/
+
+
+
+/*==== PRIVATE FUNCTIONS ====================================================*/
+LOCAL void pg_stop_t_nc2_ndrx( void );
+LOCAL void pg_stop_non_drx_timer( void );
+LOCAL void pg_start_non_drx_timer(USHORT milliseconds);
+LOCAL BOOL pg_decode_imsi(T_ms_id ms_id, UBYTE *digits, UBYTE *nr_digits);
+/*==== PUBLIC FUNCTIONS =====================================================*/
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_init
++------------------------------------------------------------------------------
+| Description : The function pg_init() ....
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_init ( void )
+{
+  TRACE_FUNCTION( "pg_init" );
+
+  pg_init_params();
+
+  grr_data->pg.v_emlpp_prio = FALSE; /* valid flag for emlpp_priority*/
+  grr_data->pg.packet_mode  = PACKET_MODE_NULL;
+
+  INIT_STATE(PG,PG_NULL);
+} /* pg_init() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_init_params
++------------------------------------------------------------------------------
+| Description : The function pg_init_params() ....
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_init_params ( void )
+{
+  TRACE_FUNCTION( "pg_init_params" );
+
+  psc_db->last_pg_mode = psc_db->network_pg_mode = REORG_PAGING;
+
+} /* pg_init_params() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_process_pp_req
++------------------------------------------------------------------------------
+| Description : The function pg_process_pp_req() ....
+|
+| Parameters  : T_D_PAGING_REQ *paging_req: pointer to the decoded message,
+                UBYTE state: state of pg state machine: IDLE, TRANSFER, TIMER_3172
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_process_pp_req ( T_D_PAGING_REQ *paging_req, UBYTE state )
+{
+  char i;
+  ULONG ul;
+
+  TRACE_FUNCTION( "pg_process_pp_req" );
+
+  if(paging_req->v_pers_lev)
+  { /*copy persistence level parameters  into database*/
+    grr_save_persistence_level (&(paging_req->pers_lev));
+  }
+  /*if(paging_req->v_nln)
+  { message contains at least one page request for RR connection establishment
+    04.08: NLN(PCH) Notification List Number
+    "The presence of the NLN(PCH) field indicates that if an NCH is present, reduced NCH monitoring can be used, and
+    gives the NLN(PCH) value, to be used as specified in 3.3.3."
+    This is used by VGCS MSs...
+
+  }*/
+  if(paging_req->rep_page_info_trnc_grp.v_rep_page_info)
+  {
+    for (i=0; i < paging_req->rep_page_info_trnc_grp.c_rep_page_info; i++)
+    {
+      if(paging_req->rep_page_info_trnc_grp.rep_page_info[i].v_rep_page_s1
+          AND
+         grr_t_status( T3172_1 ) EQ 0 /* T3172 is not running*/
+        )
+      {
+        /*
+         * page request for TBF establishment
+         */
+        switch(state)
+        {
+          case PG_IDLE:
+            if(paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s1.v_ptmsi)
+            {
+              /*
+               *Paging with PTMSI
+               */
+              if(grr_data->db.ms_id.new_ptmsi NEQ GMMRR_TMSI_INVALID)
+              {
+                if(grr_check_ptmsi(&(paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s1.ptmsi)))
+                {
+                  TRACE_EVENT("Paging with PTIMSI for TBF");
+                  sig_pg_ctrl_downlink_transfer ( GMMRR_PTMSI );
+                  i = paging_req->rep_page_info_trnc_grp.c_rep_page_info; /* break condition for for loop*/
+                }
+                else
+                {
+                  TRACE_EVENT("Paging with wrong PTIMSI for TBF: IGNORE");
+                }
+              }
+              else
+              {
+                TRACE_EVENT("Paging with PTMSI, but no valid PTMSI in MS");
+              }
+            }
+            else
+            {
+              /*
+               *Paging with Mobile Identity (IMSI)
+               */
+              UBYTE dummy_digits[MAX_IMSI_DIGITS];
+              UBYTE dummy_nr_digits = 0;
+
+              if(pg_decode_imsi(paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s1.ms_id,
+                dummy_digits, &dummy_nr_digits))
+              {
+
+                 if( dummy_nr_digits
+                      EQ
+                      grr_data->db.ms_id.imsi.number_of_digits)/* check the number of digits*/
+                  {
+                    if( !memcmp
+                        ( grr_data->db.ms_id.imsi.digit, /* IMSI stored in the data_base*/
+                          dummy_digits, /* received IMSI*/
+                          grr_data->db.ms_id.imsi.number_of_digits
+                        )
+                      )
+                    {
+                        /* The MS was paged with the IMSI*/
+                      TRACE_EVENT("Paging with Mobile Identity (IMSI) for TBF");
+                      sig_pg_ctrl_downlink_transfer ( GMMRR_IMSI );
+                      i = paging_req->rep_page_info_trnc_grp.c_rep_page_info; /* break condition for for loop*/
+                    }
+                    else
+                    {
+                      TRACE_EVENT("Paging with wrong IMSI for TBF: IGNORE");
+                    }
+
+                  }
+              }
+            }
+            break;
+          default:
+            /*
+             * ignore paging request for TBF establishment, do
+             * not check the content
+             */
+            break;
+        }
+      }
+
+      if(paging_req->rep_page_info_trnc_grp.rep_page_info[i].v_rep_page_s2)
+      {
+        /*
+         * page request for RR connection establishment
+         */
+        /*
+         * In packet access mode, class A or B shall respond to a pp_req for
+         * RR connection est.
+         * During the time T3172 is running, the mobile station shall ignore all
+         * received PACKET PAGING REQUEST messages except paging request to trigger
+         * RR connection establishment.
+         */
+        switch(state)
+        {
+          case PG_IDLE:
+          case PG_TRANSFER:
+          case PG_ACCESS:
+            if(paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.v_tmsi_field)
+            {
+              /*
+               *Paging with TMSI
+               */
+              if(grr_data->db.ms_id.tmsi NEQ GMMRR_TMSI_INVALID)
+              {
+                ul = grr_buffer2ulong((BUF_ptmsi *)&(paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.tmsi_field));
+                if(ul EQ grr_data->db.ms_id.tmsi)
+                {
+                  TRACE_EVENT("Paging with Mobile Identity (TMSI) for RR est");
+
+                  /* Set some parameters needed  RR connection establishment*/
+                  grr_data->pg.ch_needed = paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.chan_need;
+                  grr_data->pg.v_emlpp_prio = paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.v_emlpp_prio;
+                  grr_data->pg.emlpp_prio = paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.emlpp_prio;
+
+                   /* MS was paged with TMSI for RR connection establishment*/
+                  grr_data->pg.type = ID_TMSI;
+                  sig_pg_ctrl_rr_est_req ( PG_ON_PCCCH);
+                  i = paging_req->rep_page_info_trnc_grp.c_rep_page_info; /* break condition for for loop*/
+                }
+                else
+                {
+                  TRACE_EVENT("Paging with wrong TMSI for RR: IGNORE");
+                }
+              }
+              else
+              {
+                TRACE_EVENT("Paging with TMSI, but no valid TMSI in MS");
+              }
+            }
+            else
+            {
+              /*
+               *Paging with Mobile Identity (IMSI)
+               */
+              UBYTE dummy_digits[MAX_IMSI_DIGITS];
+              UBYTE dummy_nr_digits = 0;
+
+              if(pg_decode_imsi(paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.ms_id,
+                dummy_digits, &dummy_nr_digits))
+              {
+
+                 if( dummy_nr_digits
+                      EQ
+                      grr_data->db.ms_id.imsi.number_of_digits)/* check the number of digits*/
+                  {
+                    if( !memcmp
+                        ( grr_data->db.ms_id.imsi.digit, /* IMSI stored in the data_base*/
+                          dummy_digits, /* received IMSI*/
+                          grr_data->db.ms_id.imsi.number_of_digits
+                        )
+                      )
+                    {
+                        /* The MS was paged with the IMSI*/
+                      TRACE_EVENT("Paging with Mobile Identity (IMSI) for RR est");
+
+                      /* Set some parameters needed  RR connection establishment*/
+                      grr_data->pg.ch_needed = paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.chan_need;
+                      grr_data->pg.v_emlpp_prio = paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.v_emlpp_prio;
+                      grr_data->pg.emlpp_prio = paging_req->rep_page_info_trnc_grp.rep_page_info[i].rep_page_s2.emlpp_prio;
+
+                      /* MS was paged with IMSI for RR connection establishment*/
+                      grr_data->pg.type = ID_IMSI;
+                      sig_pg_ctrl_rr_est_req ( PG_ON_PCCCH );
+                      i = paging_req->rep_page_info_trnc_grp.c_rep_page_info; /* break condition for for loop*/
+                    }
+                    else
+                    {
+                      TRACE_EVENT("Paging with wrong IMSI for RR: IGNORE");
+                    }
+                }
+              }
+            }
+            break;
+        default:
+            /*
+             * ignore paging request for RR connection establishment, do
+             * not check the content
+             */
+            break;
+        }
+      }
+    }
+  }
+} /* pg_process_pp_req() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_send_stop
++------------------------------------------------------------------------------
+| Description : The function pg_send_stop() ....
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_send_stop ( void )
+{
+  TRACE_FUNCTION( "pg_send_stop" );
+
+  if (grr_data->is_pg_started EQ TRUE)
+  {
+    PALLOC (mphp_stop_pccch_req, MPHP_STOP_PCCCH_REQ);
+    PSEND (hCommL1, mphp_stop_pccch_req);
+  }
+  grr_data->is_pg_started = FALSE;
+
+  pg_stop_non_drx_timer();
+  pg_stop_t_nc2_ndrx();
+
+} /* pg_send_stop() */
+
+
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_send_start
++------------------------------------------------------------------------------
+| Description : The function pg_send_start() ....
+|
+| Parameters  : UBYTE page_mode
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_send_start ( UBYTE page_mode )
+{
+  UBYTE  pccch_group; /* 0 .. KC-1*/
+  USHORT dummy;
+
+  TRACE_FUNCTION( "pg_send_start" );
+
+  if(grr_is_non_drx_period())
+    page_mode = REORG_PAGING;
+
+  switch(page_mode)
+  {
+    case  NORMAL_PAGING:
+    case  EXT_PAGING:
+    case  REORG_PAGING:
+    case  SAME_PAGING:
+      break;/*O.K.*/
+    default:
+      /* same as before: nothing to do return*/
+      TRACE_EVENT("page_mode is corrupt: assume same as before: return");
+      return;
+  }
+
+  if(psc_db->paging_group.kc EQ 0)
+  {
+    /*
+     * No PCCCH description: return
+     */
+    TRACE_ERROR("psc_db->paging_group.kc EQ 0");
+    return;
+  }
+
+  {
+    PALLOC( start_pccch, MPHP_START_PCCCH_REQ );
+
+    start_pccch->imsi_mod = grr_imsi_mod( );
+    pccch_group           = ( UBYTE )( start_pccch->imsi_mod % psc_db->paging_group.kc );
+
+    if( !grr_get_pccch_freq_par(  pccch_group,
+                                 &start_pccch->p_ch_des.p_chan_sel,
+                                 &start_pccch->p_freq_list ) )
+    {
+      PFREE( start_pccch );
+
+      return;
+    }
+
+    start_pccch->kcn             = psc_db->paging_group.kc;     /*<  KC multiplied by N             */
+    start_pccch->split_pg_cycle  = grr_data->ms.split_pg_cycle; /*<  Split paging cycle             */
+    start_pccch->bs_pag_blks_res = psc_db->pccch.bs_pag_blks;   /*<  No paging no PBCCH             */
+    start_pccch->bs_pbcch_blks   = psc_db->pccch.bs_pbcch_blks; /*<  Number of PBCCH per multiframe */
+    start_pccch->pb              = psc_db->g_pwr_par.pb;        /*<  Power reduction value          */
+    start_pccch->p_ch_des.tsc    = psc_db->paging_group.pccch[pccch_group].tsc; /*<  Training sequence code   */
+    start_pccch->p_ch_des.tn     = psc_db->paging_group.pccch[pccch_group].tn;
+
+    if( start_pccch->p_ch_des.p_chan_sel.hopping )
+    {
+      TRACE_EVENT_P8( "Hopping PCCCH - ma_num:%d hsn/maio:%ld tsc:%d tn:%d f1:%d f2:%d f3:%d f4:%d",
+                      psc_db->paging_group.pccch[pccch_group].ma_num,
+                      start_pccch->p_ch_des.p_chan_sel.p_rf_ch.arfcn,
+                      start_pccch->p_ch_des.tsc,
+                      start_pccch->p_ch_des.tn,
+                      start_pccch->p_freq_list.p_rf_chan_no.p_radio_freq[0],
+                      start_pccch->p_freq_list.p_rf_chan_no.p_radio_freq[1],
+                      start_pccch->p_freq_list.p_rf_chan_no.p_radio_freq[2],
+                      start_pccch->p_freq_list.p_rf_chan_no.p_radio_freq[3] );
+    }
+    else
+    {
+      TRACE_EVENT_P3( "Static PCCCH - arfcn:%ld tsc:%d tn:%d",
+                      start_pccch->p_ch_des.p_chan_sel.p_rf_ch.arfcn,
+                      start_pccch->p_ch_des.tsc,
+                      start_pccch->p_ch_des.tn );
+
+      start_pccch->p_ch_des.p_chan_sel.p_rf_ch.arfcn =
+        grr_g23_arfcn_to_l1( start_pccch->p_ch_des.p_chan_sel.p_rf_ch.arfcn );
+    }
+
+    /*
+     * Calculate downlink signalling counter
+     */
+       /* DRX period is used, there is a valid SPILT_PG_CYCLE value */
+    dummy = grr_data->ms.split_pg_cycle;
+    
+
+    /* Downlink signalling counter*/
+    dummy = ( ( 90 * dummy ) / 64 < 10 ) ? 10 : ( 90 * dummy ) / 64;
+
+    if(!grr_data->is_pg_started)
+    {
+      grr_data->pg.initial_dsc = dummy;
+      grr_data->pg.dsc         = dummy;
+
+      GRR_EM_SET_DSC_VAL(dummy);
+
+    }
+    if( grr_data->pg.is_l1_ref_tn_changed NEQ psc_db->paging_group.pccch[pccch_group].tn )
+    {
+      sig_pg_cs_pause( );
+    }
+
+    /*Read PCCCH */
+    {
+      start_pccch->page_mode =  page_mode;
+      PSEND(hCommL1, start_pccch);
+    }
+    if( grr_data->pg.is_l1_ref_tn_changed NEQ psc_db->paging_group.pccch[pccch_group].tn  )
+    {
+      sig_pg_cs_resume( );
+
+      grr_data->pg.is_l1_ref_tn_changed = psc_db->paging_group.pccch[pccch_group].tn;
+    }
+
+    psc_db->last_pg_mode = page_mode;
+
+    switch(page_mode)
+    {
+      case  NORMAL_PAGING:
+        TRACE_EVENT("page mode NORMAL");
+        break;/*O.K.*/
+      case  EXT_PAGING:
+        TRACE_EVENT("page mode EXT");
+        break;/*O.K.*/
+      case  REORG_PAGING:
+        TRACE_EVENT("page mode REORG");
+        break;/*O.K.*/
+      case  SAME_PAGING:
+        TRACE_EVENT("page mode SAME");
+        break;/*O.K.*/
+    }
+
+  }
+  grr_data->is_pg_started = TRUE;
+
+} /* pg_send_start() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_start_non_drx_timer
++------------------------------------------------------------------------------
+| Description : The function pg_start_non_drx_timer() starts the timer for
+|               non DRX period
+|
+| Parameters  : USHORT milliseconds
+|
++------------------------------------------------------------------------------
+*/
+LOCAL void pg_start_non_drx_timer(USHORT milliseconds)
+{
+  TRACE_FUNCTION( "pg_start_non_drx_timer");
+
+  psc_db->non_drx_timer_running = TRUE;
+  vsi_t_start(GRR_handle,T_TRANS_NON_DRX, milliseconds);
+}/* pg_start_non_drx_timer*/
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_stop_non_drx_timer
++------------------------------------------------------------------------------
+| Description : The function pg_stop_non_drx_timer() stops the timer for
+|               non DRX period
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_stop_non_drx_timer(void)
+{
+  TRACE_FUNCTION( "pg_stop_non_drx_timer");
+
+  if(psc_db->non_drx_timer_running)
+  {
+    psc_db->non_drx_timer_running = FALSE;
+    vsi_t_stop(GRR_handle,T_TRANS_NON_DRX);
+  }
+}/* pg_stop_non_drx_timer*/
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_start_t_nc2_ndrx
++------------------------------------------------------------------------------
+| Description : ...
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_start_t_nc2_ndrx( UBYTE nc_non_drx_period )
+{ 
+  TRACE_FUNCTION( "pg_start_t_nc2_ndrx");
+  
+  if( nc_non_drx_period NEQ 0 )
+  {
+    USHORT nc2_drx_period = NC2_NON_DRX_TABLE[nc_non_drx_period];
+    
+    psc_db->nc2_non_drx_period_running = TRUE;
+    
+    vsi_t_start( GRR_handle, T_NC2_NON_DRX, nc2_drx_period );
+
+    TRACE_EVENT_P1( "Timer T_NC2_NON_DRX started: %d",  nc2_drx_period );
+  }
+}/* pg_start_t_nc2_ndrx */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_stop_t_nc2_ndrx
++------------------------------------------------------------------------------
+| Description : ...
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_stop_t_nc2_ndrx( void )
+{
+  TRACE_FUNCTION( "pg_stop_t_nc2_ndrx" );
+
+  if( psc_db->nc2_non_drx_period_running EQ TRUE )
+  {
+    psc_db->nc2_non_drx_period_running = FALSE;
+  
+    vsi_t_stop( GRR_handle, T_NC2_NON_DRX );
+
+    TRACE_EVENT( "Timer T_NC2_NON_DRX stopped" );
+  }
+} /* pg_stop_t_nc2_ndrx */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_handle_non_drx_timers
++------------------------------------------------------------------------------
+| Description : ...
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_handle_non_drx_timers(void)
+{
+  USHORT non_drx_timer = 0;
+  TRACE_FUNCTION("pg_handle_non_drx_timers");
+
+  non_drx_timer = ( psc_db->non_drx_timer < psc_db->gprs_cell_opt.drx_t_max ) ?
+                    psc_db->non_drx_timer : psc_db->gprs_cell_opt.drx_t_max;
+
+  non_drx_timer = (USHORT)(NON_DRX_TABLE[non_drx_timer]*1000);/* in milliseconds*/
+
+  pg_start_non_drx_timer(non_drx_timer);
+
+}/* pg_handle_non_drx_timers */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_non_drx
++------------------------------------------------------------------------------
+| Description : Handles the timer of non DRX mode
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_non_drx ( void )
+{
+  TRACE_FUNCTION( "pg_non_drx" );
+
+  switch( GET_STATE( PG ) )
+  {
+    case PG_IDLE:
+      /*
+       * To ensure that the paging mode is not set to page normal in other states
+       * than packet idle mode, the function grr_is_packet_idle_mode is called in
+       * advance.
+       *
+       * State PG_IDLE not necessarily means that GRR is in packet idle mode. In
+       * case GRR is currently executing the RRGRR_STOP_TASK_REQ/CNF procedure,
+       * just the services CPAP and TC changes their states. All other services
+       * remain in state IDLE, which is not quite correct. A similar scenario
+       * occurs during execution of the MPHP_ASSIGNMENT_REQ/CON procedure.
+       */
+      if( grr_is_packet_idle_mode( ) EQ TRUE )
+      {
+        pg_handle_idle_mode( PTM_IDLE );
+      }
+      else
+      {
+        TRACE_EVENT( "pg_non_drx: Timeout T_TRANS_NON_DRX handled by next packet idle mode" );
+      }
+      break;
+    default:
+      TRACE_EVENT( "pg_non_drx: Timeout T_TRANS_NON_DRX handled by next PG_IDLE" );
+      break;
+  }
+}/* pg_non_drx*/
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_decode_imsi
++------------------------------------------------------------------------------
+| Description : Handles the timer of non DRX mode
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+LOCAL BOOL pg_decode_imsi(T_ms_id ms_id, UBYTE *digits, UBYTE *nr_digits)
+{
+  UBYTE odd_even_type, i;
+  BOOL is_odd;
+  TRACE_FUNCTION("pg_decode_imsi");
+
+  *nr_digits = 0;
+  if(ms_id.ms_id_len > 0)
+  {
+    odd_even_type = (ms_id.ident_digit[0] & 0x0f);
+
+    if(odd_even_type & 1) /* Check IMSI or not*/
+    {
+      is_odd = odd_even_type & 0x08; /* odd: ung.*/
+      digits[0] = (ms_id.ident_digit[0] >> 4);
+      *nr_digits = 1;
+
+      for(i=1; i< ms_id.ms_id_len; i++)
+      {
+        if(!is_odd AND (i EQ (ms_id.ms_id_len-1)))
+        {
+          digits[*nr_digits] = (ms_id.ident_digit[i] & 0x0f);
+          *nr_digits = (*nr_digits) + 1;
+        }
+        else
+        {
+          digits[*nr_digits] = (ms_id.ident_digit[i] & 0x0f);
+          *nr_digits = (*nr_digits) + 1;
+          digits[*nr_digits] = (ms_id.ident_digit[i] >> 4);
+          *nr_digits = (*nr_digits) + 1;
+        }
+      }
+    }
+    else
+    {
+      /*No IMSI, ignore*/
+      return FALSE;
+    }
+  }
+  else
+  {
+    /* No mobile identity: strange*/
+    return FALSE;
+  }
+  return TRUE;
+}/* pg_decode_imsi*/
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_handle_classB_NMO_III
++------------------------------------------------------------------------------
+| Description : Handles NMO III for Class B mobile
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_handle_classB_NMO_III(T_PG_TRANS transition)
+{
+  TRACE_FUNCTION("pg_handle_classB_NMO_III");
+
+  switch(transition)
+  {
+    case T3172_PTM:
+    case ACCESS_PTM:
+    case IDLE_PTM:
+      if(grr_data->ms.reverts_NMO_III)
+      {
+        /*
+         * The MS reverts to CC or CG in NMO III
+         * In this state IDLE->TRANSFER it can be only a CG mobile
+         * it shall stop PCCCH reading, if available
+         * it shall stop also CCCH reading, if running
+         */
+        TRACE_EVENT("IDLE/ACCESS->PTM: NMO III Class B reverts. Stop CCCH (and PCCCH)");
+        if(grr_is_pbcch_present())
+        {
+          /*
+           * Stop monitoring PCCCH
+           */
+          pg_send_stop();
+        }
+        /*
+         * Stop monitoring CCCH
+         */
+        sig_pg_ctrl_stop_mon_ccch();
+      }
+      else
+      {
+        /*
+         * This is non reverting Class B
+         * If PBCCH is present it should stop PCCCH and start CCCH
+         * else it should only monitor CCCH
+         */
+        TRACE_EVENT("IDLE/ACCESS->PTM: NMO III Class B. Start CCCH (and stop PCCCH)");
+        if(grr_is_pbcch_present())
+        {
+          /*
+           * Stop monitoring PCCCH
+           */
+          pg_send_stop();
+        }
+        /*
+         * Start monitoring CCCH (Normal Paging)
+         */
+        if( grr_data->ms.tbf_mon_ccch
+#ifdef REL99
+           AND
+           !psc_db->gprs_cell_opt.gprs_ext_bits.gprs_ext_info.bss_paging_coord
+#endif
+          )
+        {
+          sig_pg_ctrl_start_mon_ccch(PAG_MODE_PTM_NP);
+        }
+      }
+      break;
+    case NULL_IDLE:
+    case PTM_IDLE:
+      if(grr_data->ms.reverts_NMO_III)
+      {
+        /*
+         * The MS reverts to CC or CG in NMO III
+         * In this state TRANSFER/NULL->IDLE it can be only a CG mobile
+         * it shall start PCCCH reading, if available
+         * it shall start also CCCH reading, if NO PBCCH
+         */
+        TRACE_EVENT("PTM/NULL->IDLE: NMO III Class B reverts. Start (P) or CCCH");
+        if(grr_is_pbcch_present())
+        {
+          /*
+           * Start monitoring PCCCH
+           */
+          pg_send_start( psc_db->last_pg_mode );
+
+        }
+        else
+        {
+          /*
+           * Start monitoring CCCH (Normal Paging)
+           */
+          sig_pg_ctrl_start_mon_ccch(PAG_MODE_DEFAULT);
+        }
+      }
+      else
+      {
+        /*
+         * This is non reverting Class B
+         * If PBCCH is present it should start PCCCH and start CCCH
+         * else it should only monitor CCCH
+         */
+        TRACE_EVENT("PTM/NULL->IDLE: NMO III Class B. Start (P) and CCCH");
+        if(grr_is_pbcch_present())
+        {
+          /*
+           * Start monitoring PCCCH
+           */
+          pg_send_start( psc_db->last_pg_mode );
+          /*
+           * Start monitoring CCCH (Normal Paging), before doing this use PAG_MODE_PIM_NP
+           * to enter later to idle mode
+           * Exceptional situation: if NON DRX is used, REORG is only for PCCCH
+           */
+          sig_pg_ctrl_start_mon_ccch(PAG_MODE_PIM_NP);
+        }
+        else
+        {
+          /*
+           * Start monitoring CCCH (Normal Paging)
+           * Normal Paging
+           */
+          sig_pg_ctrl_start_mon_ccch(PAG_MODE_DEFAULT);
+        }
+      }
+      break;
+    case ACCESS_IDLE:
+      if(grr_data->ms.reverts_NMO_III)
+      {
+        /*
+         * The MS reverts to CC or CG in NMO III
+         * In this state ACCESS->IDLE it can be only a CG mobile
+         * it shall start PCCCH reading, if available
+         * it shall start CCCH reading, if NO PBCCH
+         */
+        TRACE_EVENT("ACCESS->IDLE: NMO III Class B reverts. Start (P) or CCCH");
+        if(!grr_is_pbcch_present())
+        {
+          /*
+           * Start monitoring CCCH (Normal Paging)
+           */
+          sig_pg_ctrl_start_mon_ccch(PAG_MODE_DEFAULT);
+        }
+      }
+      else
+      {
+        /*
+         * This is non reverting Class B
+         * If PBCCH is present it should start PCCCH and start CCCH
+         * else it should only monitor CCCH
+         */
+        TRACE_EVENT("ACCESS->IDLE: NMO III Class B. Start (P) and CCCH");
+        if(grr_is_pbcch_present())
+        {
+          /*
+           * Start monitoring PCCCH
+           */
+          pg_send_start( psc_db->last_pg_mode );
+          /*
+           * Start monitoring CCCH (Normal Paging)
+           * Exceptional situation: if NON DRX is used, we RORG is only for PCCCH
+           */
+          if(grr_data->ms.tbf_mon_ccch
+#ifdef REL99
+              AND
+             !psc_db->gprs_cell_opt.gprs_ext_bits.gprs_ext_info.bss_paging_coord
+#endif
+            )
+          {
+            sig_pg_ctrl_start_mon_ccch(PAG_MODE_PTM_NP);
+          }
+        }
+        else
+        {
+          /*
+           * Start monitoring CCCH (Normal Paging)
+           * Normal Paging
+           */
+          sig_pg_ctrl_start_mon_ccch(PAG_MODE_DEFAULT);
+        }
+      }
+      break;
+    case PTM_ACCESS:
+    case IDLE_ACCESS:
+      /*
+       * Not necessary
+       */
+      break;
+  }/*switch*/
+}
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_handle_access_mode
++------------------------------------------------------------------------------
+| Description : Handles the paging when entering the packet access mode.
+|
+| Parameters  : void
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_handle_access_mode ( void )
+{
+  TRACE_FUNCTION( "pg_handle_access_mode" );
+
+
+  if( grr_is_pbcch_present( ) )
+  {
+    if( !grr_data->is_pg_started OR psc_db->last_pg_mode NEQ REORG_PAGING )
+    {
+      /*
+       * enable the L1 reading USF values
+       */
+      pg_send_start( PG_REORG );
+    }
+  }
+  else
+  {
+    /*
+     * start monitoring CCCH
+     */
+    sig_pg_ctrl_start_mon_ccch( PAG_MODE_REORG );
+  }
+} /* pg_handle_access_mode() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_set_l1_ref_tn
++------------------------------------------------------------------------------
+| Description : The function pg_set_l1_ref_tn () ....
+|
+| Parameters  :
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_set_l1_ref_tn ( UBYTE* out_tn, UBYTE in_tn )
+{
+  TRACE_FUNCTION( "pg_set_l1_ref_tn" );
+
+  if( in_tn NEQ *out_tn )
+  {
+    grr_data->pg.is_l1_ref_tn_changed = TRUE;
+  }
+
+  *out_tn = in_tn;
+
+} /* pg_set_l1_ref_tn */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_handle_idle_mode
++------------------------------------------------------------------------------
+| Description :
+|
+| Parameters  :
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_handle_idle_mode ( T_PG_TRANS transition )
+{
+  TRACE_FUNCTION( "pg_handle_idle_mode" );
+
+  switch( grr_data->pg.nmo )
+  {
+    case GMMRR_NET_MODE_I:
+      /*
+       * NMOI:
+       * ====
+       * Paging on ( CCCH or PCCCH ) and PACCH, GPRS attached MS should
+       * monitor one paging channel, so we have to stop monitoring CCCH
+       * channel if PCCCH is present.
+       */
+      if( grr_is_pbcch_present( ) )
+      {
+        pg_send_start( psc_db->last_pg_mode );
+      }
+      else
+      {
+        sig_pg_ctrl_start_mon_ccch( PAG_MODE_DEFAULT );
+      }
+      break;
+
+    case GMMRR_NET_MODE_II:
+      /*
+       * NMOII:
+       * =====
+       * Paging on CCCH, paging for both circuit switched and packet data
+       * will be sent on CCCH, PCCCH has been not allocated in the cell.
+       */
+      switch( grr_data->ms.class_of_mode )
+      {
+        case GMMRR_CLASS_A:
+/* BEGIN GRR_NMO_III */
+        case GMMRR_CLASS_B:
+/* END   GRR_NMO_III */
+        case GMMRR_CLASS_BG:
+        case GMMRR_CLASS_BC:
+        case GMMRR_CLASS_CG:
+        case GMMRR_CLASS_CC:
+          sig_pg_ctrl_start_mon_ccch( PAG_MODE_DEFAULT );
+        break;
+      }
+      break;
+
+    case GMMRR_NET_MODE_III:
+      /*
+       * NMOIII:
+       * =======
+       * If PBCCH not present paging for both circuit switched and packet
+       * data will be sent on CCCH,
+       * if PBCCH is present paging for circuit switched data is sent on
+       * CCCH and for packet data on PCCCH.
+       */
+      if( grr_is_pbcch_present( ) )
+      {
+        TRACE_EVENT( "GMMRR_NET_MODE_III PBCCH" );
+
+        switch(grr_data->ms.class_of_mode)
+        {
+          case GMMRR_CLASS_A:
+            pg_send_start( psc_db->last_pg_mode );
+            sig_pg_ctrl_start_mon_ccch( PAG_MODE_PIM_NP );
+            break;
+
+          /*
+           * We need to check whether the MS class B  mobile
+           * reverts to CG or CC in NMO III. If not it should monitor PCCCH and CCCH
+           * else it needs to monitor either CCCH or PCCCH
+           */
+/* BEGIN GRR_NMO_III */
+          case GMMRR_CLASS_B:
+/* END   GRR_NMO_III */
+          case GMMRR_CLASS_BG:
+          case GMMRR_CLASS_BC:
+            pg_handle_classB_NMO_III( transition );
+            break;
+
+          case GMMRR_CLASS_CG:
+            sig_pg_ctrl_stop_mon_ccch( );
+            pg_send_start( psc_db->last_pg_mode );
+            break;
+
+          case GMMRR_CLASS_CC:
+            sig_pg_ctrl_start_mon_ccch( PAG_MODE_DEFAULT );
+            break;
+         }
+       }
+       else
+       {
+        TRACE_EVENT ( "GMMRR_NET_MODE_III BCCH" );
+
+        sig_pg_ctrl_start_mon_ccch( PAG_MODE_DEFAULT );
+      }
+      break;
+  }
+} /* pg_handle_idle_mode() */
+
+/*
++------------------------------------------------------------------------------
+| Function    : pg_handle_transfer_mode
++------------------------------------------------------------------------------
+| Description :
+|
+| Parameters  :
+|
++------------------------------------------------------------------------------
+*/
+GLOBAL void pg_handle_transfer_mode ( T_PG_TRANS transition )
+{
+  TRACE_FUNCTION( "pg_handle_transfer_mode" );
+
+  switch( grr_data->pg.nmo )
+  {
+    case GMMRR_NET_MODE_I:
+      /*
+       * NMOI:
+       * ====
+       * Paging on ( CCCH or PCCCH ) and PACCH, GPRS attached MS should
+       * monitor one paging channel, so we have to stop monitoring CCCH
+       * channel if PCCCH is present.
+       */
+      if( grr_is_pbcch_present( ) )
+      {
+        pg_send_stop( );
+      }
+      else
+      {
+        sig_pg_ctrl_stop_mon_ccch( );
+      }
+      break;
+
+    case GMMRR_NET_MODE_II:
+      /*
+       * NMOII:
+       * =====
+       * Paging on CCCH, paging for both circuit switched and packet data
+       * will be sent on CCCH, PCCCH has been not allocated in the cell.
+       *
+       * The MS should monitor CCCH except for the CLASS_CG.
+       */
+      switch( grr_data->ms.class_of_mode )
+      {
+        case GMMRR_CLASS_A:
+/* BEGIN GRR_NMO_III*/
+        case GMMRR_CLASS_B:
+/* END   GRR_NMO_III*/
+        case GMMRR_CLASS_BG:
+        case GMMRR_CLASS_BC:
+          if( grr_data->ms.tbf_mon_ccch 
+#ifdef REL99
+             AND
+             !psc_db->gprs_cell_opt.gprs_ext_bits.gprs_ext_info.bss_paging_coord
+#endif			
+            )
+          {
+            sig_pg_ctrl_start_mon_ccch( PAG_MODE_PTM_NP );
+          }
+          else
+          {
+            sig_pg_ctrl_stop_mon_ccch( );
+          }
+          break;
+
+        case GMMRR_CLASS_CG:
+          sig_pg_ctrl_stop_mon_ccch( );
+          break;
+      }
+      break;
+
+    case GMMRR_NET_MODE_III:
+      /*
+       * NMOIII:
+       * =======
+       * If PBCCH not present paging for both circuit switched and packet
+       * data will be sent on CCCH,
+       * if PBCCH is present paging for circuit switched data is sent on
+       * CCCH and for packet data on PCCCH.
+       */
+      switch( grr_data->ms.class_of_mode )
+      {
+        case GMMRR_CLASS_A:
+          if( grr_is_pbcch_present( ) )
+          {
+            pg_send_stop( );
+          }
+
+          sig_pg_ctrl_start_mon_ccch( PAG_MODE_PTM_NP );
+          break;
+
+/* BEGIN GRR_NMO_III*/
+        case GMMRR_CLASS_B:
+/* END   GRR_NMO_III*/
+        case GMMRR_CLASS_BG:
+        case GMMRR_CLASS_BC:
+          pg_handle_classB_NMO_III( transition );
+          break;
+
+        case GMMRR_CLASS_CG:
+          if( grr_is_pbcch_present( ) )
+          {
+            pg_send_stop( );
+          }
+          else
+          {
+            sig_pg_ctrl_stop_mon_ccch( );
+          }
+          break;
+      }
+      break;
+  }
+} /* pg_handle_transfer_mode() */