[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