[libpri-commits] rmudgett: branch rmudgett/q931_fsm r2246 - /team/rmudgett/q931_fsm/

SVN commits to the libpri project libpri-commits at lists.digium.com
Wed Mar 9 20:26:44 CST 2011


Author: rmudgett
Date: Wed Mar  9 20:26:39 2011
New Revision: 2246

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=2246
Log:
* Added unit test of the FSM driver submodule.

* Restored FSM_EV_INIT.  The INIT event must be separate from the PROLOG
event to allow transitions from a state to one of it's superstates.

* Driver is now functional.

Added:
    team/rmudgett/q931_fsm/fsmtest.c   (with props)
Modified:
    team/rmudgett/q931_fsm/Makefile
    team/rmudgett/q931_fsm/pri_fsm.c
    team/rmudgett/q931_fsm/pri_fsm.h

Modified: team/rmudgett/q931_fsm/Makefile
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/Makefile?view=diff&rev=2246&r1=2245&r2=2246
==============================================================================
--- team/rmudgett/q931_fsm/Makefile (original)
+++ team/rmudgett/q931_fsm/Makefile Wed Mar  9 20:26:39 2011
@@ -205,6 +205,9 @@
 rosetest: rosetest.o
 	$(CC) -o rosetest rosetest.o -L. -lpri $(CFLAGS)
 
+fsmtest: fsmtest.o
+	$(CC) -o fsmtest fsmtest.o -L. -lpri $(CFLAGS)
+
 MAKE_DEPS= -MD -MT $@ -MF .$(subst /,_,$@).d -MP
 
 %.o: %.c

