[asterisk-commits] nadi: branch group/trunk-cm-csel-hash r46349 -
/team/group/trunk-cm-csel-hash...
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Fri Oct 27 02:00:09 MST 2006
Author: nadi
Date: Fri Oct 27 04:00:07 2006
New Revision: 46349
URL: http://svn.digium.com/view/asterisk?rev=46349&view=rev
Log:
introducing a state machine for chan_misdn, so far only used for a small count of events.
Modified:
team/group/trunk-cm-csel-hash/channels/chan_misdn.c
Modified: team/group/trunk-cm-csel-hash/channels/chan_misdn.c
URL: http://svn.digium.com/view/asterisk/team/group/trunk-cm-csel-hash/channels/chan_misdn.c?rev=46349&r1=46348&r2=46349&view=diff
==============================================================================
--- team/group/trunk-cm-csel-hash/channels/chan_misdn.c (original)
+++ team/group/trunk-cm-csel-hash/channels/chan_misdn.c Fri Oct 27 04:00:07 2006
@@ -89,6 +89,26 @@
#define MISDN_ASTERISK_TECH_PVT(ast) ((ast)->tech_pvt)
#define MISDN_ASTERISK_PVT(ast) 1
+enum asterisk_events {
+ AEVENT_NOTHING = EVENT_UNKNOWN + 1,
+ AEVENT_CALL,
+ AEVENT_HANGUP,
+ AEVENT_ANSWER,
+
+ AEVENT_CBUSY,
+ AEVENT_CRING,
+ AEVENT_CRINGING,
+ AEVENT_CANSWER,
+ AEVENT_CTAKEOFFHOOK,
+ AEVENT_COFFHOOK,
+ AEVENT_CFLASH,
+ AEVENT_CPROGRESS,
+ AEVENT_CPROCEEDING,
+ AEVENT_CCONGESTION,
+ AEVENT_CHOLD,
+ AEVENT_CUNHOLD,
+};
+
enum misdn_chan_state {
MISDN_NOTHING = 0, /*!< at beginning */
MISDN_WAITING4DIGS, /*!< when waiting for infos */
@@ -115,6 +135,8 @@
struct chan_list {
char allowed_bearers[BUFFERSIZE];
+
+ struct state_machine *sm;
enum misdn_chan_state state;
int need_queue_hangup;
@@ -185,6 +207,22 @@
int bytes_wrote;
char *samples;
char *ok;
+};
+
+struct state_machine_transition {
+ int state;
+ int event;
+ int state_next;
+ int event_out;
+ int (*handle)(void *p, int state, int event);
+};
+
+struct state_machine {
+ void *p;
+ int state;
+ struct state_machine_transition *table;
+ int num_rows;
+ int (*send_event)(void *p, int event);
};
/* Global Static Variables */
@@ -611,6 +649,142 @@
}
}
+/* State Machine: Generic */
+#define STATE_CONTINUE -1 /*!< use this in state_next, if there exists a following transition */
+#define STATE_ANY -2 /*!< wildcard for the state field in struct state_machine_transision */
+
+#define EVENT_DEFAULT -1 /*!< return value of handle(): statemachine will send the event as read from the transition table */
+#define EVENT_BREAK -2 /*!< return value of handle(): no event will be sent out, state_machine_run returns success */
+#define EVENT_NONE -3 /*!< return value of handle(): no event will be sent out, state_machine_run continues */
+#define EVENT_ANY -4 /*!< wildcard for the event field in struct state_machine_transision */
+
+static struct state_machine * state_machine_create (void *p,
+ int state,
+ struct state_machine_transition *table,
+ int num_rows,
+ int (*send_event)(void *p, int event))
+{
+ struct state_machine *sm = malloc(sizeof(struct state_machine));
+
+ chan_misdn_log(0, 0, "state_machine_create: Creating with state %d ...\n", state);
+
+ if (sm) {
+ sm->p = p;
+ sm->state = state;
+ sm->table = table;
+ sm->num_rows = num_rows;
+ sm->send_event = send_event;
+ }
+
+ return sm;
+}
+
+static void state_machine_destroy (struct state_machine *sm)
+{
+ chan_misdn_log(0, 0, "state_machine_destroy: Freeing at state %d ...\n", sm ? sm->state : -99 );
+
+ if (sm)
+ free(sm);
+}
+
+static int state_machine_run (struct state_machine *sm, int event)
+{
+ struct state_machine_transition *t;
+ int i = 0,
+ event_out = EVENT_DEFAULT,
+ err,
+ retval = -1;
+
+ chan_misdn_log(0, 0, "state_machine_run: Running event %d at state %d ...\n", event, sm->state);
+
+ for (; i < sm->num_rows; ++i) {
+ t = &sm->table[i];
+ if ((t->state == sm->state || t->state == STATE_ANY) &&
+ (t->event == event || t->event == EVENT_ANY)) {
+ if (t->handle)
+ event_out = t->handle(sm->p, sm->state, event);
+ switch (event_out) {
+ case EVENT_BREAK:
+ retval = 0;
+ goto out;
+ case EVENT_NONE:
+ break;
+ case EVENT_DEFAULT:
+ event_out = t->event_out;
+ default:
+ chan_misdn_log(0, 0, "state_machine_run: -> Sending event %d ...\n", event_out);
+ err = sm->send_event(sm->p, event_out);
+ chan_misdn_log(0, 0, "state_machine_run: -> Finished event %d, retval: %d ...\n", event_out, err);
+ if (err) {
+ retval = err;
+ goto out;
+ }
+ }
+ event_out = EVENT_DEFAULT;
+ if (t->state_next == STATE_CONTINUE)
+ continue;
+ sm->state = t->state_next;
+ retval = 0;
+ goto out;
+ }
+ }
+
+out:
+ chan_misdn_log(0, 0, "state_machine_run: Returning with value %d ...\n", retval);
+ return retval;
+}
+
+static inline void state_machine_set_state (struct state_machine *sm, int state)
+{
+ sm->state = state;
+}
+
+static inline int state_machine_get_state (struct state_machine *sm)
+{
+ return sm->state;
+}
+
+/* State Machine: mISDN */
+static int handle_aevent_hangup (void *p, int state, int event);
+static int handle_aevent_control (void *p, int state, int event) { return 0; }
+
+static struct state_machine_transition misdn_state_table[] = {
+ { MISDN_NOTHING, AEVENT_CALL, MISDN_CALLING, EVENT_SETUP, NULL },
+
+ { MISDN_CALLING, AEVENT_HANGUP, MISDN_CLEANING, EVENT_RELEASE_COMPLETE, NULL },
+ { MISDN_HOLDED, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_DIALING, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_CALLING_ACKNOWLEDGE, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_ALERTING, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_PROGRESS, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_PROCEEDING, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_CONNECTED, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_PRECONNECTED, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+ { MISDN_DISCONNECTED, AEVENT_HANGUP, MISDN_CLEANING, EVENT_RELEASE, NULL },
+ { MISDN_RELEASED, AEVENT_HANGUP, MISDN_CLEANING, EVENT_NONE, NULL },
+ { MISDN_CLEANING, AEVENT_HANGUP, MISDN_CLEANING, EVENT_NONE, NULL },
+ { MISDN_BUSY, AEVENT_HANGUP, MISDN_CLEANING, EVENT_NONE, NULL },
+ { MISDN_HOLD_DISCONNECT, AEVENT_HANGUP, MISDN_CLEANING, EVENT_RELEASE, handle_aevent_hangup },
+ { MISDN_CLEANING, AEVENT_HANGUP, MISDN_CLEANING, EVENT_NONE, handle_aevent_hangup },
+ { STATE_ANY, AEVENT_HANGUP, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_hangup },
+
+ { STATE_ANY, AEVENT_ANSWER, MISDN_CONNECTED, EVENT_CONNECT, NULL },
+
+ { STATE_ANY, AEVENT_CBUSY, MISDN_CLEANING, EVENT_DISCONNECT, handle_aevent_control },
+};
+
+static int send_event (void *p, int event)
+{
+ struct misdn_bchannel *bc = ((struct chan_list *)p)->bc;
+
+ if (event > EVENT_NOTHING && event < EVENT_UNKNOWN) {
+ misdn_lib_send_event(bc, event);
+ return 0;
+ }
+
+ return -1;
+}
+
/* Channel Selection */
static void * misdn_occupy (void *priv)
{
@@ -701,6 +875,9 @@
return NULL;
}
+ cl->sm = state_machine_create(cl, MISDN_NOTHING, misdn_state_table,
+ sizeof(misdn_state_table) / sizeof(struct state_machine_transition),
+ send_event);
cl->orginator = orig;
cl->need_queue_hangup = 1;
cl->need_hangup = 1;
@@ -1546,6 +1723,7 @@
ch->state = MISDN_CLEANING;
cl_dequeue_chan(&cl_te, ch);
+ state_machine_destroy(ch->sm);
free(ch);
}
@@ -2620,7 +2798,8 @@
else
chan_misdn_log(2, port, "NO OPTS GIVEN\n");
- r = misdn_lib_send_event(newbc, EVENT_SETUP);
+ if ((r = state_machine_run(ch->sm, AEVENT_CALL)))
+ chan_misdn_log(0, port, "state_machine_run failed on AEVENT_CALL: %d!\n", r);
/** we should have l3id after sending setup **/
ch->l3id = newbc->l3_id;
@@ -2641,9 +2820,9 @@
if (newbc->nt)
stop_bc_tones(ch);
- ch->state = MISDN_CALLING;
-
- return 0;
+ ch->state = state_machine_get_state(ch->sm);
+
+ return 0;
}
static enum ast_bridge_result misdn_bridge (struct ast_channel *c0, struct ast_channel *c1, int flags,
@@ -2734,7 +2913,8 @@
struct chan_list *p;
struct misdn_bchannel *bc;
const char *varcause;
- int tmpcause;
+ int tmpcause,
+ re;
if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast)))
return -1;
@@ -2782,73 +2962,64 @@
p->bc ? p->bc->pid : -1, ast->context, ast->exten, AST_CID_P(ast), get_ch_state(p),
p->l3id, bc->cause, bc->out_cause, get_ch_state(p));
- switch (p->state) {
- case MISDN_CALLING:
- p->state = MISDN_CLEANING;
- misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE);
- break;
+ if ((re = state_machine_run(p->sm, AEVENT_HANGUP))) {
+ chan_misdn_log(0, bc->port, "state_machine_run failed on AEVENT_HANGUP: %d!\n", re);
+ return -1;
+ }
+
+ p->state = state_machine_get_state(p->sm);
+ chan_misdn_log(1, bc->port, "Channel: %s hanguped new state:%s\n", ast->name, get_ch_state(p));
+
+ return 0;
+}
+
+static int handle_aevent_hangup (void *p, int state, int event)
+{
+ struct chan_list *ch = p;
+ struct misdn_bchannel *bc = ch->bc;
+ int re = EVENT_DEFAULT;
+
+ switch (state) {
case MISDN_HOLDED:
case MISDN_DIALING:
- start_bc_tones(p);
- hanguptone_indicate(p);
- if (bc->need_disconnect)
- misdn_lib_send_event(bc, EVENT_DISCONNECT);
- break;
case MISDN_CALLING_ACKNOWLEDGE:
- start_bc_tones(p);
- hanguptone_indicate(p);
- if (bc->need_disconnect)
- misdn_lib_send_event(bc, EVENT_DISCONNECT);
+ start_bc_tones(ch);
+ hanguptone_indicate(ch);
+ if (!bc->need_disconnect)
+ re = EVENT_NONE;
break;
case MISDN_ALERTING:
case MISDN_PROGRESS:
case MISDN_PROCEEDING:
- if (p->orginator != ORG_AST)
- hanguptone_indicate(p);
- if (bc->need_disconnect)
- misdn_lib_send_event(bc, EVENT_DISCONNECT);
+ if (ch->orginator != ORG_AST)
+ hanguptone_indicate(ch);
+ if (!bc->need_disconnect)
+ re = EVENT_NONE;
break;
case MISDN_CONNECTED:
case MISDN_PRECONNECTED:
/* Alerting or Disconect */
if (bc->nt) {
- start_bc_tones(p);
- hanguptone_indicate(p);
+ start_bc_tones(ch);
+ hanguptone_indicate(ch);
bc->progress_indicator = 8;
}
- if (bc->need_disconnect)
- misdn_lib_send_event(bc, EVENT_DISCONNECT);
- break;
- case MISDN_DISCONNECTED:
- misdn_lib_send_event(bc, EVENT_RELEASE);
- p->state = MISDN_CLEANING; /* MISDN_HUNGUP_FROM_AST; */
- break;
- case MISDN_RELEASED:
- case MISDN_CLEANING:
- p->state = MISDN_CLEANING;
- break;
- case MISDN_BUSY:
+ if (!bc->need_disconnect)
+ re = EVENT_NONE;
break;
case MISDN_HOLD_DISCONNECT:
/* need to send release here */
chan_misdn_log(1, bc->port, " --> cause %d\n --> out_cause %d\n", bc->cause, bc->out_cause);
bc->out_cause = -1;
- misdn_lib_send_event(bc, EVENT_RELEASE);
- p->state = MISDN_CLEANING;
break;
default:
- if (bc->nt) {
+ if (bc->nt)
bc->out_cause = -1;
- misdn_lib_send_event(bc, EVENT_RELEASE);
- p->state = MISDN_CLEANING;
- } else if (bc->need_disconnect)
- misdn_lib_send_event(bc, EVENT_DISCONNECT);
- }
-
- p->state = MISDN_CLEANING;
- chan_misdn_log(1, bc->port, "Channel: %s hanguped new state:%s\n", ast->name, get_ch_state(p));
-
- return 0;
+ else if (!bc->need_disconnect)
+ re = EVENT_RELEASE;
+ }
+
+ return re;
}
static int misdn_answer (struct ast_channel *ast)
@@ -2877,7 +3048,6 @@
p->bc->nojitter = 1;
}
- p->state = MISDN_CONNECTED;
misdn_lib_echo(p->bc, 0);
stop_indicate(p);
@@ -2886,7 +3056,9 @@
ast_copy_string(p->bc->cad, p->bc->dad, sizeof(p->bc->cad));
}
- misdn_lib_send_event(p->bc, EVENT_CONNECT);
+ state_machine_run(p->sm, AEVENT_ANSWER);
+ p->state = state_machine_get_state(p->sm);
+
start_bc_tones(p);
return 0;
@@ -3332,6 +3504,7 @@
hangup_chan(ch);
else {
ch->state = MISDN_CALLING_ACKNOWLEDGE;
+ state_machine_set_state(ch->sm, ch->state);
ast_setstate(ch->ast, AST_STATE_DOWN);
hangup_chan(ch);
ch->ast = NULL;
@@ -3343,6 +3516,7 @@
if (bc->nt)
hanguptone_indicate(ch);
ch->state = MISDN_EXTCANTMATCH;
+ state_machine_set_state(ch->sm, ch->state);
bc->out_cause = 1;
misdn_lib_send_event(bc, EVENT_DISCONNECT);
break;
@@ -3357,6 +3531,7 @@
}
if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) {
ch->state = MISDN_DIALING;
+ state_machine_set_state(ch->sm, ch->state);
stop_indicate(ch);
if (pbx_start_chan(ch) < 0) {
hangup_chan(ch);
@@ -3480,6 +3655,7 @@
chan_misdn_log(0, bc->port, "Bearer Not allowed!\b");
bc->out_cause = 88;
ch->state = MISDN_EXTCANTMATCH;
+ state_machine_set_state(ch->sm, ch->state);
misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE);
return RESPONSE_OK;
}
@@ -3491,6 +3667,7 @@
hangup_chan(ch);
else {
ch->state = MISDN_CALLING_ACKNOWLEDGE;
+ state_machine_set_state(ch->sm, ch->state);
ast_setstate(chan, AST_STATE_DOWN);
hangup_chan(ch);
ch->ast = NULL;
@@ -3512,6 +3689,7 @@
if (bc->nt)
hanguptone_indicate(ch);
ch->state = MISDN_EXTCANTMATCH;
+ state_machine_set_state(ch->sm, ch->state);
bc->out_cause = 1;
misdn_lib_send_event(bc, bc->nt ? EVENT_RELEASE_COMPLETE : EVENT_RELEASE);
break;
@@ -3519,6 +3697,7 @@
if (!ch->overlap_dial && ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) {
ch->state = MISDN_DIALING;
+ state_machine_set_state(ch->sm, ch->state);
misdn_lib_send_event(bc, (bc->nt || (bc->need_more_infos && misdn_lib_is_ptp(bc->port))) ?
EVENT_SETUP_ACKNOWLEDGE : EVENT_PROCEEDING);
if (pbx_start_chan(ch) < 0) {
@@ -3532,6 +3711,7 @@
} else {
if (bc->sending_complete) {
ch->state = MISDN_EXTCANTMATCH;
+ state_machine_set_state(ch->sm, ch->state);
bc->out_cause = 1;
chan_misdn_log(0, bc->port, " --> sending_complete so we never match ..\n");
misdn_lib_send_event(bc, bc->nt ? EVENT_RELEASE_COMPLETE : EVENT_RELEASE);
@@ -3546,6 +3726,7 @@
else
dialtone_indicate(ch);
ch->state = MISDN_WAITING4DIGS;
+ state_machine_set_state(ch->sm, ch->state);
if (ch->overlap_dial && !dad_len) {
ast_mutex_lock(&ch->overlap_tv_lock);
ch->overlap_tv = ast_tvnow();
@@ -3559,6 +3740,7 @@
break;
case EVENT_SETUP_ACKNOWLEDGE:
ch->state = MISDN_CALLING_ACKNOWLEDGE;
+ state_machine_set_state(ch->sm, ch->state);
if (bc->channel)
update_name(ch->ast, bc->port, bc->channel);
if (!ast_strlen_zero(bc->infos_pending)) {
@@ -3576,6 +3758,7 @@
if (misdn_cap_is_speech(bc->capability) && misdn_inband_avail(bc))
start_bc_tones(ch);
ch->state = MISDN_PROCEEDING;
+ state_machine_set_state(ch->sm, ch->state);
ast_queue_control(ch->ast, AST_CONTROL_PROCEEDING);
break;
case EVENT_PROGRESS:
@@ -3584,10 +3767,12 @@
start_bc_tones(ch);
ast_queue_control(ch->ast, AST_CONTROL_PROGRESS);
ch->state = MISDN_PROGRESS;
+ state_machine_set_state(ch->sm, ch->state);
}
break;
case EVENT_ALERTING:
ch->state = MISDN_ALERTING;
+ state_machine_set_state(ch->sm, ch->state);
ast_queue_control(ch->ast, AST_CONTROL_RINGING);
ast_setstate(ch->ast, AST_STATE_RINGING);
cb_log(1, bc->port, "Set State Ringing\n");
@@ -3621,6 +3806,7 @@
ch->addr = bc->addr;
start_bc_tones(ch);
ch->state = MISDN_CONNECTED;
+ state_machine_set_state(ch->sm, ch->state);
ast_queue_control(ch->ast, AST_CONTROL_ANSWER);
break;
case EVENT_DISCONNECT:
@@ -3629,12 +3815,13 @@
/* we might not have an ch->ast ptr here anymore */
if (ch) {
chan_misdn_log(3, bc->port, " --> org:%d nt:%d, inbandavail:%d state:%d\n", ch->orginator, bc->nt, misdn_inband_avail(bc), ch->state);
+ ch->state = MISDN_DISCONNECTED;
+ state_machine_set_state(ch->sm, ch->state);
if (ch->orginator == ORG_AST && !bc->nt && misdn_inband_avail(bc) && ch->state != MISDN_CONNECTED) {
/* If there's inband information available (e.g. a recorded message saying what was wrong with the
* dialled number, or perhaps even giving an alternative number, then play it instead of
* immediately releasing the call */
chan_misdn_log(1, bc->port, " --> Inband Info Avail, not sending RELEASE\n");
- ch->state = MISDN_DISCONNECTED;
start_bc_tones(ch);
break;
}
@@ -3664,6 +3851,7 @@
hangup_chan(ch);
release_chan(bc);
ch->state = MISDN_CLEANING;
+ state_machine_set_state(ch->sm, ch->state);
break;
case EVENT_CLEANUP:
stop_bc_tones(ch);
@@ -3791,6 +3979,7 @@
/*remember the channel again*/
ch->bc = bc;
ch->state = MISDN_CONNECTED;
+ state_machine_set_state(ch->sm, ch->state);
hold_ast = AST_BRIDGED_P(ch->ast);
if (hold_ast)
@@ -3814,6 +4003,7 @@
if (bridged){
chan_misdn_log(2, bc->port, "Bridge Partner is of type: %s\n", bridged->tech->type);
ch->state = MISDN_HOLDED;
+ state_machine_set_state(ch->sm, ch->state);
ch->l3id = bc->l3_id;
misdn_lib_send_event(bc, EVENT_HOLD_ACKNOWLEDGE);
/* XXX This should queue an AST_CONTROL_HOLD frame on this channel
@@ -3839,6 +4029,7 @@
struct chan_list *ch_br = bridged ? MISDN_ASTERISK_TECH_PVT(bridged) : NULL;
if (ch_br && ch_br->bc && ast_exists_extension(bridged, ch->context, (char *)bc->fac_in.u.CDeflection.DeflectedToNumber, 1, bc->oad)) {
ch_br->state = MISDN_DIALING;
+ state_machine_set_state(ch->sm, ch->state);
if (pbx_start_chan(ch_br) < 0)
chan_misdn_log(-1, ch_br->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n");
}
More information about the asterisk-commits
mailing list