1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _FSM_H_
3 #define _FSM_H_
4
5 #include <linux/kernel.h>
6 #include <linux/types.h>
7 #include <linux/timer.h>
8 #include <linux/time.h>
9 #include <linux/slab.h>
10 #include <linux/sched.h>
11 #include <linux/string.h>
12 #include <linux/atomic.h>
13
14 /*
15 * Define this to get debugging messages.
16 */
17 #define FSM_DEBUG 0
18
19 /*
20 * Define this to get debugging massages for
21 * timer handling.
22 */
23 #define FSM_TIMER_DEBUG 0
24
25 /*
26 * Define these to record a history of
27 * Events/Statechanges and print it if a
28 * action_function is not found.
29 */
30 #define FSM_DEBUG_HISTORY 0
31 #define FSM_HISTORY_SIZE 40
32
33 struct fsm_instance_t;
34
35 /*
36 * Definition of an action function, called by a FSM
37 */
38 typedef void (*fsm_function_t)(struct fsm_instance_t *, int, void *);
39
40 /*
41 * Internal jump table for a FSM
42 */
43 typedef struct {
44 fsm_function_t *jumpmatrix;
45 int nr_events;
46 int nr_states;
47 const char **event_names;
48 const char **state_names;
49 } fsm;
50
51 #if FSM_DEBUG_HISTORY
52 /*
53 * Element of State/Event history used for debugging.
54 */
55 typedef struct {
56 int state;
57 int event;
58 } fsm_history;
59 #endif
60
61 /*
62 * Representation of a FSM
63 */
64 typedef struct fsm_instance_t {
65 fsm *f;
66 atomic_t state;
67 char name[16];
68 void *userdata;
69 int userint;
70 wait_queue_head_t wait_q;
71 #if FSM_DEBUG_HISTORY
72 int history_index;
73 int history_size;
74 fsm_history history[FSM_HISTORY_SIZE];
75 #endif
76 } fsm_instance;
77
78 /*
79 * Description of a state-event combination
80 */
81 typedef struct {
82 int cond_state;
83 int cond_event;
84 fsm_function_t function;
85 } fsm_node;
86
87 /*
88 * Description of a FSM Timer.
89 */
90 typedef struct {
91 fsm_instance *fi;
92 struct timer_list tl;
93 int expire_event;
94 void *event_arg;
95 } fsm_timer;
96
97 /**
98 * init_fsm - Creates a finite state machine
99 * @name: Name of this instance for logging purposes
100 * @state_names: Array of names for all states for logging purposes
101 * @event_names: Array of names for all events for logging purposes
102 * @nr_states: Number of states for this instance
103 * @nr_events: Number of events for this instance
104 * @tmpl: Pointer to fsm_node array describing this FSM
105 * @tmpl_len: Number of entries in the tmpl array
106 * @order: GFP flags for memory allocation (e.g. GFP_KERNEL)
107 *
108 * Allocates and initializes a finite state machine instance with the
109 * specified states, events, and transition table.
110 *
111 * Return: Pointer to initialized FSM instance, or NULL on failure
112 */
113 fsm_instance *init_fsm(char *name, const char **state_names,
114 const char **event_names, int nr_states,
115 int nr_events, const fsm_node *tmpl,
116 int tmpl_len, gfp_t order);
117
118 /**
119 * kfree_fsm - Releases a finite state machine
120 * @fi: Pointer to FSM instance, previously created with init_fsm()
121 *
122 * Frees all memory associated with the FSM instance.
123 */
124 void kfree_fsm(fsm_instance *fi);
125
126 #if FSM_DEBUG_HISTORY
127 void fsm_print_history(fsm_instance *fi);
128
129 void fsm_record_history(fsm_instance *fi, int state, int event);
130 #endif
131
132 /**
133 * fsm_event - Emits an event to a finite state machine
134 * @fi: Pointer to FSM which should receive the event
135 * @event: The event to be delivered
136 * @arg: Generic argument, passed to the action function
137 *
138 * If an action function is defined for the current state/event
139 * combination, that function is called with the provided arguments.
140 *
141 * Return:
142 * * 0 - Success, action function was called
143 * * 1 - State/event out of range, or no action function defined
144 */
145 static inline int
fsm_event(fsm_instance * fi,int event,void * arg)146 fsm_event(fsm_instance *fi, int event, void *arg)
147 {
148 fsm_function_t r;
149 int state = atomic_read(&fi->state);
150
151 if ((state >= fi->f->nr_states) ||
152 (event >= fi->f->nr_events) ) {
153 printk(KERN_ERR "fsm(%s): Invalid state st(%ld/%ld) ev(%d/%ld)\n",
154 fi->name, (long)state,(long)fi->f->nr_states, event,
155 (long)fi->f->nr_events);
156 #if FSM_DEBUG_HISTORY
157 fsm_print_history(fi);
158 #endif
159 return 1;
160 }
161 r = fi->f->jumpmatrix[fi->f->nr_states * event + state];
162 if (r) {
163 #if FSM_DEBUG
164 printk(KERN_DEBUG "fsm(%s): state %s event %s\n",
165 fi->name, fi->f->state_names[state],
166 fi->f->event_names[event]);
167 #endif
168 #if FSM_DEBUG_HISTORY
169 fsm_record_history(fi, state, event);
170 #endif
171 r(fi, event, arg);
172 return 0;
173 } else {
174 #if FSM_DEBUG || FSM_DEBUG_HISTORY
175 printk(KERN_DEBUG "fsm(%s): no function for event %s in state %s\n",
176 fi->name, fi->f->event_names[event],
177 fi->f->state_names[state]);
178 #endif
179 #if FSM_DEBUG_HISTORY
180 fsm_print_history(fi);
181 #endif
182 return !0;
183 }
184 }
185
186 /**
187 * fsm_newstate - Modifies the state of a finite state machine
188 * @fi: Pointer to FSM
189 * @newstate: The new state for this FSM
190 *
191 * This does not trigger an event or call an action function.
192 * Wakes up any processes waiting on the FSM's wait queue.
193 */
194 static inline void
fsm_newstate(fsm_instance * fi,int newstate)195 fsm_newstate(fsm_instance *fi, int newstate)
196 {
197 atomic_set(&fi->state,newstate);
198 #if FSM_DEBUG_HISTORY
199 fsm_record_history(fi, newstate, -1);
200 #endif
201 #if FSM_DEBUG
202 printk(KERN_DEBUG "fsm(%s): New state %s\n", fi->name,
203 fi->f->state_names[newstate]);
204 #endif
205 wake_up(&fi->wait_q);
206 }
207
208 /**
209 * fsm_getstate - Retrieves the current state of a finite state machine
210 * @fi: Pointer to FSM
211 *
212 * Return: Current state number
213 */
214 static inline int
fsm_getstate(fsm_instance * fi)215 fsm_getstate(fsm_instance *fi)
216 {
217 return atomic_read(&fi->state);
218 }
219
220 /**
221 * fsm_getstate_str - Retrieves the name of the current FSM state
222 * @fi: Pointer to FSM
223 *
224 * Return: State name string, or "Invalid" if state is out of range
225 */
226 const char *fsm_getstate_str(fsm_instance *fi);
227
228 /**
229 * fsm_settimer - Initializes a timer for a finite state machine
230 * @fi: Pointer to FSM
231 * @this: The timer to be initialized
232 *
233 * Prepares an fsm_timer for usage with fsm_addtimer().
234 */
235 void fsm_settimer(fsm_instance *fi, fsm_timer *this);
236
237 /**
238 * fsm_deltimer - Clears a pending timer of an FSM instance
239 * @timer: The timer to clear
240 *
241 * Stops and removes the timer. Safe to call on an inactive timer.
242 */
243 void fsm_deltimer(fsm_timer *timer);
244
245 /**
246 * fsm_addtimer - Adds and starts a timer for an FSM instance
247 * @timer: The timer to be added (timer->fi must point to the FSM instance)
248 * @millisec: Duration in milliseconds after which the timer expires
249 * @event: Event to trigger when timer expires
250 * @arg: Generic argument provided to the event handler
251 *
252 * Starts a timer that will trigger the specified event after the given
253 * duration. The timer must have been initialized with fsm_settimer().
254 *
255 * Return: Always returns 0
256 */
257 int fsm_addtimer(fsm_timer *timer, int millisec, int event, void *arg);
258
259 /**
260 * fsm_modtimer - Modifies a timer of a finite state machine
261 * @timer: The timer to modify
262 * @millisec: New duration in milliseconds after which the timer expires
263 * @event: Event to trigger when timer expires
264 * @arg: Generic argument provided to the event handler
265 *
266 * Stops the existing timer and restarts it with new parameters.
267 */
268 void fsm_modtimer(fsm_timer *timer, int millisec, int event, void *arg);
269
270 #endif /* _FSM_H_ */
271