Added: team/rmudgett/q931_fsm/fsmtest.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/fsmtest.c?view=auto&rev=2246
==============================================================================
--- team/rmudgett/q931_fsm/fsmtest.c (added)
+++ team/rmudgett/q931_fsm/fsmtest.c Wed Mar  9 20:26:39 2011
@@ -1,0 +1,513 @@
+/*
+ * libpri: An implementation of Primary Rate ISDN
+ *
+ * Copyright (C) 2011 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ *
+ * In addition, when this program is distributed with Asterisk in
+ * any form that would qualify as a 'combined work' or as a
+ * 'derivative work' (but not mere aggregation), you can redistribute
+ * and/or modify the combination under the terms of the license
+ * provided with that copy of Asterisk, instead of the license
+ * terms granted here.
+ */
+
+/*!
+ * \file
+ * \brief Unit test of the FSM driver submodule.
+ *
+ * \author Richard Mudgett <rmudgett at digium.com>
+ */
+
+
+#include "compat.h"
+#include "libpri.h"
+#include "pri_internal.h"
+#include "pri_fsm.h"	//BUGBUG
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum test_fsm_ev {
+	TST_EV_A = FSM_EV_FIRST_USER_EV,
+	TST_EV_B,
+	TST_EV_C,
+	TST_EV_D,
+	TST_EV_E,
+	TST_EV_F,
+	TST_EV_G,
+	TST_EV_H,
+	TST_EV_J,
+};
+
+/* ------------------------------------------------------------------- */
+
+static void tst_pri_message(struct pri *ctrl, char *stuff)
+{
+	fprintf(stdout, "%s", stuff);
+}
+
+static void tst_pri_error(struct pri *ctrl, char *stuff)
+{
+	fprintf(stdout, "%s", stuff);
+	fprintf(stderr, "%s", stuff);
+}
+
+/* ------------------------------------------------------------------- */
+
+/*!
+ * \internal
+ * \brief Convert the given FSM event code to string.
+ *
+ * \param event FSM event code to convert.
+ *
+ * \return String equivalent of event code.
+ */
+static const char *tst_ev2str(int event)
+{
+	if (event < FSM_EV_FIRST_USER_EV) {
+		return fsm_ev2str(event);
+	}
+	switch (event) {
+	case TST_EV_A:
+		return "TST_EV_A";
+	case TST_EV_B:
+		return "TST_EV_B";
+	case TST_EV_C:
+		return "TST_EV_C";
+	case TST_EV_D:
+		return "TST_EV_D";
+	case TST_EV_E:
+		return "TST_EV_E";
+	case TST_EV_F:
+		return "TST_EV_F";
+	case TST_EV_G:
+		return "TST_EV_G";
+	case TST_EV_H:
+		return "TST_EV_H";
+	case TST_EV_J:
+		return "TST_EV_J";
+	default:
+		return "TST_EV_unknown";
+	}
+}
+
+/*!
+ * \internal
+ * \brief Simple event posting.
+ * 
+ * \param ctrl D channel controller. 
+ * \param fsm Event is sent to this FSM.
+ * \param code Event code.
+ *  
+ * \return Nothing
+ */
+static void tst_simple_post(struct pri *ctrl, struct fsm_ctrl *fsm, int code)
+{
+	struct fsm_event ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.code = code;
+	fsm_event_post(ctrl, fsm, &ev);
+}
+
+/*!
+ * \internal
+ * \brief Simple event pushing.
+ * 
+ * \param ctrl D channel controller. 
+ * \param fsm Event is sent to this FSM.
+ * \param code Event code.
+ *  
+ * \return Nothing
+ */
+static void tst_simple_push(struct pri *ctrl, struct fsm_ctrl *fsm, int code)
+{
+	struct fsm_event ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.code = code;
+	fsm_event_push(ctrl, fsm, &ev);
+}
+
+static void *tst_state_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+static void *tst_state_1_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+static void *tst_state_1_1_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+static void *tst_state_1_1_2(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+static void *tst_state_1_2(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+static void *tst_state_1_3(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+static void *tst_state_1_3_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+
+#define ACT_DEBUG(ctrl, fsm, event)	\
+	pri_message(ctrl, "%s:  Act %s(%s)\n", fsm->name, __PRETTY_FUNCTION__, tst_ev2str(event->code))
+
+/*!
+ * \internal
+ * \brief State 1.1.1.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to process.
+ *
+ * \return The value has various meanings depending upon what
+ * event was passed in.
+ * \see enum fsm_ev event descriptions for return value.
+ */
+static void *tst_state_1_1_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
+{
+	switch (event->code) {
+	case FSM_EV_GET_SUPERSTATE:
+		return tst_state_1_1;
+	case FSM_EV_GET_STATE_NAME:
+		return (void *) __PRETTY_FUNCTION__;
+	case FSM_EV_GET_EV_NAME:
+		return (void *) tst_ev2str(event->parms.num);
+	case FSM_EV_GET_DEBUG:
+		return FSM_IS_DEBUG(ctrl->debug & PRI_DEBUG_Q931_STATE);
+	case FSM_EV_PROLOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case FSM_EV_INIT:
+		return NULL;
+	case FSM_EV_EPILOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case TST_EV_A:
+		ACT_DEBUG(ctrl, fsm, event);
+		tst_simple_post(ctrl, fsm, TST_EV_B);
+		return NULL;
+	case TST_EV_B:
+		ACT_DEBUG(ctrl, fsm, event);
+		tst_simple_push(ctrl, fsm, TST_EV_D);
+		tst_simple_push(ctrl, fsm, TST_EV_C);
+		tst_simple_post(ctrl, fsm, TST_EV_E);
+		fsm_transition(ctrl, ctrl->debug & PRI_DEBUG_Q931_STATE, fsm, tst_state_1_1_1);
+		return NULL;
+	case TST_EV_C:
+		ACT_DEBUG(ctrl, fsm, event);
+		fsm_transition(ctrl, ctrl->debug & PRI_DEBUG_Q931_STATE, fsm, tst_state_1_1_2);
+		return NULL;
+	case TST_EV_E:
+		ACT_DEBUG(ctrl, fsm, event);
+		tst_simple_post(ctrl, fsm, TST_EV_F);
+		fsm_transition(ctrl, ctrl->debug & PRI_DEBUG_Q931_STATE, fsm, tst_state_1_2);
+		return NULL;
+	default:
+		break;
+	}
+	return tst_state_1_1;
+}
+
+/*!
+ * \internal
+ * \brief State 1.1.2.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to process.
+ *
+ * \return The value has various meanings depending upon what
+ * event was passed in.
+ * \see enum fsm_ev event descriptions for return value.
+ */
+static void *tst_state_1_1_2(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
+{
+	switch (event->code) {
+	case FSM_EV_GET_SUPERSTATE:
+		return tst_state_1_1;
+	case FSM_EV_GET_STATE_NAME:
+		return (void *) __PRETTY_FUNCTION__;
+	case FSM_EV_GET_EV_NAME:
+		return (void *) tst_ev2str(event->parms.num);
+	case FSM_EV_GET_DEBUG:
+		return FSM_IS_DEBUG(ctrl->debug & PRI_DEBUG_Q931_STATE);
+	case FSM_EV_PROLOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case FSM_EV_INIT:
+		return NULL;
+	case FSM_EV_EPILOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case TST_EV_D:
+		ACT_DEBUG(ctrl, fsm, event);
+		fsm_transition(ctrl, ctrl->debug & PRI_DEBUG_Q931_STATE, fsm, tst_state_1_1);
+		return NULL;
+	case TST_EV_H:
+		ACT_DEBUG(ctrl, fsm, event);
+		fsm_transition(ctrl, ctrl->debug & PRI_DEBUG_Q931_STATE, fsm, NULL);
+		return NULL;
+	default:
+		break;
+	}
+	return tst_state_1_1;
+}
+
+/*!
+ * \internal
+ * \brief State 1.1.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to process.
+ *
+ * \return The value has various meanings depending upon what
+ * event was passed in.
+ * \see enum fsm_ev event descriptions for return value.
+ */
+static void *tst_state_1_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
+{
+	switch (event->code) {
+	case FSM_EV_GET_SUPERSTATE:
+		return tst_state_1;
+	case FSM_EV_GET_STATE_NAME:
+		return (void *) __PRETTY_FUNCTION__;
+	case FSM_EV_GET_EV_NAME:
+		return (void *) tst_ev2str(event->parms.num);
+	case FSM_EV_GET_DEBUG:
+		return FSM_IS_DEBUG(ctrl->debug & PRI_DEBUG_Q931_STATE);
+	case FSM_EV_PROLOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case FSM_EV_INIT:
+		return tst_state_1_1_1;
+	case FSM_EV_EPILOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	default:
+		break;
+	}
+	return tst_state_1;
+}
+
+/*!
+ * \internal
+ * \brief State 1.2.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to process.
+ *
+ * \return The value has various meanings depending upon what
+ * event was passed in.
+ * \see enum fsm_ev event descriptions for return value.
+ */
+static void *tst_state_1_2(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
+{
+	switch (event->code) {
+	case FSM_EV_GET_SUPERSTATE:
+		return tst_state_1;
+	case FSM_EV_GET_STATE_NAME:
+		return (void *) __PRETTY_FUNCTION__;
+	case FSM_EV_GET_EV_NAME:
+		return (void *) tst_ev2str(event->parms.num);
+	case FSM_EV_GET_DEBUG:
+		return FSM_IS_DEBUG(ctrl->debug & PRI_DEBUG_Q931_STATE);
+	case FSM_EV_PROLOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case FSM_EV_INIT:
+		return NULL;
+	case FSM_EV_EPILOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case TST_EV_F:
+		ACT_DEBUG(ctrl, fsm, event);
+		tst_simple_post(ctrl, fsm, TST_EV_G);
+		fsm_transition(ctrl, ctrl->debug & PRI_DEBUG_Q931_STATE, fsm, tst_state_1_3);
+		return NULL;
+	default:
+		break;
+	}
+	return tst_state_1;
+}
+
+/*!
+ * \internal
+ * \brief State 1.3.1.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to process.
+ *
+ * \return The value has various meanings depending upon what
+ * event was passed in.
+ * \see enum fsm_ev event descriptions for return value.
+ */
+static void *tst_state_1_3_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
+{
+	switch (event->code) {
+	case FSM_EV_GET_SUPERSTATE:
+		return tst_state_1_3;
+	case FSM_EV_GET_STATE_NAME:
+		return (void *) __PRETTY_FUNCTION__;
+	case FSM_EV_GET_EV_NAME:
+		return (void *) tst_ev2str(event->parms.num);
+	case FSM_EV_GET_DEBUG:
+		return FSM_IS_DEBUG(ctrl->debug & PRI_DEBUG_Q931_STATE);
+	case FSM_EV_PROLOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case FSM_EV_INIT:
+		return NULL;
+	case FSM_EV_EPILOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	default:
+		break;
+	}
+	return tst_state_1_3;
+}
+
+/*!
+ * \internal
+ * \brief State 1.3.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to process.
+ *
+ * \return The value has various meanings depending upon what
+ * event was passed in.
+ * \see enum fsm_ev event descriptions for return value.
+ */
+static void *tst_state_1_3(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
+{
+	switch (event->code) {
+	case FSM_EV_GET_SUPERSTATE:
+		return tst_state_1;
+	case FSM_EV_GET_STATE_NAME:
+		return (void *) __PRETTY_FUNCTION__;
+	case FSM_EV_GET_EV_NAME:
+		return (void *) tst_ev2str(event->parms.num);
+	case FSM_EV_GET_DEBUG:
+		return FSM_IS_DEBUG(ctrl->debug & PRI_DEBUG_Q931_STATE);
+	case FSM_EV_PROLOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case FSM_EV_INIT:
+		return tst_state_1_3_1;
+	case FSM_EV_EPILOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case TST_EV_G:
+		ACT_DEBUG(ctrl, fsm, event);
+		fsm_transition(ctrl, ctrl->debug & PRI_DEBUG_Q931_STATE, fsm, tst_state_1_1_2);
+		return NULL;
+	default:
+		break;
+	}
+	return tst_state_1;
+}
+
+/*!
+ * \internal
+ * \brief State 1.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to process.
+ *
+ * \return The value has various meanings depending upon what
+ * event was passed in.
+ * \see enum fsm_ev event descriptions for return value.
+ */
+static void *tst_state_1(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
+{
+	switch (event->code) {
+	case FSM_EV_GET_SUPERSTATE:
+		return fsm_top_state;
+	case FSM_EV_GET_STATE_NAME:
+		return (void *) __PRETTY_FUNCTION__;
+	case FSM_EV_GET_EV_NAME:
+		return (void *) tst_ev2str(event->parms.num);
+	case FSM_EV_GET_DEBUG:
+		return FSM_IS_DEBUG(ctrl->debug & PRI_DEBUG_Q931_STATE);
+	case FSM_EV_PROLOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	case FSM_EV_INIT:
+		return tst_state_1_1;
+	case FSM_EV_EPILOG:
+		ACT_DEBUG(ctrl, fsm, event);
+		break;
+	default:
+		break;
+	}
+	return fsm_top_state;
+}
+
+/*!
+ * \internal
+ * \brief Destructor of the test FSM.
+ *
+ * \param ctrl D channel controller.
+ * \param parms Which instance of the FSM is being destoyed.
+ *
+ * \return Nothing
+ */
+static void tst_fsm_destructor(struct pri *ctrl, struct fsm_ctrl *fsm, void *parms)
+{
+	pri_message(ctrl, DBGHEAD "\n", DBGINFO);
+	*(int *) parms = *((int *) parms) + 1;
+}
+
+/*!
+ * \brief FSM driver test program.
+ *
+ * \param argc Program argument count.
+ * \param argv Program argument string array.
+ *
+ * \retval 0 on success.
+ * \retval Nonzero on error.
+ */
+int main(int argc, char *argv[])
+{
+	static struct pri dummy_ctrl;
+	static struct fsm_queue ev_q;
+	static struct fsm_ctrl fsm;
+	static int fsm_destroyed;
+
+	pri_set_message(tst_pri_message);
+	pri_set_error(tst_pri_error);
+
+	memset(&dummy_ctrl, 0, sizeof(dummy_ctrl));
+	dummy_ctrl.debug = PRI_DEBUG_Q931_STATE;
+
+	/* For sanity specify what version of libpri we are testing. */
+	pri_error(&dummy_ctrl, "libpri version tested: %s\n", pri_get_version());
+
+	/* Setup FSM */
+	fsm.state = NULL;
+	fsm.destructor = tst_fsm_destructor;
+	fsm.name = "Test FSM";
+	fsm.que = &ev_q;
+	fsm.parms = &fsm_destroyed;
+	fsm_init(&dummy_ctrl, &fsm, tst_state_1);
+
+	/* Nothing to do. */
+	fsm_run(&dummy_ctrl, &ev_q);
+
+	/* Post event. */
+	tst_simple_post(&dummy_ctrl, &fsm, TST_EV_A);
+	fsm_run(&dummy_ctrl, &ev_q);
+
+	/* Post event. */
+	tst_simple_post(&dummy_ctrl, &fsm, TST_EV_H);
+	fsm_run(&dummy_ctrl, &ev_q);
+
+	if (fsm_destroyed != 1) {
+		pri_error(&dummy_ctrl, "Test FSM destroyed %d times!\n", fsm_destroyed);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* end fsmtest.c */

Propchange: team/rmudgett/q931_fsm/fsmtest.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/rmudgett/q931_fsm/fsmtest.c
------------------------------------------------------------------------------
    svn:keywords = 'Author Date Id Revision'

Propchange: team/rmudgett/q931_fsm/fsmtest.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/rmudgett/q931_fsm/pri_fsm.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/pri_fsm.c?view=diff&rev=2246&r1=2245&r2=2246
==============================================================================
--- team/rmudgett/q931_fsm/pri_fsm.c (original)
+++ team/rmudgett/q931_fsm/pri_fsm.c Wed Mar  9 20:26:39 2011
@@ -58,6 +58,8 @@
 		return "FSM_EV_GET_EV_NAME";
 	case FSM_EV_GET_DEBUG:
 		return "FSM_EV_GET_DEBUG";
+	case FSM_EV_INIT:
+		return "FSM_EV_INIT";
 	case FSM_EV_PROLOG:
 		return "FSM_EV_PROLOG";
 	case FSM_EV_EPILOG:
@@ -73,29 +75,29 @@
  * \brief Push an event on the head of the event queue.
  *
  * \param ctrl D channel controller.
+ * \param fsm FSM controller.
  * \param event Event to push.
  *
  * \return Nothing
  */
-void fsm_event_push(struct pri *ctrl, struct fsm_event *event)
+void fsm_event_push(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
 {
 	unsigned next_head;
 	struct fsm_queue *que;
 	struct fsm_event dbg_event;
 	fsm_state state;
 
-	state = event->fsm->state;
-
-	dbg_event.fsm = event->fsm;
+	state = fsm->state;
+
 	dbg_event.code = FSM_EV_GET_DEBUG;
-	if (state(ctrl, &dbg_event)) {
+	if (state(ctrl, fsm, &dbg_event)) {
 		dbg_event.code = FSM_EV_GET_EV_NAME;
 		dbg_event.parms.num = event->code;
-		pri_message(ctrl, "%s: Push event %s\n", event->fsm->name,
-			(char *) state(ctrl, &dbg_event));
-	}
-
-	que = event->fsm->que;
+		pri_message(ctrl, "%s: Push event %s\n", fsm->name,
+			(char *) state(ctrl, fsm, &dbg_event));
+	}
+
+	que = fsm->que;
 
 	/* Determine previous head index. */
 	if (que->head) {
@@ -110,36 +112,37 @@
 
 	/* Put event in the queue. */
 	que->head = next_head;
-	que->events[que->head] = *event;
+	que->events[que->head].fsm = fsm;
+	que->events[que->head].event = *event;
 }
 
 /*!
  * \brief Post an event on the tail of the event queue.
  *
  * \param ctrl D channel controller.
+ * \param fsm FSM controller.
  * \param event Event to post.
  *
  * \return Nothing
  */
-void fsm_event_post(struct pri *ctrl, struct fsm_event *event)
+void fsm_event_post(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
 {
 	unsigned next_tail;
 	struct fsm_queue *que;
 	struct fsm_event dbg_event;
 	fsm_state state;
 
-	state = event->fsm->state;
-
-	dbg_event.fsm = event->fsm;
+	state = fsm->state;
+
 	dbg_event.code = FSM_EV_GET_DEBUG;
-	if (state(ctrl, &dbg_event)) {
+	if (state(ctrl, fsm, &dbg_event)) {
 		dbg_event.code = FSM_EV_GET_EV_NAME;
 		dbg_event.parms.num = event->code;
-		pri_message(ctrl, "%s: Post event %s\n", event->fsm->name,
-			(char *) state(ctrl, &dbg_event));
-	}
-
-	que = event->fsm->que;
+		pri_message(ctrl, "%s: Post event %s\n", fsm->name,
+			(char *) state(ctrl, fsm, &dbg_event));
+	}
+
+	que = fsm->que;
 
 	/* Determine next tail index. */
 	next_tail = que->tail + 1;
@@ -152,7 +155,8 @@
 	}
 
 	/* Put event in the queue. */
-	que->events[que->tail] = *event;
+	que->events[que->tail].fsm = fsm;
+	que->events[que->tail].event = *event;
 	que->tail = next_tail;
 }
 
@@ -160,13 +164,14 @@
  * \brief Top state of all FSMs.
  *
  * \param ctrl D channel controller.
+ * \param fsm FSM controller.
  * \param event Event to process.
  *
  * \return The value has various meanings depending upon what
  * event was passed in.
  * \see enum fsm_ev event descriptions for return value.
  */
-void *fsm_top_state(struct pri *ctrl, struct fsm_event *event)
+void *fsm_top_state(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event)
 {
 	switch (event->code) {
 	case FSM_EV_GET_SUPERSTATE:
@@ -179,12 +184,13 @@
 	case FSM_EV_GET_DEBUG:
 		/* Noone should be doing this since how are we to know? */
 		pri_error(ctrl, DBGHEAD "Asking for FSM debug output enable!\n", DBGINFO);
-		return (void *) 1;
+		return FSM_IS_DEBUG(1);
 	case FSM_EV_PROLOG:
+	case FSM_EV_INIT:
 	case FSM_EV_EPILOG:
 	default:
 		pri_error(ctrl, DBGHEAD "%s: Unhandled event: %s(%d)!\n", DBGINFO,
-			event->fsm->name, fsm_ev2str(event->code), event->code);
+			fsm->name, fsm_ev2str(event->code), event->code);
 		break;
 	}
 	return NULL;
@@ -197,40 +203,38 @@
  * \param debug TRUE if FSM debug output enabled.
  * \param fsm FSM that is transitioning states.
  * \param dest Transitioning to state. (NULL if terminal)
- * \param src Transitioning from state. (NULL if initial)
- *
- * \return Nothing
- */
-void fsm_transition(struct pri *ctrl, int debug, struct fsm_ctrl *fsm, fsm_state dest, fsm_state src)
+ *
+ * \return Nothing
+ */
+void fsm_transition(struct pri *ctrl, int debug, struct fsm_ctrl *fsm, fsm_state dest)
 {
 	struct fsm_event local_event;
-	fsm_state epilog_state[FSM_MAX_SUPERSTATE_NESTING];
-	fsm_state prolog_state[FSM_MAX_SUPERSTATE_NESTING];
+	fsm_state epilog_state[FSM_MAX_SUPERSTATE_NESTING + 1];/* Plus top state. */
+	fsm_state prolog_state[FSM_MAX_SUPERSTATE_NESTING + 1];/* Plus top state. */
+	fsm_state src;
 	const char *epilog_name;
 	const char *prolog_name;
+	const char *init_name;
 	const char *src_name;
 	const char *dest_name;
-	int epilog_index;
-	int prolog_index;
-	int idx;
-
-	local_event.fsm = fsm;
-
-	/* Get original state names. */
-	local_event.code = FSM_EV_GET_STATE_NAME;
-	if (src && src != fsm_top_state) {
-		src_name = src(ctrl, &local_event);
-	} else {
+	int epilog_index;/* Must be signed. */
+	int prolog_index;/* Must be signed. */
+	int idx;/* Must be signed. */
+
+	src = fsm->state;
+	if (!src || src == fsm_top_state) {
 		/* This is the initial transition to start the FSM. */
 		fsm->state = fsm_top_state;
-		src_name = fsm_top_state(ctrl, &local_event);
 		src = NULL;
 	}
+
+	/* Get original destination state name. */
+	local_event.code = FSM_EV_GET_STATE_NAME;
 	if (dest && dest != fsm_top_state) {
-		dest_name = dest(ctrl, &local_event);
+		dest_name = dest(ctrl, fsm, &local_event);
 	} else {
 		/* This is the terminal transition to end the FSM. */
-		dest_name = fsm_top_state(ctrl, &local_event);
+		dest_name = fsm_top_state(ctrl, fsm, &local_event);
 		dest = NULL;
 	}
 
@@ -242,36 +246,56 @@
 
 	/* Build src superstate nesting stack. */
 	epilog_index = 0;
-	epilog_state[epilog_index++] = src;
-	while (src && src != fsm_top_state) {
+	epilog_state[epilog_index] = src;
+	while (src) {
 		if (FSM_MAX_SUPERSTATE_NESTING <= epilog_index) {
+			local_event.code = FSM_EV_GET_STATE_NAME;
+			src_name = fsm->state(ctrl, fsm, &local_event);
+
 			pri_error(ctrl, "%s: FSM source state %s nested too deep!\n", fsm->name,
 				src_name);
 			return;
 		}
-		src = src(ctrl, &local_event);
-		epilog_state[epilog_index++] = src;
+		src = src(ctrl, fsm, &local_event);
+		if (src == fsm_top_state) {
+			src = NULL;
+		}
+		epilog_state[++epilog_index] = src;
 	}
 
 	/* Build dest superstate nesting stack. */
 	prolog_index = 0;
-	prolog_state[prolog_index++] = dest;
-	while (dest && dest != fsm_top_state) {
+	prolog_state[prolog_index] = dest;
+	while (dest) {
 		if (FSM_MAX_SUPERSTATE_NESTING <= prolog_index) {
 			pri_error(ctrl, "%s: FSM destination state %s nested too deep!\n", fsm->name,
 				dest_name);
 			return;
 		}
-		dest = dest(ctrl, &local_event);
-		prolog_state[prolog_index++] = dest;
+		dest = dest(ctrl, fsm, &local_event);
+		if (dest == fsm_top_state) {
+			dest = NULL;
+		}
+		prolog_state[++prolog_index] = dest;
+	}
+
+	if (!epilog_index && !prolog_index) {
+		pri_error(ctrl, "%s: FSM initial transition is termination transition!\n",
+			fsm->name);
+		return;
 	}
 
 	/* Find first non-common superstate level. */
 	for (;;) {
 		--epilog_index;
 		--prolog_index;
-		if (!epilog_index || !prolog_index) {
-			/* No more nested superstates. */
+		if (epilog_index < 0 || prolog_index < 0) {
+			/* No more epilogs or prologs in stack. */
+			if (epilog_index == prolog_index) {
+				/* This is a state transition to self. */
+				epilog_index = 0;
+				prolog_index = 0;
+			}
 			break;
 		}
 		if (epilog_state[epilog_index] != prolog_state[prolog_index]) {
@@ -280,72 +304,85 @@
 	}
 
 	/* Execute state epilogs */
-	if (epilog_state[epilog_index]) {
+	if (0 <= epilog_index) {
 		epilog_name = fsm_ev2str(FSM_EV_EPILOG);
 
 		for (idx = 0; idx <= epilog_index; ++idx) {
 			src = epilog_state[idx];
 			if (debug) {
 				local_event.code = FSM_EV_GET_STATE_NAME;
-				src_name = (const char *) src(ctrl, &local_event);
+				src_name = src(ctrl, fsm, &local_event);
 
 				pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, epilog_name,
 					src_name);
 			}
 			local_event.code = FSM_EV_EPILOG;
-			src(ctrl, &local_event);
+			src(ctrl, fsm, &local_event);
 		}
 	}
 
 	/* Execute known state prologs */
-	if (prolog_state[prolog_index]) {
+	if (0 <= prolog_index) {
 		prolog_name = fsm_ev2str(FSM_EV_PROLOG);
 
 		for (idx = prolog_index; 0 <= idx; --idx) {
 			dest = prolog_state[idx];
 			if (debug) {
 				local_event.code = FSM_EV_GET_STATE_NAME;
-				dest_name = (const char *) dest(ctrl, &local_event);
+				dest_name = dest(ctrl, fsm, &local_event);
 
 				pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, prolog_name,
 					dest_name);
 			}
 			local_event.code = FSM_EV_PROLOG;
-			dest = dest(ctrl, &local_event);
-		}
+			dest(ctrl, fsm, &local_event);
+		}
+	} else if (prolog_state[0]) {
+		/* Transitioned to a state's superstate. */
+		prolog_name = fsm_ev2str(FSM_EV_PROLOG);
+		dest = prolog_state[0];
 	} else {
 		/* Termination transition. */
+		fsm->state = fsm_top_state;
 		if (fsm->destructor) {
 			if (debug) {
 				pri_message(ctrl, "%s: Destroying\n", fsm->name);
 			}
-			fsm->destructor(ctrl, fsm->parms);
+			fsm->destructor(ctrl, fsm, fsm->parms);
 		}
 		return;
 	}
 
-	if (!dest) {
-		/* The original dest state is a leaf state. */
-		fsm->state = prolog_state[0];
-		return;
-	}
-
-	/* Drill into nested states for the final destination state. */
-	do {
-		src = dest;
+	/* We reached the specified destination state. */
+	fsm->state = dest;
+
+	/* Drill down into possible further nested states. */
+	init_name = fsm_ev2str(FSM_EV_INIT);
+	for (;;) {
+		if (debug) {
+			pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, init_name,
+				dest_name);
+		}
+		local_event.code = FSM_EV_INIT;
+		dest = dest(ctrl, fsm, &local_event);
+		if (!dest) {
+			/* We made it to a leaf state. */
+			break;
+		}
+
 		if (debug) {
 			local_event.code = FSM_EV_GET_STATE_NAME;
-			dest_name = (const char *) dest(ctrl, &local_event);
+			dest_name = dest(ctrl, fsm, &local_event);
 
 			pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, prolog_name,
 				dest_name);
 		}
 		local_event.code = FSM_EV_PROLOG;
-		dest = dest(ctrl, &local_event);
-	} while (dest);
-
-	/* We made it to a leaf state. */
-	fsm->state = src;
+		dest(ctrl, fsm, &local_event);
+
+		/* We drilled one level deeper. */
+		fsm->state = dest;
+	}
 }
 
 /*!
@@ -353,43 +390,42 @@
  * \brief Send a real event to the FSM.
  *
  * \param ctrl D channel controller.
- * \param event Event to process.
- *
- * \return Nothing
- */
-static void fsm_event_process(struct pri *ctrl, struct fsm_event *event)
+ * \param ev Event Q entry to process.
+ *
+ * \return Nothing
+ */
+static void fsm_event_q_process(struct pri *ctrl, struct fsm_event_q *ev)
 {
 	int debug;
 	struct fsm_event local_event;
 	const char *name_event;
 	fsm_state state;
 
-	state = event->fsm->state;
-
-	local_event.fsm = event->fsm;
+	state = ev->fsm->state;
+
 	local_event.code = FSM_EV_GET_DEBUG;
-	if (state(ctrl, &local_event)) {
+	if (state(ctrl, ev->fsm, &local_event)) {
 		debug = 1;
 	} else {
 		debug = 0;
 	}
 
 	local_event.code = FSM_EV_GET_EV_NAME;
-	local_event.parms.num = event->code;
-	name_event = (const char *) state(ctrl, &local_event);
+	local_event.parms.num = ev->event.code;
+	name_event = (const char *) state(ctrl, ev->fsm, &local_event);
 
 	do {
 		if (debug) {
 			const char *name_state;
 
 			local_event.code = FSM_EV_GET_STATE_NAME;
-			name_state = (const char *) state(ctrl, &local_event);
-
-			pri_message(ctrl, "%s: Event %s in state %s\n", event->fsm->name, name_event,
+			name_state = (const char *) state(ctrl, ev->fsm, &local_event);
+
+			pri_message(ctrl, "%s: Event %s in state %s\n", ev->fsm->name, name_event,
 				name_state);
 		}
 
-		state = state(ctrl, event);
+		state = state(ctrl, ev->fsm, &ev->event);
 	} while (state);
 }
 
@@ -403,11 +439,11 @@
  */
 void fsm_run(struct pri *ctrl, struct fsm_queue *que)
 {
-	struct fsm_event event;
+	struct fsm_event_q ev;
 
 	while (que->head != que->tail) {
 		/* Pull the next event off the head of the queue. */
-		event = que->events[que->head];
+		ev = que->events[que->head];
 
 		/* Advance the queue head. */
 		++que->head;
@@ -415,7 +451,7 @@
 			que->head = 0;
 		}
 
-		fsm_event_process(ctrl, &event);
+		fsm_event_q_process(ctrl, &ev);
 	}
 }
 
@@ -424,16 +460,17 @@
  *
  * \param ctrl D channel controller.
  * \param fsm Filled in FSM control structure set to the initial FSM state.
- *
- * \return Nothing
- */
-void fsm_init(struct pri *ctrl, struct fsm_ctrl *fsm)
+ * \param init Initial FSM state.
+ *
+ * \return Nothing
+ */
+void fsm_init(struct pri *ctrl, struct fsm_ctrl *fsm, fsm_state init)
 {
 	int debug;
 	struct fsm_event dbg_event;
 
 	dbg_event.code = FSM_EV_GET_DEBUG;
-	if (fsm->state(ctrl, &dbg_event)) {
+	if (init(ctrl, fsm, &dbg_event)) {
 		debug = 1;
 	} else {
 		debug = 0;
@@ -442,7 +479,8 @@
 	if (debug) {
 		pri_message(ctrl, "%s: Initial transition\n", fsm->name);
 	}
-	fsm_transition(ctrl, debug, fsm, fsm->state, NULL);
+	fsm->state = fsm_top_state;
+	fsm_transition(ctrl, debug, fsm, init);
 }
 
 /* ------------------------------------------------------------------- */

Modified: team/rmudgett/q931_fsm/pri_fsm.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/pri_fsm.h?view=diff&rev=2246&r1=2245&r2=2246
==============================================================================
--- team/rmudgett/q931_fsm/pri_fsm.h (original)
+++ team/rmudgett/q931_fsm/pri_fsm.h Wed Mar  9 20:26:39 2011
@@ -46,6 +46,7 @@
 #define FSM_MAX_Q_EVENTS			10		/*!< Max number of events the common Q can contain. */
 #define FSM_MAX_SUPERSTATE_NESTING	10		/*!< Max number of nested superstates. */
 
+struct fsm_ctrl;
 struct fsm_event;
 struct fsm_queue;
 
@@ -71,10 +72,24 @@
 	FSM_EV_GET_EV_NAME,
 	/*!
 	 * \brief Event to get the FSM debug output enable flag.
+	 * 
+	 * \note Return the FSM_IS_DEBUG() value.
 	 *
 	 * \retval NULL if debug output is disabled.
 	 */
 	FSM_EV_GET_DEBUG,
+	/*!
+	 * \brief Event to get the initial sub-state of the FSM state.
+	 *
+	 * \note
+	 * Used to drill down into the FSM to find the initial FSM leaf
+	 * state.
+	 *
+	 * \retval NULL The state is a leaf state.  There is no default substate.
+	 *
+	 * \retval substate The default substate to drill down into the FSM.
+	 */
+	FSM_EV_INIT,
 	/*!
 	 * \brief Event to construct the FSM state.
 	 *
@@ -82,13 +97,7 @@
 	 * Used to construct the FSM state when an event causes a
 	 * transition into the state.
 	 *
-	 * \note
-	 * The return value is used to drill down into the FSM to find
-	 * the initial FSM leaf state.
-	 *
-	 * \retval NULL The state is a leaf state.  There is no default substate.
-	 *
-	 * \retval substate The default substate to drill down into the FSM.
+	 * \return The superstate of the current state.
 	 */
 	FSM_EV_PROLOG,
 	/*!
@@ -119,26 +128,28 @@
  * \brief Pass an event to a state.
  *
  * \param ctrl D channel controller.
+ * \param fsm FSM controller.
  * \param event Event to process.
  *
  * \return The value has various meanings depending upon what
  * event was passed in.
  * \see enum fsm_ev event descriptions for return value.
  */
-typedef void *(*fsm_state)(struct pri *ctrl, struct fsm_event *event);
+typedef void *(*fsm_state)(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
 
 /*!
  * \brief Do what is necessary to clean up after a FSM terminates.
  *
  * \param ctrl D channel controller.
+ * \param fsm FSM controller.
  * \param parms Struct containing the instance of the FSM that terminated.
  *
  * \note
- * The destructor may destroy the parms structure.
+ * The destructor may destroy the fsm and parms structures.
  *
  * \return Nothing
  */
-typedef void (*fsm_destructor)(struct pri *ctrl, void *parms);
+typedef void (*fsm_destructor)(struct pri *ctrl, struct fsm_ctrl *fsm, void *parms);
 
 /*! FSM control block. */
 struct fsm_ctrl {
@@ -161,14 +172,10 @@
 };
 
 struct fsm_event {
-	/* The following elements are common to all events. */
-
 	/*! FSM event code. */
 	int code;
-	/*! Which FSM is to receive the event. */
-	struct fsm_ctrl *fsm;
-
-	/* Any following elements are optional. */
+
+	/* Any following parms elements are optional. */
 
 	/*!
 	 * \brief Event parameters if needed.
@@ -183,6 +190,13 @@
 	} parms;
 };
 
+struct fsm_event_q {
+	/*! Which FSM is to receive the event. */
+	struct fsm_ctrl *fsm;
+	/*! Event sent to FSM. */
+	struct fsm_event event;
+};
+
 /*!
  * Q of FSM events so the main FSM driver can determine when it
  * is done processing events.
@@ -192,18 +206,20 @@
 	unsigned head;
 	/*! Next index to put and event on the Q. */
 	unsigned tail;
-	struct fsm_event events[FSM_MAX_Q_EVENTS];
-};
-
-/* ------------------------------------------------------------------- */
+	struct fsm_event_q events[FSM_MAX_Q_EVENTS];
+};
+
+/* ------------------------------------------------------------------- */
+
+#define FSM_IS_DEBUG(is_debug)	((is_debug) ? (void *) 1 : (void *) 0)
 
 const char *fsm_ev2str(enum fsm_ev event);
-void fsm_event_push(struct pri *ctrl, struct fsm_event *event);
-void fsm_event_post(struct pri *ctrl, struct fsm_event *event);
-void *fsm_top_state(struct pri *ctrl, struct fsm_event *event);
-void fsm_transition(struct pri *ctrl, int debug, struct fsm_ctrl *fsm, fsm_state dest, fsm_state src);
+void fsm_event_push(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+void fsm_event_post(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+void *fsm_top_state(struct pri *ctrl, struct fsm_ctrl *fsm, struct fsm_event *event);
+void fsm_transition(struct pri *ctrl, int debug, struct fsm_ctrl *fsm, fsm_state dest);
 void fsm_run(struct pri *ctrl, struct fsm_queue *que);
-void fsm_init(struct pri *ctrl, struct fsm_ctrl *fsm);
+void fsm_init(struct pri *ctrl, struct fsm_ctrl *fsm, fsm_state init);
 
 /* ------------------------------------------------------------------- */
 




More information about the libpri-commits mailing list