root/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. copy_cgPtyNum_from_sngss7
  2. copy_cgPtyNum_to_sngss7
  3. copy_cdPtyNum_from_sngss7
  4. copy_cdPtyNum_to_sngss7
  5. copy_tknStr_from_sngss7
  6. check_for_state_change
  7. check_cics_in_range
  8. extract_chan_data
  9. check_for_reset
  10. get_unique_id
  11. check_if_rx_grs_started
  12. check_if_rx_grs_processed
  13. check_if_rx_gra_started
  14. check_for_res_sus_flag
  15. process_span_ucic
  16. clear_rx_grs_flags
  17. clear_rx_grs_data
  18. clear_rx_gra_data
  19. clear_tx_grs_flags
  20. clear_tx_grs_data
  21. clear_rx_rsc_flags
  22. clear_tx_rsc_flags
  23. encode_subAddrIE_nsap
  24. encode_subAddrIE_nat

   1 /*
   2  * Copyright (c) 2009, Konrad Hammel <konrad@sangoma.com>
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  *
   9  * * Redistributions of source code must retain the above copyright
  10  * notice, this list of conditions and the following disclaimer.
  11  *
  12  * * Redistributions in binary form must reproduce the above copyright
  13  * notice, this list of conditions and the following disclaimer in the
  14  * documentation and/or other materials provided with the distribution.
  15  *
  16  * * Neither the name of the original author; nor the names of any contributors
  17  * may be used to endorse or promote products derived from this software
  18  * without specific prior written permission.
  19  *
  20  *
  21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
  25  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 
  34 /* INCLUDE ********************************************************************/
  35 #include "ftmod_sangoma_ss7_main.h"
  36 /******************************************************************************/
  37 
  38 /* DEFINES ********************************************************************/
  39 /******************************************************************************/
  40 
  41 /* GLOBALS ********************************************************************/
  42 uint32_t sngss7_id;
  43 /******************************************************************************/
  44 
  45 /* PROTOTYPES *****************************************************************/
  46 uint8_t copy_tknStr_from_sngss7(TknStr str, char *ftdm, TknU8 oddEven);
  47 uint8_t copy_cgPtyNum_from_sngss7(ftdm_caller_data_t *ftdm, SiCgPtyNum *cgPtyNum);
  48 uint8_t copy_cgPtyNum_to_sngss7(ftdm_caller_data_t *ftdm, SiCgPtyNum *cgPtyNum);
  49 uint8_t copy_cdPtyNum_from_sngss7(ftdm_caller_data_t *ftdm, SiCdPtyNum *cdPtyNum);
  50 uint8_t copy_cdPtyNum_to_sngss7(ftdm_caller_data_t *ftdm, SiCdPtyNum *cdPtyNum);
  51 
  52 int check_for_state_change(ftdm_channel_t *ftdmchan);
  53 int check_cics_in_range(sngss7_chan_data_t *sngss7_info);
  54 int check_for_reset(sngss7_chan_data_t *sngss7_info);
  55 
  56 unsigned long get_unique_id(void);
  57 
  58 ftdm_status_t extract_chan_data(uint32_t circuit, sngss7_chan_data_t **sngss7_info, ftdm_channel_t **ftdmchan);
  59 
  60 ftdm_status_t check_if_rx_grs_started(ftdm_span_t *ftdmspan);
  61 ftdm_status_t check_if_rx_grs_processed(ftdm_span_t *ftdmspan);
  62 ftdm_status_t check_if_rx_gra_started(ftdm_span_t *ftdmspan);
  63 ftdm_status_t check_for_res_sus_flag(ftdm_span_t *ftdmspan);
  64 
  65 ftdm_status_t process_span_ucic(ftdm_span_t *ftdmspan);
  66 
  67 ftdm_status_t clear_rx_grs_flags(sngss7_chan_data_t *sngss7_info);
  68 ftdm_status_t clear_tx_grs_flags(sngss7_chan_data_t *sngss7_info);
  69 ftdm_status_t clear_rx_rsc_flags(sngss7_chan_data_t *sngss7_info);
  70 ftdm_status_t clear_tx_rsc_flags(sngss7_chan_data_t *sngss7_info);
  71 ftdm_status_t clear_rx_grs_data(sngss7_chan_data_t *sngss7_info);
  72 ftdm_status_t clear_rx_gra_data(sngss7_chan_data_t *sngss7_info);
  73 ftdm_status_t clear_tx_grs_data(sngss7_chan_data_t *sngss7_info);
  74 
  75 ftdm_status_t encode_subAddrIE_nsap(const char *subAddr, char *subAddrIE, int type);
  76 ftdm_status_t encode_subAddrIE_nat(const char *subAddr, char *subAddrIE, int type);
  77 /******************************************************************************/
  78 
  79 /* FUNCTIONS ******************************************************************/
  80 uint8_t copy_cgPtyNum_from_sngss7(ftdm_caller_data_t *ftdm, SiCgPtyNum *cgPtyNum)
  81 {
  82 
  83         return 0;
  84 }
  85 
  86 /******************************************************************************/
  87 uint8_t copy_cgPtyNum_to_sngss7(ftdm_caller_data_t *ftdm, SiCgPtyNum *cgPtyNum)
  88 {
  89         int k;
  90         int j;
  91         int flag;
  92         int odd;
  93         char tmp[2];
  94         uint8_t lower;
  95         uint8_t upper;
  96 
  97         /**************************************************************************/
  98         cgPtyNum->eh.pres                  = PRSNT_NODEF;
  99         /**************************************************************************/
 100         cgPtyNum->natAddrInd.pres   = PRSNT_NODEF;
 101         cgPtyNum->natAddrInd.val        = 0x03;
 102         /**************************************************************************/
 103         cgPtyNum->scrnInd.pres    = PRSNT_NODEF;
 104         cgPtyNum->scrnInd.val      = ftdm->screen;
 105         /**************************************************************************/
 106         cgPtyNum->presRest.pres  = PRSNT_NODEF;
 107         cgPtyNum->presRest.val    = ftdm->pres;
 108         /**************************************************************************/
 109         cgPtyNum->numPlan.pres    = PRSNT_NODEF;
 110         cgPtyNum->numPlan.val      = 0x01;
 111         /**************************************************************************/
 112         cgPtyNum->niInd.pres            = PRSNT_NODEF;
 113         cgPtyNum->niInd.val              = 0x00;
 114         /**************************************************************************/
 115         cgPtyNum->addrSig.pres    = PRSNT_NODEF;
 116 
 117         /* atoi will search through memory starting from the pointer it is given until
 118          * it finds the \0...since tmp is on the stack it will start going through the
 119          * possibly causing corruption.  Hard code a \0 to prevent this
 120          */
 121         tmp[1] = '\0';
 122         k = 0;
 123         j = 0;
 124         flag = 0;
 125         odd = 0;
 126         upper = 0x0;
 127         lower = 0x0;
 128 
 129         while (1) {
 130                 /* grab a digit from the ftdm digits */
 131                 tmp[0] = ftdm->cid_num.digits[k];
 132 
 133                 /* check if the digit is a number and that is not null */
 134                 while (!(isdigit(tmp[0])) && (tmp[0] != '\0')) {
 135                         /* move on to the next value */
 136                         k++;
 137                         tmp[0] = ftdm->cid_num.digits[k];
 138                 } /* while(!(isdigit(tmp))) */
 139 
 140                 /* check if tmp is null or a digit */
 141                 if (tmp[0] != '\0') {
 142                         /* push it into the lower nibble */
 143                         lower = atoi(&tmp[0]);
 144                         /* move to the next digit */
 145                         k++;
 146                         /* grab a digit from the ftdm digits */
 147                         tmp[0] = ftdm->cid_num.digits[k];
 148 
 149                         /* check if the digit is a number and that is not null */
 150                         while (!(isdigit(tmp[0])) && (tmp[0] != '\0')) {
 151                                 k++;
 152                                 tmp[0] = ftdm->cid_num.digits[k];
 153                         } /* while(!(isdigit(tmp))) */
 154 
 155                         /* check if tmp is null or a digit */
 156                         if (tmp[0] != '\0') {
 157                                 /* push the digit into the upper nibble */
 158                                 upper = (atoi(&tmp[0])) << 4;
 159                         } else {
 160                                 /* there is no upper ... fill in 0 */
 161                                 upper = 0x0;
 162                                 /* throw the odd flag */
 163                                 odd = 1;
 164                                 /* throw the end flag */
 165                                 flag = 1;
 166                         } /* if (tmp != '\0') */
 167                 } else {
 168                         /* keep the odd flag down */
 169                         odd = 0;
 170                         /* break right away since we don't need to write the digits */
 171                         break;
 172                 }
 173 
 174                 /* push the digits into the trillium structure */
 175                 cgPtyNum->addrSig.val[j] = upper | lower;
 176 
 177                 /* increment the trillium pointer */
 178                 j++;
 179 
 180                 /* if the flag is up we're through all the digits */
 181                 if (flag) break;
 182 
 183                 /* move to the next digit */
 184                 k++;
 185         } /* while(1) */
 186 
 187         cgPtyNum->addrSig.len = j;
 188 
 189         /**************************************************************************/
 190         cgPtyNum->oddEven.pres    = PRSNT_NODEF;
 191         cgPtyNum->oddEven.val      = odd;
 192         /**************************************************************************/
 193         return 0;
 194 }
 195 
 196 /******************************************************************************/
 197 uint8_t copy_cdPtyNum_from_sngss7(ftdm_caller_data_t *ftdm, SiCdPtyNum *cdPtyNum)
 198 {
 199 
 200         return 0;
 201 }
 202 
 203 /******************************************************************************/
 204 uint8_t copy_cdPtyNum_to_sngss7(ftdm_caller_data_t *ftdm, SiCdPtyNum *cdPtyNum)
 205 {
 206         int k;
 207         int j;
 208         int flag;
 209         int odd;
 210         char tmp[2];
 211         uint8_t lower;
 212         uint8_t upper;
 213 
 214         /**************************************************************************/
 215         cdPtyNum->eh.pres                  = PRSNT_NODEF;
 216         /**************************************************************************/
 217         cdPtyNum->natAddrInd.pres   = PRSNT_NODEF;
 218         cdPtyNum->natAddrInd.val        = 0x03;
 219         /**************************************************************************/
 220         cdPtyNum->numPlan.pres    = PRSNT_NODEF;
 221         cdPtyNum->numPlan.val      = 0x01;
 222         /**************************************************************************/
 223         cdPtyNum->innInd.pres      = PRSNT_NODEF;
 224         cdPtyNum->innInd.val            = 0x01;
 225         /**************************************************************************/
 226         cdPtyNum->addrSig.pres    = PRSNT_NODEF;
 227 
 228         /* atoi will search through memory starting from the pointer it is given until
 229          * it finds the \0...since tmp is on the stack it will start going through the
 230          * possibly causing corruption.  Hard code a \0 to prevent this
 231          */ /* dnis */
 232         tmp[1] = '\0';
 233         k = 0;
 234         j = 0;
 235         flag = 0;
 236         odd = 0;
 237         upper = 0x0;
 238         lower = 0x0;
 239 
 240         while (1) {
 241                 /* grab a digit from the ftdm digits */
 242                 tmp[0] = ftdm->dnis.digits[k];
 243 
 244                 /* check if the digit is a number and that is not null */
 245                 while (!(isdigit(tmp[0])) && (tmp[0] != '\0')) {
 246                         /* move on to the next value */
 247                         k++;
 248                         tmp[0] = ftdm->dnis.digits[k];
 249                 } /* while(!(isdigit(tmp))) */
 250 
 251                 /* check if tmp is null or a digit */
 252                 if (tmp[0] != '\0') {
 253                         /* push it into the lower nibble */
 254                         lower = atoi(&tmp[0]);
 255                         /* move to the next digit */
 256                         k++;
 257                         /* grab a digit from the ftdm digits */
 258                         tmp[0] = ftdm->dnis.digits[k];
 259 
 260                         /* check if the digit is a number and that is not null */
 261                         while (!(isdigit(tmp[0])) && (tmp[0] != '\0')) {
 262                                 k++;
 263                                 tmp[0] = ftdm->dnis.digits[k];
 264                         } /* while(!(isdigit(tmp))) */
 265 
 266                         /* check if tmp is null or a digit */
 267                         if (tmp[0] != '\0') {
 268                                 /* push the digit into the upper nibble */
 269                                 upper = (atoi(&tmp[0])) << 4;
 270                         } else {
 271                                 /* there is no upper ... fill in ST */
 272                                 upper = 0xF0;
 273                                 /* keep the odd flag down */
 274                                 odd = 0;
 275                                 /* throw the end flag */
 276                                 flag = 1;
 277                         } /* if (tmp != '\0') */
 278                 } else {
 279                         /* throw the odd flag */
 280                         odd = 1;
 281                         /* need to add the ST */
 282                         lower = 0xF;
 283                         upper = 0x0;
 284                         /* throw the flag */
 285                         flag = 1;
 286                 }
 287 
 288                 /* push the digits into the trillium structure */
 289                 cdPtyNum->addrSig.val[j] = upper | lower;
 290 
 291                 /* increment the trillium pointer */
 292                 j++;
 293 
 294                 /* if the flag is up we're through all the digits */
 295                 if (flag) break;
 296 
 297                 /* move to the next digit */
 298                 k++;
 299         } /* while(1) */
 300 
 301         cdPtyNum->addrSig.len = j;
 302 
 303         /**************************************************************************/
 304         cdPtyNum->oddEven.pres    = PRSNT_NODEF;
 305 
 306         cdPtyNum->oddEven.val      = odd;
 307 
 308         /**************************************************************************/
 309         return 0;
 310 }
 311 
 312 /******************************************************************************/
 313 uint8_t copy_tknStr_from_sngss7(TknStr str, char *ftdm, TknU8 oddEven)
 314 {
 315         uint8_t i;
 316         uint8_t j;
 317 
 318         /* check if the token string is present */
 319 
 320         if (str.pres == 1) {
 321                 j = 0;
 322 
 323                 for (i = 0; i < str.len; i++) {
 324                         sprintf(&ftdm[j], "%X", (str.val[i] & 0x0F));
 325                         j++;
 326                         sprintf(&ftdm[j], "%X", ((str.val[i] & 0xF0) >> 4));
 327                         j++;
 328                 }
 329 
 330                 /* if the odd flag is up the last digit is a fake "0" */
 331                 if ((oddEven.pres == 1) && (oddEven.val == 1)) {
 332                         ftdm[j-1] = '\0';
 333                 } else {
 334                         ftdm[j] = '\0';
 335                 }
 336 
 337                 
 338         } else {
 339                 SS7_ERROR("Asked to copy tknStr that is not present!\n");
 340                 return 1;
 341         }
 342 
 343         return 0;
 344 }
 345 
 346 /******************************************************************************/
 347 int check_for_state_change(ftdm_channel_t *ftdmchan)
 348 {
 349 
 350         /* check to see if there are any pending state changes on the channel and give them a sec to happen*/
 351         ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_STATE_CHANGE, 500);
 352 
 353         /* check the flag to confirm it is clear now */
 354 
 355         if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
 356                 /* the flag is still up...so we have a problem */
 357                 SS7_DEBUG_CHAN(ftdmchan, "FTDM_CHANNEL_STATE_CHANGE flag set for over 500ms, channel state = %s\n",
 358                                                                         ftdm_channel_state2str (ftdmchan->state));
 359 
 360                 return 1;
 361         }
 362 
 363         return 0;
 364 }
 365 
 366 /******************************************************************************/
 367 int check_cics_in_range(sngss7_chan_data_t *sngss7_info)
 368 {
 369 
 370 
 371 #if 0
 372         ftdm_channel_t          *tmp_ftdmchan;
 373         sngss7_chan_data_t  *tmp_sngss7_info;
 374         int                             i = 0;
 375         
 376         /* check all the circuits in the range to see if we are the last ckt to reset */
 377         for ( i = sngss7_info->grs.circuit; i < ( sngss7_info->grs.range + 1 ); i++ ) {
 378                 if ( g_ftdm_sngss7_data.cfg.isupCircuit[i].siglink == 0 ) {
 379                 
 380                         /* get the ftdmchan and ss7_chan_data from the circuit */
 381                         if (extract_chan_data(g_ftdm_sngss7_data.cfg.isupCircuit[i].id, &tmp_sngss7_info, &tmp_ftdmchan)) {
 382                                 SS7_ERROR("Failed to extract channel data for circuit = %d!\n", g_ftdm_sngss7_data.cfg.isupCircuit[i].id);
 383                                 return 0;
 384                         }
 385 
 386                         /* check if the channel still has the reset flag done is up */
 387                         if (!sngss7_test_flag(tmp_sngss7_info, FLAG_GRP_RESET_RX_DN)) {
 388                                 SS7_DEBUG_CHAN(tmp_ftdmchan, "[CIC:%d] Still processing reset...\n", tmp_sngss7_info->circuit->cic);
 389                                 return 0;
 390                         }
 391                 } /* if not siglink */
 392         } /* for */
 393 
 394         SS7_DEBUG("All circuits out of reset: circuit=%d, range=%d\n",
 395                                 sngss7_info->grs.circuit,
 396                                 sngss7_info->grs.range);
 397         return 1;
 398 
 399 #endif
 400 
 401         return 0;
 402 
 403 }
 404 
 405 /******************************************************************************/
 406 ftdm_status_t extract_chan_data(uint32_t circuit, sngss7_chan_data_t **sngss7_info, ftdm_channel_t **ftdmchan)
 407 {
 408         /*SS7_FUNC_TRACE_ENTER(__FUNCTION__);*/
 409 
 410         if (g_ftdm_sngss7_data.cfg.isupCkt[circuit].obj == NULL) {
 411                 SS7_ERROR("sngss7_info is Null for circuit #%d\n", circuit);
 412                 return FTDM_FAIL;
 413         }
 414 
 415         ftdm_assert_return(g_ftdm_sngss7_data.cfg.isupCkt[circuit].obj, FTDM_FAIL, "received message on signalling link or non-configured cic\n");
 416 
 417         *sngss7_info = g_ftdm_sngss7_data.cfg.isupCkt[circuit].obj;
 418 
 419         ftdm_assert_return((*sngss7_info)->ftdmchan, FTDM_FAIL, "received message on signalling link or non-configured cic\n");
 420         *ftdmchan = (*sngss7_info)->ftdmchan;
 421 
 422         /*SS7_FUNC_TRACE_EXIT(__FUNCTION__);*/
 423         return FTDM_SUCCESS;
 424 }
 425 
 426 /******************************************************************************/
 427 int check_for_reset(sngss7_chan_data_t *sngss7_info)
 428 {
 429 
 430         if (sngss7_test_flag(sngss7_info,FLAG_RESET_RX)) {
 431                 return 1;
 432         }
 433         
 434         if (sngss7_test_flag(sngss7_info,FLAG_RESET_TX)) {
 435                 return 1;
 436         }
 437         
 438         if (sngss7_test_flag(sngss7_info,FLAG_GRP_RESET_RX)) {
 439                 return 1;
 440         }
 441         
 442         if (sngss7_test_flag(sngss7_info,FLAG_GRP_RESET_TX)) {
 443                 return 1;
 444         }
 445 
 446         return 0;
 447         
 448 }
 449 
 450 /******************************************************************************/
 451 unsigned long get_unique_id(void)
 452 {
 453 
 454         if (sngss7_id < 420000000) {
 455                 sngss7_id++;
 456         } else {
 457                 sngss7_id = 1;
 458         }
 459 
 460         return(sngss7_id);
 461 }
 462 
 463 /******************************************************************************/
 464 ftdm_status_t check_if_rx_grs_started(ftdm_span_t *ftdmspan)
 465 {
 466         ftdm_channel_t          *ftdmchan = NULL;
 467         sngss7_chan_data_t  *sngss7_info = NULL;
 468         sngss7_span_data_t      *sngss7_span = (sngss7_span_data_t *)ftdmspan->mod_data;
 469         int                             i;
 470 
 471         for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) {
 472 
 473                 /* extract the channel in question */
 474                 if (extract_chan_data(i, &sngss7_info, &ftdmchan)) {
 475                         SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i);
 476                         continue;
 477                 }
 478 
 479                 /* check if the GRP_RESET_RX flag is already up */
 480                 if (sngss7_test_flag(sngss7_info, FLAG_GRP_RESET_RX)) {
 481                         /* we have already processed this channel...move along */
 482                         continue;
 483                 }
 484 
 485                 /* lock the channel */
 486                 ftdm_mutex_lock(ftdmchan->mutex);
 487 
 488                 /* clear up any pending state changes */
 489                 while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
 490                         ftdm_sangoma_ss7_process_state_change (ftdmchan);
 491                 }
 492 
 493                 SS7_INFO_CHAN(ftdmchan, "Rx GRS (%d:%d)\n", 
 494                                 g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_grs.circuit].cic, 
 495                                 (g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_grs.circuit].cic + sngss7_span->rx_grs.range));
 496 
 497                 /* flag the channel as having received a reset */
 498                 sngss7_set_flag(sngss7_info, FLAG_GRP_RESET_RX);
 499 
 500                 switch (ftdmchan->state) {
 501                 /**************************************************************************/
 502                 case FTDM_CHANNEL_STATE_RESTART:
 503 
 504                         /* go to idle so that we can redo the restart state*/
 505                         ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_IDLE);
 506 
 507                         break;
 508                 /**************************************************************************/
 509                 default:
 510 
 511                         /* set the state of the channel to restart...the rest is done by the chan monitor */
 512                         ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART);
 513                         break;
 514                 /**************************************************************************/
 515                 } /* switch (ftdmchan->state) */
 516 
 517                 /* unlock the channel again before we exit */
 518                 ftdm_mutex_unlock(ftdmchan->mutex);
 519 
 520         } /* for (chans in GRS */
 521 
 522         return FTDM_SUCCESS;
 523 }
 524 
 525 /******************************************************************************/
 526 ftdm_status_t check_if_rx_grs_processed(ftdm_span_t *ftdmspan)
 527 {
 528         ftdm_channel_t          *ftdmchan = NULL;
 529         sngss7_chan_data_t  *sngss7_info = NULL;
 530         sngss7_span_data_t      *sngss7_span = (sngss7_span_data_t *)ftdmspan->mod_data;
 531         int                             i;
 532         int                                     byte = 0;
 533         int                                     bit = 0;
 534 
 535 
 536         ftdm_log(FTDM_LOG_DEBUG, "Found Rx GRS on span %s...checking circuits\n", ftdmspan->name);
 537 
 538         /* check all the circuits in the range to see if they are done resetting */
 539         for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) {
 540 
 541                 /* extract the channel in question */
 542                 if (extract_chan_data(i, &sngss7_info, &ftdmchan)) {
 543                         SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i);
 544                         continue;
 545                 }
 546 
 547                 /* lock the channel */
 548                 ftdm_mutex_lock(ftdmchan->mutex);
 549 
 550                 /* check if there is a state change pending on the channel */
 551                 if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
 552                         /* check the state to the GRP_RESET_RX_DN flag */
 553                         if (!sngss7_test_flag(sngss7_info, FLAG_GRP_RESET_RX_DN)) {
 554                                 /* this channel is still resetting...do nothing */
 555                                         goto GRS_UNLOCK_ALL;
 556                         } /* if (!sngss7_test_flag(sngss7_info, FLAG_GRP_RESET_RX_DN)) */
 557                 } else {
 558                         /* state change pending */
 559                         goto GRS_UNLOCK_ALL;
 560                 }
 561         } /* for ( i = circuit; i < (circuit + range + 1); i++) */
 562 
 563         SS7_DEBUG("All circuits out of reset for GRS: circuit=%d, range=%d\n",
 564                                         sngss7_span->rx_grs.circuit,
 565                                         sngss7_span->rx_grs.range);
 566 
 567         /* check all the circuits in the range to see if they are done resetting */
 568         for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) {
 569 
 570                 /* extract the channel in question */
 571                 if (extract_chan_data(i, &sngss7_info, &ftdmchan)) {
 572                         SS7_ERROR("Failed to extract channel data for circuit = %d!\n",i);
 573                         /* check if we need to die */
 574                         SS7_ASSERT;
 575                         /* move along */
 576                         continue;
 577                 }
 578 
 579                 /* throw the GRP reset flag complete flag */
 580                 sngss7_set_flag(sngss7_info, FLAG_GRP_RESET_RX_CMPLT);
 581 
 582                 /* move the channel to the down state */
 583                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
 584 
 585                 /* update the status map if the ckt is in blocked state */
 586                 if ((sngss7_test_flag(sngss7_info, FLAG_CKT_MN_BLOCK_RX)) ||
 587                         (sngss7_test_flag(sngss7_info, FLAG_CKT_MN_BLOCK_TX)) ||
 588                         (sngss7_test_flag(sngss7_info, FLAG_GRP_MN_BLOCK_RX)) ||
 589                         (sngss7_test_flag(sngss7_info, FLAG_GRP_MN_BLOCK_RX))) {
 590                 
 591                         sngss7_span->rx_grs.status[byte] = (sngss7_span->rx_grs.status[byte] | (1 << bit));
 592                 } /* if blocked */
 593                 
 594                 /* update the bit and byte counter*/
 595                 bit ++;
 596                 if (bit == 8) {
 597                         byte++;
 598                         bit = 0;
 599                 }
 600         } /* for ( i = circuit; i < (circuit + range + 1); i++) */
 601 
 602 GRS_UNLOCK_ALL:
 603         for ( i = sngss7_span->rx_grs.circuit; i < (sngss7_span->rx_grs.circuit + sngss7_span->rx_grs.range + 1); i++) {
 604                 /* extract the channel in question */
 605                 if (extract_chan_data(i, &sngss7_info, &ftdmchan)) {
 606                         SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i);
 607                         continue;
 608                 }
 609 
 610                 /* unlock the channel */
 611                 ftdm_mutex_unlock(ftdmchan->mutex);
 612         }
 613 
 614         return FTDM_SUCCESS;
 615 }
 616 
 617 /******************************************************************************/
 618 ftdm_status_t check_if_rx_gra_started(ftdm_span_t *ftdmspan)
 619 {
 620         ftdm_channel_t          *ftdmchan = NULL;
 621         sngss7_chan_data_t  *sngss7_info = NULL;
 622         sngss7_span_data_t      *sngss7_span = (sngss7_span_data_t *)ftdmspan->mod_data;
 623         int                             i;
 624 
 625         for (i = sngss7_span->rx_gra.circuit; i < (sngss7_span->rx_gra.circuit + sngss7_span->rx_gra.range + 1); i++) {
 626 
 627                 /* extract the channel in question */
 628                 if (extract_chan_data(i, &sngss7_info, &ftdmchan)) {
 629                         SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i);
 630                         continue;
 631                 }
 632 
 633                 /* check if the channel is already procoessing the GRA */
 634                 if (sngss7_test_flag(sngss7_info, FLAG_GRP_RESET_TX_RSP)) {
 635                         /* move along */
 636                         continue;
 637                 }
 638 
 639                 /* lock the channel */
 640                 ftdm_mutex_lock(ftdmchan->mutex);
 641 
 642                 /* clear up any pending state changes */
 643                 while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
 644                         ftdm_sangoma_ss7_process_state_change (ftdmchan);
 645                 }
 646 
 647                 SS7_INFO_CHAN(ftdmchan, "Rx GRA (%d:%d)\n", 
 648                                 g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_gra.circuit].cic, 
 649                                 (g_ftdm_sngss7_data.cfg.isupCkt[sngss7_span->rx_gra.circuit].cic + sngss7_span->rx_gra.range));
 650 
 651                 switch (ftdmchan->state) {
 652                 /**********************************************************************/
 653                 case FTDM_CHANNEL_STATE_RESTART:
 654                         
 655                         /* throw the FLAG_RESET_TX_RSP to indicate we have acknowledgement from the remote side */
 656                         sngss7_set_flag(sngss7_info, FLAG_GRP_RESET_TX_RSP);
 657 
 658                         /* go to DOWN */
 659                         ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
 660 
 661                         break;
 662                 /**********************************************************************/
 663                 case FTDM_CHANNEL_STATE_DOWN:
 664 
 665                         /* do nothing, just drop the message */
 666                         SS7_DEBUG("Receveived GRA in down state, dropping\n");
 667 
 668                         break;
 669                 /**********************************************************************/
 670                 case FTDM_CHANNEL_STATE_TERMINATING:
 671                 case FTDM_CHANNEL_STATE_HANGUP:
 672                 case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
 673                         
 674                         /* throw the FLAG_RESET_TX_RSP to indicate we have acknowledgement from the remote side */
 675                         sngss7_set_flag(sngss7_info, FLAG_GRP_RESET_TX_RSP);
 676 
 677                         break;
 678                 /**********************************************************************/
 679                 default:
 680                         /* ITU Q764-2.9.5.1.c -> release the circuit */
 681                         if (sngss7_span->rx_gra.cause != 0) {
 682                                 ftdmchan->caller_data.hangup_cause = sngss7_span->rx_gra.cause;
 683                         } else {
 684                                 ftdmchan->caller_data.hangup_cause = 98;        /* Message not compatiable with call state */
 685                         }
 686 
 687                         /* go to terminating to hang up the call */
 688                         ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
 689                         break;
 690                 /**********************************************************************/
 691                 }
 692 
 693                 /* unlock the channel again before we exit */
 694                 ftdm_mutex_unlock(ftdmchan->mutex);
 695 
 696         } /* for ( circuits in request */
 697 
 698 
 699         return FTDM_SUCCESS;
 700 }
 701 
 702 /******************************************************************************/
 703 ftdm_status_t check_for_res_sus_flag(ftdm_span_t *ftdmspan)
 704 {
 705         ftdm_channel_t          *ftdmchan = NULL;
 706         sngss7_chan_data_t      *sngss7_info = NULL;
 707         ftdm_sigmsg_t           sigev;
 708         int                             x;
 709 
 710         for (x = 1; x < (ftdmspan->chan_count + 1); x++) {
 711 
 712                 /* extract the channel structure and sngss7 channel data */
 713                 ftdmchan = ftdmspan->channels[x];
 714                 
 715                 /* if the call data is NULL move on */
 716                 if (ftdmchan->call_data == NULL) continue;
 717 
 718                 sngss7_info = ftdmchan->call_data;
 719 
 720                 /* lock the channel */
 721                 ftdm_mutex_lock(ftdmchan->mutex);
 722 
 723                 memset (&sigev, 0, sizeof (sigev));
 724 
 725                 sigev.chan_id = ftdmchan->chan_id;
 726                 sigev.span_id = ftdmchan->span_id;
 727                 sigev.channel = ftdmchan;
 728 
 729                 /* if we have the PAUSED flag and the sig status is still UP */
 730                 if ((sngss7_test_flag(sngss7_info, FLAG_INFID_PAUSED)) &&
 731                         (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SIG_UP))) {
 732 
 733                         /* clear up any pending state changes */
 734                         while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
 735                                 ftdm_sangoma_ss7_process_state_change (ftdmchan);
 736                         }
 737                         
 738                         /* throw the channel into SUSPENDED to process the flag */
 739                         /* after doing this once the sig status will be down */
 740                         ftdm_set_state_locked (ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED);
 741                 }
 742 
 743                 /* if the RESUME flag is up go to SUSPENDED to process the flag */
 744                 /* after doing this the flag will be cleared */
 745                 if (sngss7_test_flag(sngss7_info, FLAG_INFID_RESUME)) {
 746 
 747                         /* clear up any pending state changes */
 748                         while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
 749                                 ftdm_sangoma_ss7_process_state_change (ftdmchan);
 750                         }
 751 
 752                         /* got SUSPENDED state to clear the flag */
 753                         ftdm_set_state_locked (ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED);
 754                 }
 755 
 756                 /* unlock the channel */
 757                 ftdm_mutex_unlock(ftdmchan->mutex);
 758 
 759         } /* for (x = 1; x < (span->chan_count + 1); x++) */
 760 
 761         /* signal the core that sig events are queued for processing */
 762         ftdm_span_trigger_signals(ftdmspan);
 763 
 764         return FTDM_SUCCESS;
 765 }
 766 
 767 /******************************************************************************/
 768 ftdm_status_t process_span_ucic(ftdm_span_t *ftdmspan)
 769 {
 770         ftdm_channel_t          *ftdmchan = NULL;
 771         sngss7_chan_data_t  *sngss7_info = NULL;
 772         sngss7_span_data_t      *sngss7_span = (sngss7_span_data_t *)ftdmspan->mod_data;
 773         int                             i;
 774 
 775         for (i = sngss7_span->ucic.circuit; i < (sngss7_span->ucic.circuit + sngss7_span->ucic.range + 1); i++) {
 776 
 777                 /* extract the channel in question */
 778                 if (extract_chan_data(i, &sngss7_info, &ftdmchan)) {
 779                         SS7_ERROR("Failed to extract channel data for circuit = %d!\n", i);
 780                         continue;
 781                 }
 782 
 783                 /* lock the channel */
 784                 ftdm_mutex_lock(ftdmchan->mutex);
 785 
 786                 SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx UCIC\n", sngss7_info->circuit->cic);
 787 
 788                 /* clear up any pending state changes */
 789                 while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
 790                         ftdm_sangoma_ss7_process_state_change (ftdmchan);
 791                 }
 792 
 793                 /* throw the ckt block flag */
 794                 sngss7_set_flag(sngss7_info, FLAG_CKT_UCIC_BLOCK);
 795 
 796                 /* set the channel to suspended state */
 797                 ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED);
 798 
 799                 /* unlock the channel again before we exit */
 800                 ftdm_mutex_unlock(ftdmchan->mutex);
 801         }
 802 
 803         /* clear out the ucic data since we're done with it */
 804         memset(&sngss7_span->ucic, 0x0, sizeof(sngss7_group_data_t));
 805 
 806         return FTDM_SUCCESS;
 807 }
 808 
 809 /******************************************************************************/
 810 ftdm_status_t clear_rx_grs_flags(sngss7_chan_data_t *sngss7_info)
 811 {
 812         /* clear all the flags related to an incoming GRS */
 813         sngss7_clear_flag(sngss7_info, FLAG_GRP_RESET_RX);
 814         sngss7_clear_flag(sngss7_info, FLAG_GRP_RESET_RX_DN);
 815         sngss7_clear_flag(sngss7_info, FLAG_GRP_RESET_RX_CMPLT);
 816 
 817         return FTDM_SUCCESS;
 818 }
 819 
 820 /******************************************************************************/
 821 ftdm_status_t clear_rx_grs_data(sngss7_chan_data_t *sngss7_info)
 822 {
 823         ftdm_channel_t          *ftdmchan = sngss7_info->ftdmchan;
 824         sngss7_span_data_t      *sngss7_span = ftdmchan->span->mod_data;
 825 
 826         /* clear the rx_grs data fields */
 827         memset(&sngss7_span->rx_grs, 0x0, sizeof(sngss7_group_data_t));
 828 
 829         return FTDM_SUCCESS;
 830 }
 831 
 832 /******************************************************************************/
 833 ftdm_status_t clear_rx_gra_data(sngss7_chan_data_t *sngss7_info)
 834 {
 835         ftdm_channel_t          *ftdmchan = sngss7_info->ftdmchan;
 836         sngss7_span_data_t      *sngss7_span = ftdmchan->span->mod_data;
 837 
 838         /* clear the rx_grs data fields */
 839         memset(&sngss7_span->rx_gra, 0x0, sizeof(sngss7_group_data_t));
 840 
 841         return FTDM_SUCCESS;
 842 }
 843 /******************************************************************************/
 844 ftdm_status_t clear_tx_grs_flags(sngss7_chan_data_t *sngss7_info)
 845 {
 846         /* clear all the flags related to an outgoing GRS */
 847         sngss7_clear_flag(sngss7_info, FLAG_GRP_RESET_BASE);
 848         sngss7_clear_flag(sngss7_info, FLAG_GRP_RESET_TX);
 849         sngss7_clear_flag(sngss7_info, FLAG_GRP_RESET_SENT);
 850         sngss7_clear_flag(sngss7_info, FLAG_GRP_RESET_TX_RSP);
 851 
 852         return FTDM_SUCCESS;
 853 }
 854 
 855 /******************************************************************************/
 856 ftdm_status_t clear_tx_grs_data(sngss7_chan_data_t *sngss7_info)
 857 {
 858         ftdm_channel_t          *ftdmchan = sngss7_info->ftdmchan;
 859         sngss7_span_data_t      *sngss7_span = ftdmchan->span->mod_data;
 860 
 861         /* clear the rx_grs data fields */
 862         memset(&sngss7_span->tx_grs, 0x0, sizeof(sngss7_group_data_t));
 863 
 864         return FTDM_SUCCESS;
 865 }
 866 
 867 /******************************************************************************/
 868 
 869 /******************************************************************************/
 870 ftdm_status_t clear_rx_rsc_flags(sngss7_chan_data_t *sngss7_info)
 871 {
 872         /* clear all the flags related to an incoming RSC */
 873         sngss7_clear_flag(sngss7_info, FLAG_RESET_RX);
 874 
 875         return FTDM_SUCCESS;
 876 }
 877 
 878 /******************************************************************************/
 879 ftdm_status_t clear_tx_rsc_flags(sngss7_chan_data_t *sngss7_info)
 880 {
 881         /* clear all the flags related to an outgoing RSC */
 882         sngss7_clear_flag(sngss7_info, FLAG_RESET_TX);
 883         sngss7_clear_flag(sngss7_info, FLAG_RESET_SENT);
 884         sngss7_clear_flag(sngss7_info, FLAG_RESET_TX_RSP);
 885 
 886         return FTDM_SUCCESS;
 887 }
 888 
 889 /******************************************************************************/
 890 ftdm_status_t encode_subAddrIE_nsap(const char *subAddr, char *subAddrIE, int type)
 891 {
 892         /* Q931 4.5.9 
 893          * 8    7       6       5       4       3       2       1       (octet)
 894          *
 895          * 0    1       1       1       0       0       0       1       (spare 8) ( IE id 1-7)
 896          * X    X       X       X       X       X       X       X       (length of IE contents)
 897          * 1    0       0       0       Z       0       0       0       (ext 8) (NSAP type 5-7) (odd/even 4) (spare 1-3)
 898          * X    X       X       X       X       X       X       X       (sub address encoded in ia5)
 899          */
 900 
 901         int     x = 0;
 902         int p = 0;
 903         int len = 0;
 904         char tmp[2];
 905 
 906         /* initalize the second element of tmp to \0 so that atoi doesn't go to far */
 907         tmp[1]='\0';
 908 
 909         /* set octet 1 aka IE id */
 910         p = 0;
 911         switch(type) {
 912         /**************************************************************************/
 913         case SNG_CALLED:                                                /* called party sub address */
 914                 subAddrIE[p] = 0x71;
 915                 break;
 916         /**************************************************************************/
 917         case SNG_CALLING:                                               /* calling party sub address */
 918                 subAddrIE[p] = 0x6d;
 919                 break;
 920         /**************************************************************************/
 921         default:                                                                /* not good */
 922                 SS7_ERROR("Sub-Address type is invalid: %d\n", type);
 923                 return FTDM_FAIL;
 924                 break;
 925         /**************************************************************************/
 926         } /* switch(type) */
 927 
 928         /* set octet 3 aka type and o/e */
 929         p = 2;
 930         subAddrIE[p] = 0x80;
 931 
 932         /* set the subAddrIE pointer octet 4 */
 933         p = 3;
 934 
 935         /* loop through all digits in subAddr and insert them into subAddrIE */
 936         while (subAddr[x] != '\0') {
 937 
 938                 /* grab a character */
 939                 tmp[0] = subAddr[x];
 940 
 941                 /* confirm it is a digit */
 942                 if (!isdigit(tmp[0])) {
 943                         /* move to the next character in subAddr */
 944                         x++;
 945 
 946                         /* restart the loop */
 947                         continue;
 948                 }
 949 
 950                 /* convert the character to IA5 encoding and write into subAddrIE */
 951                 subAddrIE[p] = atoi(&tmp[0]);   /* lower nibble is the digit */
 952                 subAddrIE[p] |= 0x3 << 4;               /* upper nibble is 0x3 */
 953 
 954                 /* increment address length counter */
 955                 len++;
 956 
 957                 /* increment the subAddrIE pointer */
 958                 p++;
 959 
 960                 /* move to the next character in subAddr */
 961                 x++;
 962 
 963         } /* while (subAddr[x] != '\0') */
 964 
 965         /* set octet 2 aka length of subaddr */
 966         p = 1;
 967         subAddrIE[p] = len + 1;
 968 
 969 
 970         return FTDM_SUCCESS;
 971 }
 972 
 973 /******************************************************************************/
 974 ftdm_status_t encode_subAddrIE_nat(const char *subAddr, char *subAddrIE, int type)
 975 {
 976         /* Q931 4.5.9 
 977          * 8    7       6       5       4       3       2       1       (octet)
 978          *
 979          * 0    1       1       1       0       0       0       1       (spare 8) ( IE id 1-7)
 980          * X    X       X       X       X       X       X       X       (length of IE contents)
 981          * 1    0       0       0       Z       0       0       0       (ext 8) (NSAP type 5-7) (odd/even 4) (spare 1-3)
 982          * X    X       X       X       X       X       X       X       (sub address encoded in ia5)
 983          */
 984 
 985         int             x = 0;
 986         int     p = 0;
 987         int     len = 0;
 988         char    tmp[2];
 989         int     flag = 0;
 990         int     odd = 0;
 991         uint8_t lower = 0x0;
 992         uint8_t upper = 0x0;
 993 
 994         /* initalize the second element of tmp to \0 so that atoi doesn't go to far */
 995         tmp[1]='\0';
 996 
 997         /* set octet 1 aka IE id */
 998         p = 0;
 999         switch(type) {
1000         /**************************************************************************/
1001         case SNG_CALLED:                                                /* called party sub address */
1002                 subAddrIE[p] = 0x71;
1003                 break;
1004         /**************************************************************************/
1005         case SNG_CALLING:                                               /* calling party sub address */
1006                 subAddrIE[p] = 0x6d;
1007                 break;
1008         /**************************************************************************/
1009         default:                                                                /* not good */
1010                 SS7_ERROR("Sub-Address type is invalid: %d\n", type);
1011                 return FTDM_FAIL;
1012                 break;
1013         /**************************************************************************/
1014         } /* switch(type) */
1015 
1016         /* set the subAddrIE pointer octet 4 */
1017         p = 3;
1018 
1019         /* loop through all digits in subAddr and insert them into subAddrIE */
1020         while (1) {
1021 
1022                 /* grab a character */
1023                 tmp[0] = subAddr[x];
1024 
1025                 /* confirm it is a hex digit */
1026                 while ((!isxdigit(tmp[0])) && (tmp[0] != '\0')) {
1027                         /* move to the next character in subAddr */
1028                         x++;
1029                         tmp[0] = subAddr[x];
1030                 }
1031 
1032                 /* check if tmp is null or a digit */
1033                 if (tmp[0] != '\0') {
1034                         /* push it into the lower nibble using strtol to allow a-f chars */
1035                         lower = strtol(&tmp[0], (char **)NULL, 16);
1036                         /* move to the next digit */
1037                         x++;
1038                         /* grab a digit from the ftdm digits */
1039                         tmp[0] = subAddr[x];
1040 
1041                         /* check if the digit is a hex digit and that is not null */
1042                         while (!(isxdigit(tmp[0])) && (tmp[0] != '\0')) {
1043                                 x++;
1044                                 tmp[0] = subAddr[x];
1045                         } /* while(!(isdigit(tmp))) */
1046 
1047                         /* check if tmp is null or a digit */
1048                         if (tmp[0] != '\0') {
1049                                 /* push the digit into the upper nibble using strtol to allow a-f chars */
1050                                 upper = (strtol(&tmp[0], (char **)NULL, 16)) << 4;
1051                         } else {
1052                                 /* there is no upper ... fill in spare */
1053                                 upper = 0x00;
1054                                 /* throw the odd flag since we need to buffer */
1055                                 odd = 1;
1056                                 /* throw the end flag */
1057                                 flag = 1;
1058                         } /* if (tmp != '\0') */
1059                 } else {
1060                         /* keep the odd flag down */
1061                         odd = 0;
1062 
1063                         /* throw the flag */
1064                         flag = 1;
1065 
1066                         /* bounce out right away */
1067                         break;
1068                 }
1069 
1070                 /* fill in the octet */
1071                 subAddrIE[p] = upper | lower;
1072 
1073                 /* increment address length counter */
1074                 len++;
1075 
1076                 /* if the flag is we're through all the digits */
1077                 if (flag) break;
1078 
1079                 /* increment the subAddrIE pointer */
1080                 p++;
1081 
1082                 /* move to the next character in subAddr */
1083                 x++;
1084 
1085         } /* while (subAddr[x] != '\0') */
1086 
1087         /* set octet 2 aka length of subaddr */
1088         p = 1;
1089         subAddrIE[p] = len + 1;
1090 
1091         /* set octet 3 aka type and o/e */
1092         p = 2;
1093         subAddrIE[p] = 0xa0 | (odd << 3);
1094 
1095 
1096         return FTDM_SUCCESS;
1097 }
1098 
1099 /******************************************************************************/
1100 /* For Emacs:
1101  * Local Variables:
1102  * mode:c
1103  * indent-tabs-mode:t
1104  * tab-width:4
1105  * c-basic-offset:4
1106  * End:
1107  * For VIM:
1108  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
1109  */
1110 /******************************************************************************/

/* [<][>][^][v][top][bottom][index][help] */