1*b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
21da177e4SLinus Torvalds #ifndef _FSM_H_
31da177e4SLinus Torvalds #define _FSM_H_
41da177e4SLinus Torvalds
51da177e4SLinus Torvalds #include <linux/kernel.h>
61da177e4SLinus Torvalds #include <linux/types.h>
71da177e4SLinus Torvalds #include <linux/timer.h>
81da177e4SLinus Torvalds #include <linux/time.h>
91da177e4SLinus Torvalds #include <linux/slab.h>
101da177e4SLinus Torvalds #include <linux/sched.h>
111da177e4SLinus Torvalds #include <linux/string.h>
1260063497SArun Sharma #include <linux/atomic.h>
131da177e4SLinus Torvalds
141da177e4SLinus Torvalds /**
151da177e4SLinus Torvalds * Define this to get debugging messages.
161da177e4SLinus Torvalds */
171da177e4SLinus Torvalds #define FSM_DEBUG 0
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds /**
201da177e4SLinus Torvalds * Define this to get debugging massages for
211da177e4SLinus Torvalds * timer handling.
221da177e4SLinus Torvalds */
231da177e4SLinus Torvalds #define FSM_TIMER_DEBUG 0
241da177e4SLinus Torvalds
251da177e4SLinus Torvalds /**
261da177e4SLinus Torvalds * Define these to record a history of
271da177e4SLinus Torvalds * Events/Statechanges and print it if a
281da177e4SLinus Torvalds * action_function is not found.
291da177e4SLinus Torvalds */
301da177e4SLinus Torvalds #define FSM_DEBUG_HISTORY 0
311da177e4SLinus Torvalds #define FSM_HISTORY_SIZE 40
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds struct fsm_instance_t;
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds /**
361da177e4SLinus Torvalds * Definition of an action function, called by a FSM
371da177e4SLinus Torvalds */
381da177e4SLinus Torvalds typedef void (*fsm_function_t)(struct fsm_instance_t *, int, void *);
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds /**
411da177e4SLinus Torvalds * Internal jump table for a FSM
421da177e4SLinus Torvalds */
431da177e4SLinus Torvalds typedef struct {
441da177e4SLinus Torvalds fsm_function_t *jumpmatrix;
451da177e4SLinus Torvalds int nr_events;
461da177e4SLinus Torvalds int nr_states;
471da177e4SLinus Torvalds const char **event_names;
481da177e4SLinus Torvalds const char **state_names;
491da177e4SLinus Torvalds } fsm;
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY
521da177e4SLinus Torvalds /**
531da177e4SLinus Torvalds * Element of State/Event history used for debugging.
541da177e4SLinus Torvalds */
551da177e4SLinus Torvalds typedef struct {
561da177e4SLinus Torvalds int state;
571da177e4SLinus Torvalds int event;
581da177e4SLinus Torvalds } fsm_history;
591da177e4SLinus Torvalds #endif
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds /**
621da177e4SLinus Torvalds * Representation of a FSM
631da177e4SLinus Torvalds */
641da177e4SLinus Torvalds typedef struct fsm_instance_t {
651da177e4SLinus Torvalds fsm *f;
661da177e4SLinus Torvalds atomic_t state;
671da177e4SLinus Torvalds char name[16];
681da177e4SLinus Torvalds void *userdata;
691da177e4SLinus Torvalds int userint;
701e1815beSFrank Blaschka wait_queue_head_t wait_q;
711da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY
721da177e4SLinus Torvalds int history_index;
731da177e4SLinus Torvalds int history_size;
741da177e4SLinus Torvalds fsm_history history[FSM_HISTORY_SIZE];
751da177e4SLinus Torvalds #endif
761da177e4SLinus Torvalds } fsm_instance;
771da177e4SLinus Torvalds
781da177e4SLinus Torvalds /**
791da177e4SLinus Torvalds * Description of a state-event combination
801da177e4SLinus Torvalds */
811da177e4SLinus Torvalds typedef struct {
821da177e4SLinus Torvalds int cond_state;
831da177e4SLinus Torvalds int cond_event;
841da177e4SLinus Torvalds fsm_function_t function;
851da177e4SLinus Torvalds } fsm_node;
861da177e4SLinus Torvalds
871da177e4SLinus Torvalds /**
881da177e4SLinus Torvalds * Description of a FSM Timer.
891da177e4SLinus Torvalds */
901da177e4SLinus Torvalds typedef struct {
911da177e4SLinus Torvalds fsm_instance *fi;
921da177e4SLinus Torvalds struct timer_list tl;
931da177e4SLinus Torvalds int expire_event;
941da177e4SLinus Torvalds void *event_arg;
951da177e4SLinus Torvalds } fsm_timer;
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds /**
981da177e4SLinus Torvalds * Creates an FSM
991da177e4SLinus Torvalds *
1001da177e4SLinus Torvalds * @param name Name of this instance for logging purposes.
1011da177e4SLinus Torvalds * @param state_names An array of names for all states for logging purposes.
1021da177e4SLinus Torvalds * @param event_names An array of names for all events for logging purposes.
1031da177e4SLinus Torvalds * @param nr_states Number of states for this instance.
1041da177e4SLinus Torvalds * @param nr_events Number of events for this instance.
1051da177e4SLinus Torvalds * @param tmpl An array of fsm_nodes, describing this FSM.
1061da177e4SLinus Torvalds * @param tmpl_len Length of the describing array.
1071da177e4SLinus Torvalds * @param order Parameter for allocation of the FSM data structs.
1081da177e4SLinus Torvalds */
1091da177e4SLinus Torvalds extern fsm_instance *
1101da177e4SLinus Torvalds init_fsm(char *name, const char **state_names,
1111da177e4SLinus Torvalds const char **event_names,
1121da177e4SLinus Torvalds int nr_states, int nr_events, const fsm_node *tmpl,
113b4e3ca1aSAl Viro int tmpl_len, gfp_t order);
1141da177e4SLinus Torvalds
1151da177e4SLinus Torvalds /**
1161da177e4SLinus Torvalds * Releases an FSM
1171da177e4SLinus Torvalds *
1181da177e4SLinus Torvalds * @param fi Pointer to an FSM, previously created with init_fsm.
1191da177e4SLinus Torvalds */
1201da177e4SLinus Torvalds extern void kfree_fsm(fsm_instance *fi);
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY
1231da177e4SLinus Torvalds extern void
1241da177e4SLinus Torvalds fsm_print_history(fsm_instance *fi);
1251da177e4SLinus Torvalds
1261da177e4SLinus Torvalds extern void
1271da177e4SLinus Torvalds fsm_record_history(fsm_instance *fi, int state, int event);
1281da177e4SLinus Torvalds #endif
1291da177e4SLinus Torvalds
1301da177e4SLinus Torvalds /**
1311da177e4SLinus Torvalds * Emits an event to a FSM.
1321da177e4SLinus Torvalds * If an action function is defined for the current state/event combination,
1331da177e4SLinus Torvalds * this function is called.
1341da177e4SLinus Torvalds *
1351da177e4SLinus Torvalds * @param fi Pointer to FSM which should receive the event.
1361da177e4SLinus Torvalds * @param event The event do be delivered.
1371da177e4SLinus Torvalds * @param arg A generic argument, handed to the action function.
1381da177e4SLinus Torvalds *
1391da177e4SLinus Torvalds * @return 0 on success,
1401da177e4SLinus Torvalds * 1 if current state or event is out of range
1411da177e4SLinus Torvalds * !0 if state and event in range, but no action defined.
1421da177e4SLinus Torvalds */
1434448aaf0SAdrian Bunk static inline int
fsm_event(fsm_instance * fi,int event,void * arg)1441da177e4SLinus Torvalds fsm_event(fsm_instance *fi, int event, void *arg)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds fsm_function_t r;
1471da177e4SLinus Torvalds int state = atomic_read(&fi->state);
1481da177e4SLinus Torvalds
1491da177e4SLinus Torvalds if ((state >= fi->f->nr_states) ||
1501da177e4SLinus Torvalds (event >= fi->f->nr_events) ) {
1511da177e4SLinus Torvalds printk(KERN_ERR "fsm(%s): Invalid state st(%ld/%ld) ev(%d/%ld)\n",
1521da177e4SLinus Torvalds fi->name, (long)state,(long)fi->f->nr_states, event,
1531da177e4SLinus Torvalds (long)fi->f->nr_events);
1541da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY
1551da177e4SLinus Torvalds fsm_print_history(fi);
1561da177e4SLinus Torvalds #endif
1571da177e4SLinus Torvalds return 1;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds r = fi->f->jumpmatrix[fi->f->nr_states * event + state];
1601da177e4SLinus Torvalds if (r) {
1611da177e4SLinus Torvalds #if FSM_DEBUG
1621da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): state %s event %s\n",
1631da177e4SLinus Torvalds fi->name, fi->f->state_names[state],
1641da177e4SLinus Torvalds fi->f->event_names[event]);
1651da177e4SLinus Torvalds #endif
1661da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY
1671da177e4SLinus Torvalds fsm_record_history(fi, state, event);
1681da177e4SLinus Torvalds #endif
1691da177e4SLinus Torvalds r(fi, event, arg);
1701da177e4SLinus Torvalds return 0;
1711da177e4SLinus Torvalds } else {
1721da177e4SLinus Torvalds #if FSM_DEBUG || FSM_DEBUG_HISTORY
1731da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): no function for event %s in state %s\n",
1741da177e4SLinus Torvalds fi->name, fi->f->event_names[event],
1751da177e4SLinus Torvalds fi->f->state_names[state]);
1761da177e4SLinus Torvalds #endif
1771da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY
1781da177e4SLinus Torvalds fsm_print_history(fi);
1791da177e4SLinus Torvalds #endif
1801da177e4SLinus Torvalds return !0;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds /**
1851da177e4SLinus Torvalds * Modifies the state of an FSM.
1861da177e4SLinus Torvalds * This does <em>not</em> trigger an event or calls an action function.
1871da177e4SLinus Torvalds *
1881da177e4SLinus Torvalds * @param fi Pointer to FSM
1891da177e4SLinus Torvalds * @param state The new state for this FSM.
1901da177e4SLinus Torvalds */
1914448aaf0SAdrian Bunk static inline void
fsm_newstate(fsm_instance * fi,int newstate)1921da177e4SLinus Torvalds fsm_newstate(fsm_instance *fi, int newstate)
1931da177e4SLinus Torvalds {
1941da177e4SLinus Torvalds atomic_set(&fi->state,newstate);
1951da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY
1961da177e4SLinus Torvalds fsm_record_history(fi, newstate, -1);
1971da177e4SLinus Torvalds #endif
1981da177e4SLinus Torvalds #if FSM_DEBUG
1991da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): New state %s\n", fi->name,
2001da177e4SLinus Torvalds fi->f->state_names[newstate]);
2011da177e4SLinus Torvalds #endif
2021e1815beSFrank Blaschka wake_up(&fi->wait_q);
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds
2051da177e4SLinus Torvalds /**
2061da177e4SLinus Torvalds * Retrieves the state of an FSM
2071da177e4SLinus Torvalds *
2081da177e4SLinus Torvalds * @param fi Pointer to FSM
2091da177e4SLinus Torvalds *
2101da177e4SLinus Torvalds * @return The current state of the FSM.
2111da177e4SLinus Torvalds */
2124448aaf0SAdrian Bunk static inline int
fsm_getstate(fsm_instance * fi)2131da177e4SLinus Torvalds fsm_getstate(fsm_instance *fi)
2141da177e4SLinus Torvalds {
2151da177e4SLinus Torvalds return atomic_read(&fi->state);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds /**
2191da177e4SLinus Torvalds * Retrieves the name of the state of an FSM
2201da177e4SLinus Torvalds *
2211da177e4SLinus Torvalds * @param fi Pointer to FSM
2221da177e4SLinus Torvalds *
2231da177e4SLinus Torvalds * @return The current state of the FSM in a human readable form.
2241da177e4SLinus Torvalds */
2251da177e4SLinus Torvalds extern const char *fsm_getstate_str(fsm_instance *fi);
2261da177e4SLinus Torvalds
2271da177e4SLinus Torvalds /**
2281da177e4SLinus Torvalds * Initializes a timer for an FSM.
2291da177e4SLinus Torvalds * This prepares an fsm_timer for usage with fsm_addtimer.
2301da177e4SLinus Torvalds *
2311da177e4SLinus Torvalds * @param fi Pointer to FSM
2321da177e4SLinus Torvalds * @param timer The timer to be initialized.
2331da177e4SLinus Torvalds */
2341da177e4SLinus Torvalds extern void fsm_settimer(fsm_instance *fi, fsm_timer *);
2351da177e4SLinus Torvalds
2361da177e4SLinus Torvalds /**
2371da177e4SLinus Torvalds * Clears a pending timer of an FSM instance.
2381da177e4SLinus Torvalds *
2391da177e4SLinus Torvalds * @param timer The timer to clear.
2401da177e4SLinus Torvalds */
2411da177e4SLinus Torvalds extern void fsm_deltimer(fsm_timer *timer);
2421da177e4SLinus Torvalds
2431da177e4SLinus Torvalds /**
2441da177e4SLinus Torvalds * Adds and starts a timer to an FSM instance.
2451da177e4SLinus Torvalds *
2461da177e4SLinus Torvalds * @param timer The timer to be added. The field fi of that timer
2471da177e4SLinus Torvalds * must have been set to point to the instance.
2481da177e4SLinus Torvalds * @param millisec Duration, after which the timer should expire.
2491da177e4SLinus Torvalds * @param event Event, to trigger if timer expires.
2501da177e4SLinus Torvalds * @param arg Generic argument, provided to expiry function.
2511da177e4SLinus Torvalds *
2521da177e4SLinus Torvalds * @return 0 on success, -1 if timer is already active.
2531da177e4SLinus Torvalds */
2541da177e4SLinus Torvalds extern int fsm_addtimer(fsm_timer *timer, int millisec, int event, void *arg);
2551da177e4SLinus Torvalds
2561da177e4SLinus Torvalds /**
2571da177e4SLinus Torvalds * Modifies a timer of an FSM.
2581da177e4SLinus Torvalds *
2591da177e4SLinus Torvalds * @param timer The timer to modify.
2601da177e4SLinus Torvalds * @param millisec Duration, after which the timer should expire.
2611da177e4SLinus Torvalds * @param event Event, to trigger if timer expires.
2621da177e4SLinus Torvalds * @param arg Generic argument, provided to expiry function.
2631da177e4SLinus Torvalds */
2641da177e4SLinus Torvalds extern void fsm_modtimer(fsm_timer *timer, int millisec, int event, void *arg);
2651da177e4SLinus Torvalds
2661da177e4SLinus Torvalds #endif /* _FSM_H_ */
267