18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _FSM_H_
38c2ecf20Sopenharmony_ci#define _FSM_H_
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/kernel.h>
68c2ecf20Sopenharmony_ci#include <linux/types.h>
78c2ecf20Sopenharmony_ci#include <linux/timer.h>
88c2ecf20Sopenharmony_ci#include <linux/time.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/sched.h>
118c2ecf20Sopenharmony_ci#include <linux/string.h>
128c2ecf20Sopenharmony_ci#include <linux/atomic.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/**
158c2ecf20Sopenharmony_ci * Define this to get debugging messages.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci#define FSM_DEBUG         0
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/**
208c2ecf20Sopenharmony_ci * Define this to get debugging massages for
218c2ecf20Sopenharmony_ci * timer handling.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci#define FSM_TIMER_DEBUG   0
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/**
268c2ecf20Sopenharmony_ci * Define these to record a history of
278c2ecf20Sopenharmony_ci * Events/Statechanges and print it if a
288c2ecf20Sopenharmony_ci * action_function is not found.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci#define FSM_DEBUG_HISTORY 0
318c2ecf20Sopenharmony_ci#define FSM_HISTORY_SIZE  40
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct fsm_instance_t;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/**
368c2ecf20Sopenharmony_ci * Definition of an action function, called by a FSM
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_citypedef void (*fsm_function_t)(struct fsm_instance_t *, int, void *);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/**
418c2ecf20Sopenharmony_ci * Internal jump table for a FSM
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_citypedef struct {
448c2ecf20Sopenharmony_ci	fsm_function_t *jumpmatrix;
458c2ecf20Sopenharmony_ci	int nr_events;
468c2ecf20Sopenharmony_ci	int nr_states;
478c2ecf20Sopenharmony_ci	const char **event_names;
488c2ecf20Sopenharmony_ci	const char **state_names;
498c2ecf20Sopenharmony_ci} fsm;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#if FSM_DEBUG_HISTORY
528c2ecf20Sopenharmony_ci/**
538c2ecf20Sopenharmony_ci * Element of State/Event history used for debugging.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_citypedef struct {
568c2ecf20Sopenharmony_ci	int state;
578c2ecf20Sopenharmony_ci	int event;
588c2ecf20Sopenharmony_ci} fsm_history;
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/**
628c2ecf20Sopenharmony_ci * Representation of a FSM
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_citypedef struct fsm_instance_t {
658c2ecf20Sopenharmony_ci	fsm *f;
668c2ecf20Sopenharmony_ci	atomic_t state;
678c2ecf20Sopenharmony_ci	char name[16];
688c2ecf20Sopenharmony_ci	void *userdata;
698c2ecf20Sopenharmony_ci	int userint;
708c2ecf20Sopenharmony_ci	wait_queue_head_t wait_q;
718c2ecf20Sopenharmony_ci#if FSM_DEBUG_HISTORY
728c2ecf20Sopenharmony_ci	int         history_index;
738c2ecf20Sopenharmony_ci	int         history_size;
748c2ecf20Sopenharmony_ci	fsm_history history[FSM_HISTORY_SIZE];
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci} fsm_instance;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/**
798c2ecf20Sopenharmony_ci * Description of a state-event combination
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_citypedef struct {
828c2ecf20Sopenharmony_ci	int cond_state;
838c2ecf20Sopenharmony_ci	int cond_event;
848c2ecf20Sopenharmony_ci	fsm_function_t function;
858c2ecf20Sopenharmony_ci} fsm_node;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/**
888c2ecf20Sopenharmony_ci * Description of a FSM Timer.
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_citypedef struct {
918c2ecf20Sopenharmony_ci	fsm_instance *fi;
928c2ecf20Sopenharmony_ci	struct timer_list tl;
938c2ecf20Sopenharmony_ci	int expire_event;
948c2ecf20Sopenharmony_ci	void *event_arg;
958c2ecf20Sopenharmony_ci} fsm_timer;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/**
988c2ecf20Sopenharmony_ci * Creates an FSM
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci * @param name        Name of this instance for logging purposes.
1018c2ecf20Sopenharmony_ci * @param state_names An array of names for all states for logging purposes.
1028c2ecf20Sopenharmony_ci * @param event_names An array of names for all events for logging purposes.
1038c2ecf20Sopenharmony_ci * @param nr_states   Number of states for this instance.
1048c2ecf20Sopenharmony_ci * @param nr_events   Number of events for this instance.
1058c2ecf20Sopenharmony_ci * @param tmpl        An array of fsm_nodes, describing this FSM.
1068c2ecf20Sopenharmony_ci * @param tmpl_len    Length of the describing array.
1078c2ecf20Sopenharmony_ci * @param order       Parameter for allocation of the FSM data structs.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_ciextern fsm_instance *
1108c2ecf20Sopenharmony_ciinit_fsm(char *name, const char **state_names,
1118c2ecf20Sopenharmony_ci	 const char **event_names,
1128c2ecf20Sopenharmony_ci	 int nr_states, int nr_events, const fsm_node *tmpl,
1138c2ecf20Sopenharmony_ci	 int tmpl_len, gfp_t order);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/**
1168c2ecf20Sopenharmony_ci * Releases an FSM
1178c2ecf20Sopenharmony_ci *
1188c2ecf20Sopenharmony_ci * @param fi Pointer to an FSM, previously created with init_fsm.
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_ciextern void kfree_fsm(fsm_instance *fi);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#if FSM_DEBUG_HISTORY
1238c2ecf20Sopenharmony_ciextern void
1248c2ecf20Sopenharmony_cifsm_print_history(fsm_instance *fi);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ciextern void
1278c2ecf20Sopenharmony_cifsm_record_history(fsm_instance *fi, int state, int event);
1288c2ecf20Sopenharmony_ci#endif
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/**
1318c2ecf20Sopenharmony_ci * Emits an event to a FSM.
1328c2ecf20Sopenharmony_ci * If an action function is defined for the current state/event combination,
1338c2ecf20Sopenharmony_ci * this function is called.
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci * @param fi    Pointer to FSM which should receive the event.
1368c2ecf20Sopenharmony_ci * @param event The event do be delivered.
1378c2ecf20Sopenharmony_ci * @param arg   A generic argument, handed to the action function.
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * @return      0  on success,
1408c2ecf20Sopenharmony_ci *              1  if current state or event is out of range
1418c2ecf20Sopenharmony_ci *              !0 if state and event in range, but no action defined.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic inline int
1448c2ecf20Sopenharmony_cifsm_event(fsm_instance *fi, int event, void *arg)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	fsm_function_t r;
1478c2ecf20Sopenharmony_ci	int state = atomic_read(&fi->state);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if ((state >= fi->f->nr_states) ||
1508c2ecf20Sopenharmony_ci	    (event >= fi->f->nr_events)       ) {
1518c2ecf20Sopenharmony_ci		printk(KERN_ERR "fsm(%s): Invalid state st(%ld/%ld) ev(%d/%ld)\n",
1528c2ecf20Sopenharmony_ci			fi->name, (long)state,(long)fi->f->nr_states, event,
1538c2ecf20Sopenharmony_ci			(long)fi->f->nr_events);
1548c2ecf20Sopenharmony_ci#if FSM_DEBUG_HISTORY
1558c2ecf20Sopenharmony_ci		fsm_print_history(fi);
1568c2ecf20Sopenharmony_ci#endif
1578c2ecf20Sopenharmony_ci		return 1;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	r = fi->f->jumpmatrix[fi->f->nr_states * event + state];
1608c2ecf20Sopenharmony_ci	if (r) {
1618c2ecf20Sopenharmony_ci#if FSM_DEBUG
1628c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "fsm(%s): state %s event %s\n",
1638c2ecf20Sopenharmony_ci		       fi->name, fi->f->state_names[state],
1648c2ecf20Sopenharmony_ci		       fi->f->event_names[event]);
1658c2ecf20Sopenharmony_ci#endif
1668c2ecf20Sopenharmony_ci#if FSM_DEBUG_HISTORY
1678c2ecf20Sopenharmony_ci		fsm_record_history(fi, state, event);
1688c2ecf20Sopenharmony_ci#endif
1698c2ecf20Sopenharmony_ci		r(fi, event, arg);
1708c2ecf20Sopenharmony_ci		return 0;
1718c2ecf20Sopenharmony_ci	} else {
1728c2ecf20Sopenharmony_ci#if FSM_DEBUG || FSM_DEBUG_HISTORY
1738c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "fsm(%s): no function for event %s in state %s\n",
1748c2ecf20Sopenharmony_ci		       fi->name, fi->f->event_names[event],
1758c2ecf20Sopenharmony_ci		       fi->f->state_names[state]);
1768c2ecf20Sopenharmony_ci#endif
1778c2ecf20Sopenharmony_ci#if FSM_DEBUG_HISTORY
1788c2ecf20Sopenharmony_ci		fsm_print_history(fi);
1798c2ecf20Sopenharmony_ci#endif
1808c2ecf20Sopenharmony_ci		return !0;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/**
1858c2ecf20Sopenharmony_ci * Modifies the state of an FSM.
1868c2ecf20Sopenharmony_ci * This does <em>not</em> trigger an event or calls an action function.
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * @param fi    Pointer to FSM
1898c2ecf20Sopenharmony_ci * @param state The new state for this FSM.
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_cistatic inline void
1928c2ecf20Sopenharmony_cifsm_newstate(fsm_instance *fi, int newstate)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	atomic_set(&fi->state,newstate);
1958c2ecf20Sopenharmony_ci#if FSM_DEBUG_HISTORY
1968c2ecf20Sopenharmony_ci	fsm_record_history(fi, newstate, -1);
1978c2ecf20Sopenharmony_ci#endif
1988c2ecf20Sopenharmony_ci#if FSM_DEBUG
1998c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "fsm(%s): New state %s\n", fi->name,
2008c2ecf20Sopenharmony_ci		fi->f->state_names[newstate]);
2018c2ecf20Sopenharmony_ci#endif
2028c2ecf20Sopenharmony_ci	wake_up(&fi->wait_q);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/**
2068c2ecf20Sopenharmony_ci * Retrieves the state of an FSM
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * @param fi Pointer to FSM
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * @return The current state of the FSM.
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cistatic inline int
2138c2ecf20Sopenharmony_cifsm_getstate(fsm_instance *fi)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	return atomic_read(&fi->state);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/**
2198c2ecf20Sopenharmony_ci * Retrieves the name of the state of an FSM
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci * @param fi Pointer to FSM
2228c2ecf20Sopenharmony_ci *
2238c2ecf20Sopenharmony_ci * @return The current state of the FSM in a human readable form.
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_ciextern const char *fsm_getstate_str(fsm_instance *fi);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/**
2288c2ecf20Sopenharmony_ci * Initializes a timer for an FSM.
2298c2ecf20Sopenharmony_ci * This prepares an fsm_timer for usage with fsm_addtimer.
2308c2ecf20Sopenharmony_ci *
2318c2ecf20Sopenharmony_ci * @param fi    Pointer to FSM
2328c2ecf20Sopenharmony_ci * @param timer The timer to be initialized.
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_ciextern void fsm_settimer(fsm_instance *fi, fsm_timer *);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/**
2378c2ecf20Sopenharmony_ci * Clears a pending timer of an FSM instance.
2388c2ecf20Sopenharmony_ci *
2398c2ecf20Sopenharmony_ci * @param timer The timer to clear.
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_ciextern void fsm_deltimer(fsm_timer *timer);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/**
2448c2ecf20Sopenharmony_ci * Adds and starts a timer to an FSM instance.
2458c2ecf20Sopenharmony_ci *
2468c2ecf20Sopenharmony_ci * @param timer    The timer to be added. The field fi of that timer
2478c2ecf20Sopenharmony_ci *                 must have been set to point to the instance.
2488c2ecf20Sopenharmony_ci * @param millisec Duration, after which the timer should expire.
2498c2ecf20Sopenharmony_ci * @param event    Event, to trigger if timer expires.
2508c2ecf20Sopenharmony_ci * @param arg      Generic argument, provided to expiry function.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * @return         0 on success, -1 if timer is already active.
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_ciextern int fsm_addtimer(fsm_timer *timer, int millisec, int event, void *arg);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/**
2578c2ecf20Sopenharmony_ci * Modifies a timer of an FSM.
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * @param timer    The timer to modify.
2608c2ecf20Sopenharmony_ci * @param millisec Duration, after which the timer should expire.
2618c2ecf20Sopenharmony_ci * @param event    Event, to trigger if timer expires.
2628c2ecf20Sopenharmony_ci * @param arg      Generic argument, provided to expiry function.
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_ciextern void fsm_modtimer(fsm_timer *timer, int millisec, int event, void *arg);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci#endif /* _FSM_H_ */
267