[libpri-commits] rmudgett: branch rmudgett/q931_fsm r2244 - /team/rmudgett/q931_fsm/
SVN commits to the libpri project
libpri-commits at lists.digium.com
Fri Mar 4 22:50:42 CST 2011
Author: rmudgett
Date: Fri Mar 4 22:50:36 2011
New Revision: 2244
URL: http://svnview.digium.com/svn/libpri?view=rev&rev=2244
Log:
Initial FSM driver implementation.
Added:
team/rmudgett/q931_fsm/pri_fsm.c (with props)
team/rmudgett/q931_fsm/pri_fsm.h (with props)
Modified:
team/rmudgett/q931_fsm/Makefile
team/rmudgett/q931_fsm/pri_internal.h
Modified: team/rmudgett/q931_fsm/Makefile
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/Makefile?view=diff&rev=2244&r1=2243&r2=2244
==============================================================================
--- team/rmudgett/q931_fsm/Makefile (original)
+++ team/rmudgett/q931_fsm/Makefile Fri Mar 4 22:50:36 2011
@@ -47,6 +47,7 @@
pri_aoc.o \
pri_cc.o \
pri_facility.o \
+ pri_fsm.o \
asn1_primitive.o \
rose.o \
rose_address.o \
@@ -73,6 +74,7 @@
pri_aoc.lo \
pri_cc.lo \
pri_facility.lo \
+ pri_fsm.lo \
asn1_primitive.lo \
rose.lo \
rose_address.lo \
Added: team/rmudgett/q931_fsm/pri_fsm.c
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/pri_fsm.c?view=auto&rev=2244
==============================================================================
--- team/rmudgett/q931_fsm/pri_fsm.c (added)
+++ team/rmudgett/q931_fsm/pri_fsm.c Fri Mar 4 22:50:36 2011
@@ -1,0 +1,453 @@
+/*
+ * 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 Common FSM driver routines.
+ *
+ * \author Richard Mudgett <rmudgett at digium.com>
+ */
+
+
+#include "compat.h"
+#include "libpri.h"
+#include "pri_internal.h"
+#include "pri_fsm.h" //BUGBUG
+
+
+/* ------------------------------------------------------------------- */
+
+/*!
+ * \brief Convert the given FSM event code to string.
+ *
+ * \param event FSM event code to convert.
+ *
+ * \return String equivalent of event code.
+ */
+const char *fsm_ev2str(enum fsm_ev event)
+{
+ switch (event) {
+ case FSM_EV_GET_SUPERSTATE:
+ return "FSM_EV_GET_SUPERSTATE";
+ case FSM_EV_GET_STATE_NAME:
+ return "FSM_EV_GET_STATE_NAME";
+ case FSM_EV_GET_EV_NAME:
+ 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:
+ return "FSM_EV_EPILOG";
+ case FSM_EV_FIRST_USER_EV:
+ /* Not a common event code. */
+ break;
+ }
+ return "FSM_EV_unknown";
+}
+
+/*!
+ * \brief Push an event on the head of the event queue.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to push.
+ *
+ * \return Nothing
+ */
+void fsm_event_push(struct pri *ctrl, 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;
+ dbg_event.code = FSM_EV_GET_DEBUG;
+ if (state(ctrl, &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;
+
+ /* Determine previous head index. */
+ if (que->head) {
+ next_head = que->head - 1;
+ } else {
+ next_head = ARRAY_LEN(que->events) - 1;
+ }
+ if (next_head == que->tail) {
+ pri_error(ctrl, DBGHEAD "Queue overflow!\n", DBGINFO);
+ return;
+ }
+
+ /* Put event in the queue. */
+ que->head = next_head;
+ que->events[que->head] = *event;
+}
+
+/*!
+ * \brief Post an event on the tail of the event queue.
+ *
+ * \param ctrl D channel controller.
+ * \param event Event to post.
+ *
+ * \return Nothing
+ */
+void fsm_event_post(struct pri *ctrl, 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;
+ dbg_event.code = FSM_EV_GET_DEBUG;
+ if (state(ctrl, &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;
+
+ /* Determine next tail index. */
+ next_tail = que->tail + 1;
+ if (ARRAY_LEN(que->events) <= next_tail) {
+ next_tail = 0;
+ }
+ if (next_tail == que->head) {
+ pri_error(ctrl, DBGHEAD "Queue overflow!\n", DBGINFO);
+ return;
+ }
+
+ /* Put event in the queue. */
+ que->events[que->tail] = *event;
+ que->tail = next_tail;
+}
+
+/*!
+ * \brief Top state of all FSMs.
+ *
+ * \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.
+ *
+ * \retval NULL For normal events: The state handled the event.
+ *
+ * \retval non-NULL For normal events: The superstate to pass
+ * the event to next.
+ */
+void *fsm_top_state(struct pri *ctrl, struct fsm_event *event)
+{
+ switch (event->code) {
+ case FSM_EV_GET_STATE_NAME:
+ return (void *) __PRETTY_FUNCTION__;
+ case FSM_EV_GET_EV_NAME:
+ return (void *) fsm_ev2str(event->parms.num);
+ 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;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/*!
+ * \brief Perform a FSM state transition.
+ *
+ * \param ctrl D channel controller.
+ * \param debug TRUE if FSM debug output enabled.
+ * \param fsm FSM that is transitioning states.
+ * \param from Transitioning from state. (NULL if initial)
+ * \param to Transitioning to state. (NULL if terminal)
+ *
+ * \return Nothing
+ */
+void fsm_transition(struct pri *ctrl, int debug, struct fsm_ctrl *fsm, fsm_state from, fsm_state to)
+{
+ struct fsm_event local_event;
+ fsm_state epilog_state[FSM_MAX_SUPERSTATE_NESTING];
+ fsm_state prolog_state[FSM_MAX_SUPERSTATE_NESTING];
+ const char *epilog_name;
+ const char *prolog_name;
+ const char *init_name;
+ const char *from_name;
+ const char *to_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 (from) {
+ from_name = from(ctrl, &local_event);
+ } else {
+ fsm->state = fsm_top_state;
+ from_name = "*";
+ }
+ if (to) {
+ to_name = to(ctrl, &local_event);
+ } else {
+ to_name = "*";
+ }
+
+ if (debug) {
+ pri_message(ctrl, "%s: Next-state %s\n", fsm->name, to_name);
+ }
+
+ local_event.code = FSM_EV_GET_SUPERSTATE;
+
+ /* Build from superstate nesting stack. */
+ epilog_index = 0;
+ epilog_state[epilog_index++] = from;
+ while (from) {
+ if (FSM_MAX_SUPERSTATE_NESTING <= epilog_index) {
+ pri_error(ctrl, "%s: FSM 'from' state %s nested too deep!\n", fsm->name,
+ from_name);
+ return;
+ }
+ from = from(ctrl, &local_event);
+ epilog_state[epilog_index++] = from;
+ }
+
+ /* Build to superstate nesting stack. */
+ prolog_index = 0;
+ prolog_state[prolog_index++] = to;
+ while (to) {
+ if (FSM_MAX_SUPERSTATE_NESTING <= prolog_index) {
+ pri_error(ctrl, "%s: FSM 'to' state %s nested too deep!\n", fsm->name,
+ to_name);
+ return;
+ }
+ to = to(ctrl, &local_event);
+ prolog_state[prolog_index++] = to;
+ }
+
+ /* Find first non-common superstate level. */
+ for (;;) {
+ --epilog_index;
+ --prolog_index;
+ if (!epilog_index || !prolog_index) {
+ /* No more nested superstates. */
+ break;
+ }
+ if (epilog_state[epilog_index] != prolog_state[prolog_index]) {
+ break;
+ }
+ }
+
+ /* Execute state epilogs */
+ if (epilog_state[epilog_index]) {
+ epilog_name = fsm_ev2str(FSM_EV_EPILOG);
+
+ for (idx = 0; idx <= epilog_index; ++idx) {
+ from = epilog_state[idx];
+ if (debug) {
+ local_event.code = FSM_EV_GET_STATE_NAME;
+ from_name = (const char *) from(ctrl, &local_event);
+
+ pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, epilog_name,
+ from_name);
+ }
+ local_event.code = FSM_EV_EPILOG;
+ from(ctrl, &local_event);
+ }
+ }
+
+ /* Execute known state prologs */
+ if (prolog_state[prolog_index]) {
+ prolog_name = fsm_ev2str(FSM_EV_PROLOG);
+
+ for (idx = prolog_index; 0 <= idx; --idx) {
+ to = prolog_state[idx];
+ if (debug) {
+ local_event.code = FSM_EV_GET_STATE_NAME;
+ to_name = (const char *) to(ctrl, &local_event);
+
+ pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, prolog_name,
+ to_name);
+ }
+ local_event.code = FSM_EV_PROLOG;
+ to(ctrl, &local_event);
+ }
+ } else {
+ /* Termination transition. */
+ if (fsm->destructor) {
+ if (debug) {
+ pri_message(ctrl, "%s: Destroying\n", fsm->name);
+ }
+ fsm->destructor(ctrl, fsm->parms);
+ }
+ return;
+ }
+
+ /* Drill down into possible further nested states. */
+ init_name = fsm_ev2str(FSM_EV_INIT);
+ to = prolog_state[0];
+ for (;;) {
+ if (debug) {
+ local_event.code = FSM_EV_GET_STATE_NAME;
+ to_name = (const char *) to(ctrl, &local_event);
+
+ pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, init_name,
+ to_name);
+ }
+ local_event.code = FSM_EV_INIT;
+ to = to(ctrl, &local_event);
+ if (!to) {
+ /* We made it to a leaf state. */
+ fsm->state = prolog_state[0];
+ break;
+ }
+ prolog_state[0] = to;
+ if (debug) {
+ local_event.code = FSM_EV_GET_STATE_NAME;
+ to_name = (const char *) to(ctrl, &local_event);
+
+ pri_message(ctrl, "%s: Event %s in state %s\n", fsm->name, prolog_name,
+ to_name);
+ }
+ local_event.code = FSM_EV_PROLOG;
+ to(ctrl, &local_event);
+ }
+}
+
+/*!
+ * \internal
+ * \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)
+{
+ int debug;
+ struct fsm_event local_event;
+ const char *name_event;
+ fsm_state state;
+
+ state = event->fsm->state;
+
+ //local_event.fsm = event->fsm;
+ local_event.code = FSM_EV_GET_DEBUG;
+ if (state(ctrl, &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);
+
+ 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);
+ }
+
+ state = state(ctrl, event);
+ } while (state);
+}
+
+/*!
+ * \brief Process all events in the event Q before returning.
+ *
+ * \param ctrl D channel controller.
+ * \param que Event Q to process.
+ *
+ * \return Nothing
+ */
+void fsm_run(struct pri *ctrl, struct fsm_queue *que)
+{
+ struct fsm_event event;
+
+ while (que->head != que->tail) {
+ /* Pull the next event off the head of the queue. */
+ event = que->events[que->head];
+
+ /* Advance the queue head. */
+ ++que->head;
+ if (ARRAY_LEN(que->events) <= que->head) {
+ que->head = 0;
+ }
+
+ fsm_event_process(ctrl, &event);
+ }
+}
+
+/*!
+ * \brief Do the initial transition to start the FSM.
+ *
+ * \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)
+{
+ int debug;
+ struct fsm_event dbg_event;
+
+ dbg_event.code = FSM_EV_GET_DEBUG;
+ if (fsm->state(ctrl, &dbg_event)) {
+ debug = 1;
+ } else {
+ debug = 0;
+ }
+
+ if (debug) {
+ pri_message(ctrl, "%s: Initial transition\n", fsm->name);
+ }
+ fsm_transition(ctrl, debug, fsm, NULL, fsm->state);
+}
+
+/* ------------------------------------------------------------------- */
+/* end pri_fsm.c */
Propchange: team/rmudgett/q931_fsm/pri_fsm.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/rmudgett/q931_fsm/pri_fsm.c
------------------------------------------------------------------------------
svn:keywords = 'Author Date Id Revision'
Propchange: team/rmudgett/q931_fsm/pri_fsm.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: team/rmudgett/q931_fsm/pri_fsm.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/pri_fsm.h?view=auto&rev=2244
==============================================================================
--- team/rmudgett/q931_fsm/pri_fsm.h (added)
+++ team/rmudgett/q931_fsm/pri_fsm.h Fri Mar 4 22:50:36 2011
@@ -1,0 +1,204 @@
+/*
+ * 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 Common declarations for FSM driver.
+ *
+ * \author Richard Mudgett <rmudgett at digium.com>
+ */
+
+#ifndef _LIBPRI_PRI_FSM_H
+#define _LIBPRI_PRI_FSM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pri;
+
+/* ------------------------------------------------------------------- */
+
+#define FSM_MAX_EV_PARAM_BYTES 100 /*!< Max space available for any event parameters. */
+#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_event;
+struct fsm_queue;
+
+/*! Commont FSM event codes. */
+enum fsm_ev {
+ /*!
+ * \brief Event to get the superstate of the FSM state.
+ *
+ * \note
+ * Used to determine which PROLOG/EPILOG events need to run in a
+ * state transition.
+ */
+ FSM_EV_GET_SUPERSTATE,
+ /*! \brief Event to get the __PRETTY_FUNCTION__ string of the FSM state. */
+ FSM_EV_GET_STATE_NAME,
+ /*! \brief Event to get the event name string. */
+ FSM_EV_GET_EV_NAME,
+ /*! \brief Event to get the FSM debug output enable flag. */
+ 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.
+ */
+ FSM_EV_INIT,
+ /*!
+ * \brief Event to construct the FSM state.
+ *
+ * \note
+ * Used to construct the FSM state when an event causes a
+ * transition into the state.
+ */
+ FSM_EV_PROLOG,
+ /*!
+ * \brief Event to destroy the FSM state.
+ *
+ * \note
+ * Used to destroy the FSM state when an event causes a
+ * transition from the state.
+ */
+ FSM_EV_EPILOG,
+
+ /*!
+ * \brief First event code value available for a user defined FSM.
+ *
+ * \note MUST be last in the enum.
+ */
+ FSM_EV_FIRST_USER_EV
+};
+
+/*!
+ * \brief Pass an event to a state.
+ *
+ * \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.
+ *
+ * \retval NULL For normal events: The state handled the event.
+ *
+ * \retval non-NULL For normal events: The superstate to pass
+ * the event to next.
+ */
+typedef void *(*fsm_state)(struct pri *ctrl, struct fsm_event *event);
+
+/*!
+ * \brief Do what is necessary to clean up after a FSM terminates.
+ *
+ * \param ctrl D channel controller.
+ * \param parms Struct containing the instance of the FSM that terminated.
+ *
+ * \note
+ * The destructor may destroy the parms structure.
+ *
+ * \return Nothing
+ */
+typedef void (*fsm_destructor)(struct pri *ctrl, void *parms);
+
+/*! FSM control block. */
+struct fsm_ctrl {
+ /*! Current state of the FSM. */
+ fsm_state state;
+ /*!
+ * \brief Function called when the FSM terminates.
+ * \note NULL if not used.
+ */
+ fsm_destructor destructor;
+ /*!
+ * \brief Name of the FSM for debug output
+ * \note Use to make unique names such as call-reference id, cc_record id...
+ */
+ const char *name;
+ /*! Where is the event Q. */
+ struct fsm_queue *que;
+ /*! Struct containing this instance of the FSM. */
+ void *parms;
+};
+
+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. */
+
+ /*!
+ * \brief Event parameters if needed.
+ * \note Union done for alignment purposes.
+ */
+ union {
+ /*! Generic pointer to guarantee pointer alignment. */
+ void *ptr;
+ int num;
+ /*! Reserve space for custom event parameters. */
+ char filler[FSM_MAX_EV_PARAM_BYTES];
+ } parms;
+};
+
+/*!
+ * Q of FSM events so the main FSM driver can determine when it
+ * is done processing events.
+ */
+struct fsm_queue {
+ /*! Next index to get an event off the Q. */
+ unsigned head;
+ /*! Next index to put and event on the Q. */
+ unsigned tail;
+ struct fsm_event events[FSM_MAX_Q_EVENTS];
+};
+
+/* ------------------------------------------------------------------- */
+
+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 from, fsm_state to);
+void fsm_run(struct pri *ctrl, struct fsm_queue *que);
+void fsm_init(struct pri *ctrl, struct fsm_ctrl *fsm);
+
+/* ------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBPRI_PRI_FSM_H */
+/* ------------------------------------------------------------------- */
+/* end pri_fsm.h */
Propchange: team/rmudgett/q931_fsm/pri_fsm.h
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/rmudgett/q931_fsm/pri_fsm.h
------------------------------------------------------------------------------
svn:keywords = 'Author Date Id Revision'
Propchange: team/rmudgett/q931_fsm/pri_fsm.h
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/rmudgett/q931_fsm/pri_internal.h
URL: http://svnview.digium.com/svn/libpri/team/rmudgett/q931_fsm/pri_internal.h?view=diff&rev=2244&r1=2243&r2=2244
==============================================================================
--- team/rmudgett/q931_fsm/pri_internal.h (original)
+++ team/rmudgett/q931_fsm/pri_internal.h Fri Mar 4 22:50:36 2011
@@ -34,6 +34,7 @@
#include <sys/time.h>
#include "pri_q921.h"
#include "pri_q931.h"
+//#include "pri_fsm.h" //BUGBUG
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
More information about the libpri-commits
mailing list