/*!
  @file
  stm2.h

  @brief
  This is the public API for the STM state machine framework.

  @detail
  The STM service provides a centralized framework in which to implement and
  manage an arbitrary number of state machines.  These state machines may be
  single or multiple-instance, and flat or hierarchical.

  The STM service operates on data structures generated by a compiled
  representation of the state machine.  The state machine compiler takes a
  BNF grammar as input to generate the necessary file containing the data
  structures.  The generated file is included by the state machine's C
  implementation code prior to compilation.

  At the most basic, a state machine is composed of an arbitrary number of
  states, one of which is designated as the initial state.  A state machine is
  initially inactive, and occupies the internal state STM_DEACTIVATED_STATE.
  Upon activation, a state machine's optional entry function is called.  After
  the state machine's entry function returns, the state machine is set to its
  initial state, at which time that state's optional entry function is called.
  After the state's entry function returns, a check is made to see if there is
  a child state machine in the initial state - if there is, it is activated and
  the process repeats.

  When a state machine is active, the only way for it to change states is by
  receiving an external input.  Each state in a state machine has a mapping of
  input to transition function.  When an input is processed by a state machine,
  an attempt to locate the input in the state machine's current state to
  transition function table is made.  Upon finding a transition function (all of
  which is specified in the STM grammar) that maps to the given input in the
  current state, the transaction function is called.  The return value of the
  transition function is of the type stm_state_t and indicates the next state
  that the state machine should transition to.  If the return value is the same
  state, nothing further happens aside from the transition function being
  called.  If the return value indicates a new state, the following sequence of
  events occurs.  The state machine's current state's child state machine is
  deactivated, if there is one.  The current state's optional exit function is
  then called.  The state machine is then set to the new state, whose optional
  entry function is called.  Lastly, if the new state has a child state machine,
  it is activated.

  When dealing with hierarchical state machines, the input is processed from the
  bottom-up.  Once the input has been processed at some point on the way up, it
  is considered consumed, and control returns to the caller.  A transition
  function can choose to circumvent this, however, and pass the input up to its
  parent for further processing.

  A state machine can be deactivated by the user, or by simply being a child of
  a state that is exited.  Deactivation occurs in the exact opposite order of
  activation, from the bottom-up.  The lowest most state's optional exit
  function is called, and the state machine containing it is set to the
  STM_DEACTIVATED_STATE.  Following this, the optional exit function for the
  state machine is called.  The process then repeats on the parent of this
  state machine until it reaches the original user-specified state machine.

  Multiple instances are simply parallel implementations of identical state
  machines.  Instead of just dealing with a single state machine and its
  children, one may choose to have n individually addressable state machine
  instances.  The parent-child relationships are exactly the same in each
  state machine instance, and are completely isolated from each other.  That is,
  different instances of the same state machine are, by design, not intended
  to share data or cross-communicate with each other.


  <!-- Doxygen chokes on the @mainpage vs. @page inside a conditional, but the
       following seems to work.  Basically, make this the mainpage if it
       is the only component being built, otherwise, just make it a page and
       let the upper level component/s @ref it. -->
  @if stm
  @mainpage STM State Machine Framework
  @endif
  @ifnot stm
  @page STM State Machine Framework
  @endif

  @anchor stm_introduction

  <b>The STM framework provides a centralized way in which to implement and
  manage an arbitrary number of state machines.  These state machines may be
  single or multiple-instance, and flat or hierarchical.</b>


  <br><h1>Public API Requirements</h1>

  The public API for the STM framework requires the client to include
  the file stm2.h


  <br><h1>Starting and Stopping State Machines</h1>

  A state machine is initially created in the deactivated state, and must
  be activated prior to sending it inputs.  Once activated, a state machine
  will be eligible to process inputs until it is deactivated (either externally,
  or by itself.)

  <b><i>Single-Instance</i></b>
  - stm_activate()
  - stm_deactivate()

  <b><i>Multiple-Instance</i></b>
  - stm_instance_activate()
  - stm_instance_deactivate()


  <br><h1>State Machine Input Handling</h1>

  External events sent to a state machine are called 'inputs' in the STM
  framework.  Inputs are 'handled' by a corresponding transition function in the
  state machine.  In a hierarchical state machine, the lowest-level child
  state machine's state is given the first chance to handle an input, followed
  by its parent, and so forth.  Upon being handled, the input is no longer
  propagated upwards through the ancestry unless each child's transition
  function explicitly requests such.

  <b><i>Single-Instance</i></b>
  - stm_process_input()

  <b><i>Multiple-Instance</i></b>
  - stm_instance_process_input()


  <br><h1>Debug and Error Hook Functions</h1>

  It is <b>strongly</b> recommended that anyone using the STM framework
  implement both error and debug hook functions (specified in the STM grammar.)
  The error hook function allows a subsystem to 'catch' all but the most fatal
  STM errors and log them appropriately.  The debug hook function allows
  a subsystem to instrument all the inner workings of its state machines and
  log it for debugging purposes.

  <b><i>Function pointer typedefs for error and debug hook functions</i></b>
  - @ref stm_error_fn_t
  - @ref stm_debug_hook_fn_t

  There is a 32-bit unsigned value dedicated to every state machine instance
  that can be used from within the debug hook function to enable and/or disable
  certain types of logging/profiling. The accessor functions to set and get
  the debug mask are the following:
  - stm_set_debug_mask()
  - stm_get_debug_mask()

  Each state machine has a unique ID associated with it, based upon the 32
  least significant bits of the MD5 message digest of the state machine instance
  name.  This can be useful as a unique identifier suitable for interfacing
  profiling code to a logic analyzer.
  - stm_get_uid()

  Upon catastrophic error, such as a rogue memory scribbler rendering the
  state machine data structures invalid, every state machine exports a
  'reset function' that peforms a reinitialization of all static parameters.
  This should never need to be used in normal situations, and one could argue
  that you have bigger problems if you should need to push this big red button.
  The reset function takes the same name as the state machine, suffixed by
  '_reset'.  So, for example, the reset function for FOO_SM is FOO_SM_reset().


  <br><h1>Helper Functions</h1>

  Helper functions exist to make it easier to manipulate and query the STM state
  machine internal structures.  These are guaranteed to work, as opposed to
  randomly frobbing the internal structure manually.

  <b><i>Per-instance user data manipulation</i></b>
  - stm_set_user_data()
  - stm_get_user_data()

  <b><i>Propagating inputs from child to parent</i></b>
  - stm_propagate_input()

  <b><i>Parent to child interaction</i></b>
  - stm_get_child()

  <i>The following helper functions are generally used in the debug and/or
  error handling hooks of a subsystem's state machines.  Using them for
  normal (input processing) is not recommended, as there are more direct
  functions dedicated to this.</i>

  <b><i>Instance information functions</i></b>
  - stm_get_num_instances()
  - stm_get_instance_num()
  - stm_get_instance()

  <b><i>General informational functions</i></b>
  - stm_get_name()
  - stm_get_error_string()
  - stm_get_last_input()
  - stm_get_last_input_name()
  - stm_get_last_calling_sm()
  - stm_get_num_states()
  - stm_get_state()
  - stm_get_state_name()


  <br><h1>Useful Quick-links</h1>

  <b><i>STM status return codes</i></b>
  - @ref stm_status_t

  <b><i>STM 'special' transition function return values</i></b>
  - @ref STM_SAME_STATE
  - @ref STM_DEACTIVATED_STATE

  <b><i>STM 'special' previous and next state values for state entry and
  exit functions</i></b>
  - @ref STM_DEACTIVATED_STATE

  <b><i>STM debug event codes</i></b>
  - @ref stm_debug_event_t

  <br><h1>Miscellaneous</h1>

  @ref stm_grammar

  @ref todo "Errata/To-Do List"

  <br><br><i>Questions? tcurtiss@qualcomm.com</i>

  @page stm_grammar STM BNF Grammar
  @code
  STM BNF Grammar

  <{state-machine}> ::= STATE_MACHINE <sm name> <{sm-block}> ...

  <{sm-block}> ::=      ENTRY          <function name>;                |
                        EXIT           <function name>;                |
                        INITIAL_STATE  <state name>;                   |
                        ERROR_HOOK     <function name>;                |
                        DEBUG_HOOK     <function name>;                |
                        INPUT_DEF_FILE <filename>; ...                 |
                       [INSTANCES      <# of instances>;]              |
                        STATE          <state name> <{state-block}> ...

  <{state-block}> ::=   ENTRY          <function name>;                |
                        EXIT           <function name>;                |
                        INPUTS         <{inputs-block}>                |
                       [STATE_MACHINE  <{sub-sm-block}>]

  <{inputs-block}> ::= <input name> [<function name>]; ...

  <{sub-sm-block}> ::=  ENTRY          <function name>;                |
                        EXIT           <function name>;                |
                        INITIAL_STATE  <state name>;                   |
                       [ERROR_HOOK     <function name>;]               |
                       [DEBUG_HOOK     <function name>;]               |
                        STATE          <state name> <{state-block}> ...
  @endcode

*/

