root/src/ftdm_state.c

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

DEFINITIONS

This source file includes following definitions.
  1. FTDM_ENUM_NAMES
  2. FT_DECLARE
  3. FT_DECLARE
  4. ftdm_parse_state_map
  5. FT_DECLARE
  6. FT_DECLARE
  7. FT_DECLARE
  8. FT_DECLARE
  9. FT_DECLARE
  10. FT_DECLARE
  11. write_history_entry
  12. FT_DECLARE
  13. FT_DECLARE
  14. FT_DECLARE

   1 /*
   2  * Copyright (c) 2010, Sangoma Technologies
   3  * Moises Silva <moy@sangoma.com>
   4  * All rights reserved.
   5  * 
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  * 
  10  * * Redistributions of source code must retain the above copyright
  11  * notice, this list of conditions and the following disclaimer.
  12  * 
  13  * * Redistributions in binary form must reproduce the above copyright
  14  * notice, this list of conditions and the following disclaimer in the
  15  * documentation and/or other materials provided with the distribution.
  16  * 
  17  * * Neither the name of the original author; nor the names of any contributors
  18  * may be used to endorse or promote products derived from this software
  19  * without specific prior written permission.
  20  * 
  21  * 
  22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
  26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33  */
  34 
  35 #include "private/ftdm_core.h"
  36 
  37 FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS)
  38 FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID)
  39 
  40 FTDM_ENUM_NAMES(CHANNEL_STATE_STATUS_NAMES, CHANNEL_STATE_STATUS_STRINGS)
  41 FTDM_STR2ENUM(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t, CHANNEL_STATE_STATUS_NAMES, FTDM_STATE_STATUS_INVALID)
  42 
  43 /* This function is only needed for boost and we should get rid of it at the next refactoring */
  44 FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *fchan)
  45 {
  46         ftdm_channel_lock(fchan);
  47 
  48         if (fchan->init_state != FTDM_CHANNEL_STATE_DOWN) {
  49                 ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, fchan, fchan->init_state, 1);
  50                 fchan->init_state = FTDM_CHANNEL_STATE_DOWN;
  51         }
  52 
  53         ftdm_channel_unlock(fchan);
  54         return FTDM_SUCCESS;
  55 }
  56 
  57 FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
  58 {
  59         uint8_t hindex = 0;
  60         ftdm_time_t diff = 0;
  61         ftdm_channel_state_t state = fchan->state;
  62 
  63         if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) {
  64                 ftdm_assert_return(!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE), FTDM_FAIL, 
  65                                 "State change flag set but state is not completed\n");
  66                 return FTDM_SUCCESS;
  67         }
  68 
  69         ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
  70 
  71         if (state == FTDM_CHANNEL_STATE_PROGRESS) {
  72                 ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
  73         } else if (state == FTDM_CHANNEL_STATE_UP) {
  74                 ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
  75                 ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA);       
  76                 ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED);    
  77         } else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
  78                 ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);    
  79                 ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA);       
  80         } else if (state == FTDM_CHANNEL_STATE_DIALING) {
  81                 ftdm_sigmsg_t msg;
  82                 memset(&msg, 0, sizeof(msg));
  83                 msg.channel = fchan;
  84                 msg.event_id = FTDM_SIGEVENT_DIALING;
  85                 ftdm_span_send_signal(fchan->span, &msg);
  86         }
  87 
  88         /* MAINTENANCE WARNING
  89          * we're assuming an indication performed 
  90          * via state change will involve a single state change */
  91         ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS);
  92 
  93         hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
  94         
  95         ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n");
  96 
  97         fchan->history[hindex].end_time = ftdm_current_time_in_ms();
  98 
  99         fchan->state_status = FTDM_STATE_STATUS_COMPLETED;
 100 
 101         diff = fchan->history[hindex].end_time - fchan->history[hindex].time;
 102 
 103         ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s in %llums\n", 
 104                         ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff);
 105         
 106 
 107         if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
 108                 ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
 109                 ftdm_interrupt_signal(fchan->state_completed_interrupt);
 110         }
 111 
 112         return FTDM_SUCCESS;
 113 }
 114 
 115 FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line,
 116                 ftdm_channel_t *fchan, ftdm_channel_state_t state)
 117 {
 118         if (fchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
 119                 /* the current state is not completed, setting a new state from a signaling module
 120                    when the current state is not completed is equivalent to implicitly acknowledging 
 121                    the current state */
 122                 _ftdm_channel_complete_state(file, func, line, fchan);
 123         }
 124         return ftdm_channel_set_state(file, func, line, fchan, state, 0);
 125 }
 126 
 127 static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map)
 128 {
 129         int x = 0, ok = 0;
 130         ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND;
 131 
 132         for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) {
 133                 int i = 0, proceed = 0;
 134                 if (!state_map->nodes[x].type) {
 135                         break;
 136                 }
 137 
 138                 if (state_map->nodes[x].direction != direction) {
 139                         continue;
 140                 }
 141                 
 142                 if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) {
 143                         proceed = 1;
 144                 } else {
 145                         for(i = 0; i < FTDM_MAP_MAX; i++) {
 146                                 if (state_map->nodes[x].check_states[i] == ftdmchan->state) {
 147                                         proceed = 1;
 148                                         break;
 149                                 }
 150                         }
 151                 }
 152 
 153                 if (!proceed) {
 154                         continue;
 155                 }
 156                 
 157                 for(i = 0; i < FTDM_MAP_MAX; i++) {
 158                         ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE);
 159                         if (state_map->nodes[x].states[i] == FTDM_END) {
 160                                 break;
 161                         }
 162                         if (state_map->nodes[x].states[i] == state) {
 163                                 ok = !ok;
 164                                 goto end;
 165                         }
 166                 }
 167         }
 168  end:
 169         
 170         return ok;
 171 }
 172 
 173 FT_DECLARE(ftdm_status_t) ftdm_channel_cancel_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
 174 {
 175         ftdm_time_t diff;
 176         ftdm_channel_state_t state;
 177         ftdm_channel_state_t last_state;
 178         uint8_t hindex = 0;
 179 
 180         if (!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE)) {
 181                 ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Cannot cancel state change from %s to %s, it was already processed\n", 
 182                                 ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state));
 183                 return FTDM_FAIL;
 184         }
 185 
 186         if (fchan->state_status != FTDM_STATE_STATUS_NEW) {
 187                 ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Failed to cancel state change from %s to %s, state is not new anymore\n", 
 188                                 ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state));
 189                 return FTDM_FAIL;
 190         }
 191 
 192         /* compute the last history index */
 193         hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
 194         diff = fchan->history[hindex].end_time - fchan->history[hindex].time;
 195 
 196         /* go back in time and revert the state to the previous state */
 197         state = fchan->state;
 198         last_state = fchan->last_state;
 199 
 200         fchan->state = fchan->last_state;
 201         fchan->state_status = FTDM_STATE_STATUS_COMPLETED;
 202         fchan->last_state = fchan->history[hindex].last_state;
 203         fchan->hindex = hindex;
 204 
 205         /* clear the state change flag */
 206         ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
 207 
 208         /* ack any pending indications as cancelled */
 209         ftdm_ack_indication(fchan, fchan->indication, FTDM_ECANCELED);
 210 
 211         /* wake up anyone sleeping waiting for the state change to complete, it won't ever be completed */
 212         if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
 213                 ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
 214                 ftdm_interrupt_signal(fchan->state_completed_interrupt);
 215         }
 216 
 217         /* NOTE
 218          * we could potentially also take out the channel from the pendingchans queue, but I believe is easier just leave it,
 219          * the only side effect will be a call to ftdm_channel_advance_states() for a channel that has nothing to advance */
 220         ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Cancelled state change from %s to %s in %llums\n", 
 221                         ftdm_channel_state2str(last_state), ftdm_channel_state2str(state), diff);
 222         
 223         return FTDM_SUCCESS;
 224 }
 225 
 226 /* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */
 227 #define DEFAULT_WAIT_TIME 1000
 228 FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
 229 {
 230         ftdm_status_t status;
 231         int ok = 1;
 232         int waitms = DEFAULT_WAIT_TIME; 
 233 
 234         if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) {
 235                 ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n",
 236                                 ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
 237                 return FTDM_FAIL;
 238         }
 239 
 240         if (ftdmchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
 241                 ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, 
 242                 "Ignored state change request from %s to %s, the previous state change has not been processed yet (status = %s)\n",
 243                 ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state),
 244                 ftdm_state_status2str(ftdmchan->state_status));
 245                 return FTDM_FAIL;
 246         }
 247 
 248         if (ftdmchan->state == state) {
 249                 ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
 250                 return FTDM_FAIL;
 251         }
 252 
 253         if (!ftdmchan->state_completed_interrupt) {
 254                 status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET);
 255                 if (status != FTDM_SUCCESS) {
 256                         ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT, 
 257                                         "Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
 258                         return status;
 259                 }
 260         }
 261 
 262 
 263         if (ftdmchan->span->state_map) {
 264                 ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
 265                 goto end;
 266         }
 267 
 268         /* basic core state validation (by-passed if the signaling module provides a state_map) */
 269         switch(ftdmchan->state) {
 270         case FTDM_CHANNEL_STATE_HANGUP:
 271         case FTDM_CHANNEL_STATE_TERMINATING:
 272                 {
 273                         ok = 0;
 274                         switch(state) {
 275                         case FTDM_CHANNEL_STATE_DOWN:
 276                         case FTDM_CHANNEL_STATE_BUSY:
 277                         case FTDM_CHANNEL_STATE_RESTART:
 278                                 ok = 1;
 279                                 break;
 280                         default:
 281                                 break;
 282                         }
 283                 }
 284                 break;
 285         case FTDM_CHANNEL_STATE_UP:
 286                 {
 287                         ok = 1;
 288                         switch(state) {
 289                         case FTDM_CHANNEL_STATE_PROGRESS:
 290                         case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
 291                         case FTDM_CHANNEL_STATE_RING:
 292                                 ok = 0;
 293                                 break;
 294                         default:
 295                                 break;
 296                         }
 297                 }
 298                 break;
 299         case FTDM_CHANNEL_STATE_DOWN:
 300                 {
 301                         ok = 0;
 302                         
 303                         switch(state) {
 304                         case FTDM_CHANNEL_STATE_DIALTONE:
 305                         case FTDM_CHANNEL_STATE_COLLECT:
 306                         case FTDM_CHANNEL_STATE_DIALING:
 307                         case FTDM_CHANNEL_STATE_RING:
 308                         case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
 309                         case FTDM_CHANNEL_STATE_PROGRESS:                               
 310                         case FTDM_CHANNEL_STATE_IDLE:                           
 311                         case FTDM_CHANNEL_STATE_GET_CALLERID:
 312                         case FTDM_CHANNEL_STATE_GENRING:
 313                                 ok = 1;
 314                                 break;
 315                         default:
 316                                 break;
 317                         }
 318                 }
 319                 break;
 320         case FTDM_CHANNEL_STATE_BUSY:
 321                 {
 322                         switch(state) {
 323                         case FTDM_CHANNEL_STATE_UP:
 324                                 ok = 0;
 325                                 break;
 326                         default:
 327                                 break;
 328                         }
 329                 }
 330                 break;
 331         case FTDM_CHANNEL_STATE_RING:
 332                 {
 333                         switch(state) {
 334                         case FTDM_CHANNEL_STATE_UP:
 335                                 ok = 1;
 336                                 break;
 337                         default:
 338                                 break;
 339                         }
 340                 }
 341                 break;
 342         default:
 343                 break;
 344         }
 345 
 346 end:
 347 
 348         if (!ok) {
 349                 ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
 350                 goto done;
 351         }
 352 
 353         ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
 354         ftdmchan->last_state = ftdmchan->state; 
 355         ftdmchan->state = state;
 356         ftdmchan->state_status = FTDM_STATE_STATUS_NEW;
 357         ftdmchan->history[ftdmchan->hindex].file = file;
 358         ftdmchan->history[ftdmchan->hindex].func = func;
 359         ftdmchan->history[ftdmchan->hindex].line = line;
 360         ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
 361         ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
 362         ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
 363         ftdmchan->history[ftdmchan->hindex].end_time = 0;
 364         ftdmchan->hindex++;
 365         if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
 366                 ftdmchan->hindex = 0;
 367         }
 368         ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
 369 
 370         ftdm_mutex_lock(ftdmchan->span->mutex);
 371         ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
 372         if (ftdmchan->span->pendingchans) {
 373                 ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
 374         }
 375         ftdm_mutex_unlock(ftdmchan->span->mutex);
 376 
 377         if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) {
 378                 /* the channel should not block waiting for state processing */
 379                 goto done;
 380         }
 381 
 382         if (!waitrq) {
 383                 /* no waiting was requested */
 384                 goto done;
 385         }
 386 
 387         /* let's wait for the state change to be completed by the signaling stack */
 388         ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
 389 
 390         ftdm_mutex_unlock(ftdmchan->mutex);
 391 
 392         status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms);
 393 
 394         ftdm_mutex_lock(ftdmchan->mutex);
 395 
 396         if (status != FTDM_SUCCESS) {
 397                 ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
 398                 ftdm_log_chan_ex(ftdmchan, file, func, line, 
 399                                 FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n",
 400                                 ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
 401                 ok = 0;
 402                 goto done;
 403         }
 404 done:
 405         return ok ? FTDM_SUCCESS : FTDM_FAIL;
 406 }
 407 
 408 FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan)
 409 {
 410         int state;
 411         ftdm_channel_lock(ftdmchan);
 412         state = ftdmchan->state;
 413         ftdm_channel_unlock(ftdmchan);
 414         return state;
 415 }
 416 
 417 FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan)
 418 {
 419         const char *state;
 420         ftdm_channel_lock(ftdmchan);
 421         state = ftdm_channel_state2str(ftdmchan->state);
 422         ftdm_channel_unlock(ftdmchan);
 423         return state;
 424 }
 425 
 426 FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan)
 427 {
 428         int last_state;
 429         ftdm_channel_lock(ftdmchan);
 430         last_state = ftdmchan->last_state;
 431         ftdm_channel_unlock(ftdmchan);
 432         return last_state;
 433 }
 434 
 435 FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan)
 436 {
 437         const char *state;
 438         ftdm_channel_lock(ftdmchan);
 439         state = ftdm_channel_state2str(ftdmchan->last_state);
 440         ftdm_channel_unlock(ftdmchan);
 441         return state;
 442 }
 443 
 444 static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime)
 445 {
 446         char func[255];
 447         char line[255];
 448         char states[255];
 449         const char *filename = NULL;
 450         snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state));
 451         snprintf(func, sizeof(func), "[%s]", fchan->history[i].func);
 452         filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR);
 453         if (!filename) {
 454                 filename = fchan->history[i].file;
 455         } else {
 456                 filename++;
 457         }
 458         if (!(*prevtime)) {
 459                 *prevtime = fchan->history[i].time;
 460         }
 461         snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line);
 462         stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime));
 463         *prevtime = fchan->history[i].time;
 464 }
 465 
 466 FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan)
 467 {
 468         uint8_t i = 0;
 469         ftdm_time_t currtime = 0;
 470         ftdm_time_t prevtime = 0;
 471 
 472         ftdm_stream_handle_t stream = { 0 };
 473         FTDM_STANDARD_STREAM(stream);
 474         if (!fchan->history[0].file) {
 475                 stream.write_function(&stream, "-- No state history --\n");
 476                 return stream.data;
 477         }
 478 
 479         stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s", 
 480                         "-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n");
 481 
 482         for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) {
 483                 if (!fchan->history[i].file) {
 484                         break;
 485                 }
 486                 write_history_entry(fchan, &stream, i, &prevtime);
 487         }
 488 
 489         for (i = 0; i < fchan->hindex; i++) {
 490                 write_history_entry(fchan, &stream, i, &prevtime);
 491         }
 492 
 493         currtime = ftdm_current_time_in_ms();
 494 
 495         stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime));
 496 
 497         return stream.data;
 498 }
 499 
 500 FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan)
 501 {
 502         ftdm_channel_state_t state;
 503 
 504         ftdm_assert_return(fchan->span->state_processor, FTDM_FAIL, "Cannot process states without a state processor!\n");
 505         
 506         while (fchan->state_status == FTDM_STATE_STATUS_NEW) {
 507                 state = fchan->state;
 508                 ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Executing state processor for %s\n", ftdm_channel_state2str(fchan->state));
 509                 fchan->span->state_processor(fchan);
 510                 if (state == fchan->state && fchan->state_status == FTDM_STATE_STATUS_NEW) {
 511                         /* if the state did not change and is still NEW, the state status must go to PROCESSED
 512                          * otherwise we don't touch it since is a new state and the old state was
 513                          * already completed implicitly by the state_processor() function via some internal
 514                          * call to ftdm_set_state() */
 515                         fchan->state_status = FTDM_STATE_STATUS_PROCESSED;
 516                 }
 517         }
 518 
 519         return FTDM_SUCCESS;
 520 }
 521 
 522 FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
 523 {
 524         uint32_t j;
 525         for(j = 1; j <= span->chan_count; j++) {
 526                 if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) {
 527                         return 0;
 528                 }
 529         }
 530         return 1;
 531 }
 532 
 533 /* For Emacs:
 534  * Local Variables:
 535  * mode:c
 536  * indent-tabs-mode:t
 537  * tab-width:4
 538  * c-basic-offset:4
 539  * End:
 540  * For VIM:
 541  * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
 542  */

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