[svn-commits] nadi: branch group/trunk-cm-csel-hash r46349 - /team/group/trunk-cm-csel-hash...

svn-commits at lists.digium.com svn-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 svn-commits mailing list