/*===========================================================================

  Copyright (c) 2007 - 2008 Qualcomm Technologies, Inc. All Rights Reserved

  Qualcomm Technologies Proprietary

  Export of this technology or software is regulated by the U.S. Government.
  Diversion contrary to U.S. law prohibited.

  All ideas, data and information contained in or disclosed by
  this document are confidential and proprietary information of
  Qualcomm Technologies, Inc. and all rights therein are expressly reserved.
  By accepting this material the recipient agrees that this material
  and the information contained therein are held in confidence and in
  trust and will not be used, copied, reproduced in whole or in part,
  nor its contents revealed in any manner to others without the express
  written permission of Qualcomm Technologies, Inc.

===========================================================================*/

/*===========================================================================

                        EDIT HISTORY FOR MODULE

This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.

$Header: /local/mnt/workspace/randrew/7x30_1/vendor/qcom-proprietary/data/common/inc/RCS/stm2.h,v 1.1 2010/02/17 23:56:22 randrew Exp $

when       who     what, where, why
--------   ---     ----------------------------------------------------------
06/12/08   trc     Rename to stm2.h to allow legacy coexistence w/ old stm.h
08/28/07   trc     Add pre-transition fn debugging hook
08/07/07   trc     Changes for new calling SM param in stm_process_input*()
04/18/07   trc     Add state parameter to stm_get_child(), rename *_input() API
04/13/07   trc     Add extern "C" {} guards around the file for c++ users
04/03/07   trc     Add stm_get_uid() for unique SM instance ID retrieval
04/02/07   trc     Refactor state machine structure into non-const, all-instance
                   const, and per-instance const structures for better static
                   initialization and 'reset button' properties
02/28/07   trc     Add STM_UNUSED() macro for suppressing compiler/Lint warnings
02/28/07   trc     Remove STM_EBADPARM since it's unused due to STM_NULL_CHECK()
02/26/07   trc     Code review changes.  Move variant code to stm_os.h
02/06/07   trc     Initial revision
===========================================================================*/

