[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