#ifndef STM2_H
#define STM2_H

#ifdef __cplusplus
/* extern "C" { ... } scoping for C++ users */
extern "C" {
#endif

/*===========================================================================

                           INCLUDE FILES

===========================================================================*/

/* Variant target/operating-system specific include file */
#include "stm2_os.h"


/*===========================================================================

                   EXTERNAL DEFINITIONS AND TYPES

===========================================================================*/

/*! @brief STM return codes used to communicate success or failure to the
           caller.
*/
typedef enum
{
  STM_SUCCESS =        0,           /*!< Operation was successful */
  STM_EBUSY =         -1,           /*!< Operation was attempted on a state
                                         machine that is already being
                                         operated upon */
  STM_EBADINSTANCE =  -2,           /*!< Operation attempted on nonexistent
                                         instance */
  STM_ENOTPROCESSED = -3,           /*!< Input was not processed by state
                                         machine */
  STM_EINACTIVE =     -4,           /*!< Input was sent to an inactive state
                                         machine */
  STM_EBADSTATE =     -5            /*!< Invalid state returned from transition
                                         function */
} stm_status_t;


/*! @brief Type definition representing an 'input' to a state machine.
           Based upon the current state of the state machine, the input to it
           will map to a transition function that will be called to determine
           the subsequent state of the state machine.
*/
typedef int32 stm_input_t;


/*! @brief Structure that maps state machine inputs to their names.  An
           array of these structures is used to map all possible inputs for
           a state machine into an index for subsequent use in looking up the
           corresponding transition function in a non-sparse table.
*/
typedef struct
{
  const char          *name; /*!< A string representing the name of the input */
  const stm_input_t   input; /*!< A valid STM input */
} stm_input_map_t;


/*! @brief Type definition representing a unique 'state' in a state machine.
*/
typedef int32 stm_state_t;


/* Forward declaration of the state machine structure so it can be referenced
   prior to its actual definition
*/
struct stm_state_machine_s;


/*! @brief State entry function type.  This function will be called upon entry
           into a given state.
*/
typedef void (*stm_state_entry_fn_t)
(
  struct stm_state_machine_s        *sm,
  stm_state_t                       previous_state,
  void                              *payload
);


/*! @brief State exit function type.  This function will be called upon exit
           from a given state.
*/
typedef void (*stm_state_exit_fn_t)
(
  struct stm_state_machine_s        *sm,
  stm_state_t                       next_state,
  void                              *payload
);


/*! @brief Structure that maps a state to its entry and exit functions, along
           with its string name.  An array of these structures is used to form
           a sequential list of all possible states for a state machine.  The
           state's value itself is represented by its index into this array.
*/
typedef struct
{
  const char            *name;      /*!< String representing the state name */
  const stm_state_entry_fn_t
                        entry_fn;   /*!< Entry function for this state */
  const stm_state_exit_fn_t
                        exit_fn;    /*!< Exit function for this state */
  struct stm_state_machine_s
                        *child_sm; /*!< Child state machine instance array */
} stm_state_map_t;


/*! @brief Transition function type.  Transition functions are called in
           accordance with the current state the recipient state machine.
*/
typedef stm_state_t (*stm_transition_fn_t)
(
 struct stm_state_machine_s         *sm,
 void                               *payload
);


/*! @brief State value, that when returned from a transition function, causes
           the state machine to remain in the current state (i.e. No state
           change occurs as a result of calling the transition function.)
*/
#define STM_SAME_STATE              ((stm_state_t)-1)


/*! @brief State value indicating that the current state machine is inactive.
           When returned from a transition function, this causes the current
           state machine and all of its children to be deactivated.
*/
#define STM_DEACTIVATED_STATE       ((stm_state_t)-2)


/*! @brief Invalid state, used for error reporting and range checking.
*/
#define STM_INVALID_STATE           ((stm_state_t)-3)


/*! @brief Invalid index returned when an index lookup fails.
*/
#define STM_INVALID_INDEX           ((int32)-1)


/*! @brief Invalid input returned when the actual input cannot be determined
           or bad parameters were passed to the lookup function.
*/
#define STM_INVALID_INPUT           ((stm_input_t)-1)


/*! @brief STM helper macro to suppress compiler and/or Lint warnings for
           unused variables.
*/
#define STM_UNUSED(var)             ( var = var )


/*! @brief State machine entry function type.  This function will be called
           upon activation of a state machine instance.
*/
typedef void (*stm_sm_entry_fn_t)
(
  struct stm_state_machine_s        *sm,
  void                              *payload
);


/*! @brief State machine exit function type.  This function will be called
           upon deactivation of a state machine instance.
*/
typedef void (*stm_sm_exit_fn_t)
(
  struct stm_state_machine_s        *sm,
  void                              *payload
);


/*! @brief Value used as first parameter to the debug hook function to indicate
           for what reason it is being called.
*/
typedef enum
{
  STM_ENTRY_FN = 0,   /*!< The entry function for the passed state machine
                           was called */
  STM_STATE_ENTRY_FN, /*!< The entry function for the passed state machine and
                           state was called */
  STM_EXIT_FN,        /*!< The exit function for the passed state machine was
                           called */
  STM_STATE_EXIT_FN,  /*!< The exit function for the passed state machine and
                           state was called */
  STM_PRE_TRANS_FN,   /*!< The transition function for the passed state machine
                           and state is about to be called */
  STM_POST_TRANS_FN,  /*!< The transition function for the passed state machine
                           and state has been called */
} stm_debug_event_t;


/*! @brief State machine debug hook function type.  This function will be
           called upon every input processed by a state machine instance.
*/
typedef void (*stm_debug_hook_fn_t)
(
  stm_debug_event_t           debug_event,
  struct stm_state_machine_s  *sm,
  stm_state_t                 state_info,
  void                        *payload
);


/*! @brief State machine error function type.  This function will be called
           upon error conditions that occur in the STM framework.
*/
typedef void (*stm_error_fn_t)
(
  stm_status_t                error,
  const char                  *filename,
  uint32                      line,
  struct stm_state_machine_s  *sm
);


/*! @brief Structure representing internal constant data of a state machine
           that is applicable to all instances.
*/
typedef struct
{
  const uint32
              num_instances;    /*!< Total instances of this state machine */

  const uint32
              num_states;       /*!< Number of states in this state machine */

  const stm_state_map_t
              *state_map_array; /*!< Array of all state mappings */

  const uint32
              num_inputs;       /*!< Number of possible inputs in this state
                                     machine */
  const stm_input_map_t
              *input_map_array; /*!< Array of all input mappings */

  const stm_transition_fn_t
              *trans_map_array; /*!< Array of all transition functions for
                                     all inputs in all states */
  const stm_sm_entry_fn_t
              entry_fn;         /*!< State machine entry function to be called
                                     upon activation */
  const stm_sm_exit_fn_t
              exit_fn;          /*!< State machine exit function to be called
                                     upon deactivation */
  const stm_error_fn_t
              error_fn;         /*!< State machine error function to be called
                                     upon any internal STM error */
  const stm_debug_hook_fn_t
              debug_hook_fn;    /*!< State machine debug hook function to be
                                     called upon any */
  const stm_state_t
              initial_state;    /*!< Initial state (upon activation) of this
                                     state machine */
} stm_state_machine_constdata_t;


/*! @brief Structure representing internal constant data of a state machine
           that is unique to each instance.
*/
typedef struct
{
  const stm_state_machine_constdata_t *
                const_data;       /*!< All-instance state machine const data */

  const char    *name;            /*!< String representing state machine name */

  const uint32  uid;              /*!< Unique ID for this state machine
                                       instance.  Lower 32-bits of standard
                                       MD5 message digest of name string. */

  const uint32  this_instance;    /*!< This particular state machine instance */

} stm_state_machine_perinst_constdata_t;


/*! @brief Structure representing one instance of a state machine.
*/
typedef struct stm_state_machine_s
{
  const stm_state_machine_perinst_constdata_t * const
              pi_const_data;    /*!< State machine per-instance constant data */

  stm_state_t current_state;    /*!< Current state of this state machine
                                     instance */

  int32       curr_input_index; /*!< Index of the current or most-recent
                                     input processed by this state machine */

  const struct stm_state_machine_s
              *curr_calling_sm; /*!< Index of current or most-recent state
                                     machine instance that called this
                                     state machine instance */

  boolean     propagate_input;  /*!< Flag to indicate whether or not to allow
                                     a parent state machine instance to process
                                     an input handled by this instance */

  boolean     is_locked;        /*!< Flag to indicate that this state machine
                                     instance is in a transition function,
                                     in the process of being activated, or
                                     in the process of being deactivated */

  void        *user_data;       /*!< User defined per-instance data */

  uint32      debug_mask;       /*!< User defined debug mask */

} stm_state_machine_t;


/*===========================================================================

                    EXTERNAL FUNCTION PROTOTYPES

===========================================================================*/

/* Note that for every API function, there is a 'rename' that converts it
   from an stm_* to an stm2_* .  This is a low-overhead way of renaming the
   entire API to avoid conflicts w/ the legacy STM api symbols.  Most users
   should never know this happens, unless looking for stm symbols in JTAG or
   such. */

#define stm_instance_activate stm2_instance_activate
extern stm_status_t stm_instance_activate
(
  stm_state_machine_t *sm,
  uint32              sm_instance,
  void                *payload
);


#define stm_instance_deactivate stm2_instance_deactivate
extern stm_status_t stm_instance_deactivate
(
  stm_state_machine_t *sm,
  uint32              sm_instance,
  void                *payload
);


#define stm_instance_process_input stm2_instance_process_input
extern stm_status_t stm_instance_process_input
(
  const stm_state_machine_t
                      *calling_sm,
  stm_state_machine_t *sm,
  uint32              sm_instance,
  stm_input_t         input,
  void                *payload
);


#define stm_activate stm2_activate
extern stm_status_t stm_activate
(
  stm_state_machine_t *sm,
  void                *payload
);


#define stm_deactivate stm2_deactivate
extern stm_status_t stm_deactivate
(
  stm_state_machine_t *sm,
  void                *payload
);


#define stm_process_input stm2_process_input
extern stm_status_t stm_process_input
(
  const stm_state_machine_t
                      *calling_sm,
  stm_state_machine_t *sm,
  stm_input_t         input,
  void                *payload
);


#define stm_propagate_input stm2_propagate_input
extern void stm_propagate_input
(
  stm_state_machine_t *sm,
  boolean             prop_flag
);


#define stm_get_error_string stm2_get_error_string
extern const char *stm_get_error_string
(
  stm_status_t        error_code
);


#define stm_get_name stm2_get_name
extern const char *stm_get_name
(
  const stm_state_machine_t *sm
);


#define stm_get_uid stm2_get_uid
extern uint32 stm_get_uid
(
  const stm_state_machine_t *sm
);


#define stm_get_num_instances stm2_get_num_instances
extern uint32 stm_get_num_instances
(
  const stm_state_machine_t *sm
);


#define stm_get_instance_num stm2_get_instance_num
extern uint32 stm_get_instance_num
(
  const stm_state_machine_t *sm
);


#define stm_get_instance stm2_get_instance
extern stm_state_machine_t *stm_get_instance
(
  stm_state_machine_t *sm,
  uint32              instance
);


#define stm_get_state stm2_get_state
extern stm_state_t stm_get_state
(
  const stm_state_machine_t *sm
);


#define stm_get_num_states stm2_get_num_states
extern uint32 stm_get_num_states
(
  const stm_state_machine_t *sm
);


#define stm_get_state_name stm2_get_state_name
extern const char *stm_get_state_name
(
  const stm_state_machine_t *sm,
  stm_state_t               state
);


#define stm_get_child stm2_get_child
extern stm_state_machine_t *stm_get_child
(
  const stm_state_machine_t *sm,
  const stm_state_t          state
);


#define stm_get_last_input stm2_get_last_input
extern stm_input_t stm_get_last_input
(
  const stm_state_machine_t *sm
);


#define stm_get_last_input_name stm2_get_last_input_name
extern const char *stm_get_last_input_name
(
  const stm_state_machine_t *sm
);


#define stm_get_last_calling_sm stm2_get_last_calling_sm
extern const stm_state_machine_t *stm_get_last_calling_sm
(
  const stm_state_machine_t *sm
);


#define stm_get_debug_mask stm2_get_debug_mask
extern uint32 stm_get_debug_mask
(
  const stm_state_machine_t *sm
);


#define stm_set_debug_mask stm2_set_debug_mask
extern void stm_set_debug_mask
(
  stm_state_machine_t *sm,
  uint32               debug_mask
);


#define stm_get_user_data stm2_get_user_data
extern void *stm_get_user_data
(
  const stm_state_machine_t *sm
);


#define stm_set_user_data stm2_set_user_data
extern void stm_set_user_data
(
  stm_state_machine_t *sm,
  void                *user_data
);


#ifdef __cplusplus
} /* extern "C" { ... } scoping */
#endif

#endif /* STM2_H */
