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 | * Creates an FSM |
99 | * |
100 | * @param name Name of this instance for logging purposes. |
101 | * @param state_names An array of names for all states for logging purposes. |
102 | * @param event_names An array of names for all events for logging purposes. |
103 | * @param nr_states Number of states for this instance. |
104 | * @param nr_events Number of events for this instance. |
105 | * @param tmpl An array of fsm_nodes, describing this FSM. |
106 | * @param tmpl_len Length of the describing array. |
107 | * @param order Parameter for allocation of the FSM data structs. |
108 | */ |
109 | extern fsm_instance * |
110 | init_fsm(char *name, const char **state_names, |
111 | const char **event_names, |
112 | int nr_states, int nr_events, const fsm_node *tmpl, |
113 | int tmpl_len, gfp_t order); |
114 | |
115 | /** |
116 | * Releases an FSM |
117 | * |
118 | * @param fi Pointer to an FSM, previously created with init_fsm. |
119 | */ |
120 | extern void kfree_fsm(fsm_instance *fi); |
121 | |
122 | #if FSM_DEBUG_HISTORY |
123 | extern void |
124 | fsm_print_history(fsm_instance *fi); |
125 | |
126 | extern void |
127 | fsm_record_history(fsm_instance *fi, int state, int event); |
128 | #endif |
129 | |
130 | /** |
131 | * Emits an event to a FSM. |
132 | * If an action function is defined for the current state/event combination, |
133 | * this function is called. |
134 | * |
135 | * @param fi Pointer to FSM which should receive the event. |
136 | * @param event The event do be delivered. |
137 | * @param arg A generic argument, handed to the action function. |
138 | * |
139 | * @return 0 on success, |
140 | * 1 if current state or event is out of range |
141 | * !0 if state and event in range, but no action defined. |
142 | */ |
143 | static inline int |
144 | fsm_event(fsm_instance *fi, int event, void *arg) |
145 | { |
146 | fsm_function_t r; |
147 | int state = atomic_read(v: &fi->state); |
148 | |
149 | if ((state >= fi->f->nr_states) || |
150 | (event >= fi->f->nr_events) ) { |
151 | printk(KERN_ERR "fsm(%s): Invalid state st(%ld/%ld) ev(%d/%ld)\n" , |
152 | fi->name, (long)state,(long)fi->f->nr_states, event, |
153 | (long)fi->f->nr_events); |
154 | #if FSM_DEBUG_HISTORY |
155 | fsm_print_history(fi); |
156 | #endif |
157 | return 1; |
158 | } |
159 | r = fi->f->jumpmatrix[fi->f->nr_states * event + state]; |
160 | if (r) { |
161 | #if FSM_DEBUG |
162 | printk(KERN_DEBUG "fsm(%s): state %s event %s\n" , |
163 | fi->name, fi->f->state_names[state], |
164 | fi->f->event_names[event]); |
165 | #endif |
166 | #if FSM_DEBUG_HISTORY |
167 | fsm_record_history(fi, state, event); |
168 | #endif |
169 | r(fi, event, arg); |
170 | return 0; |
171 | } else { |
172 | #if FSM_DEBUG || FSM_DEBUG_HISTORY |
173 | printk(KERN_DEBUG "fsm(%s): no function for event %s in state %s\n" , |
174 | fi->name, fi->f->event_names[event], |
175 | fi->f->state_names[state]); |
176 | #endif |
177 | #if FSM_DEBUG_HISTORY |
178 | fsm_print_history(fi); |
179 | #endif |
180 | return !0; |
181 | } |
182 | } |
183 | |
184 | /** |
185 | * Modifies the state of an FSM. |
186 | * This does <em>not</em> trigger an event or calls an action function. |
187 | * |
188 | * @param fi Pointer to FSM |
189 | * @param state The new state for this FSM. |
190 | */ |
191 | static inline void |
192 | fsm_newstate(fsm_instance *fi, int newstate) |
193 | { |
194 | atomic_set(v: &fi->state,i: newstate); |
195 | #if FSM_DEBUG_HISTORY |
196 | fsm_record_history(fi, newstate, -1); |
197 | #endif |
198 | #if FSM_DEBUG |
199 | printk(KERN_DEBUG "fsm(%s): New state %s\n" , fi->name, |
200 | fi->f->state_names[newstate]); |
201 | #endif |
202 | wake_up(&fi->wait_q); |
203 | } |
204 | |
205 | /** |
206 | * Retrieves the state of an FSM |
207 | * |
208 | * @param fi Pointer to FSM |
209 | * |
210 | * @return The current state of the FSM. |
211 | */ |
212 | static inline int |
213 | fsm_getstate(fsm_instance *fi) |
214 | { |
215 | return atomic_read(v: &fi->state); |
216 | } |
217 | |
218 | /** |
219 | * Retrieves the name of the state of an FSM |
220 | * |
221 | * @param fi Pointer to FSM |
222 | * |
223 | * @return The current state of the FSM in a human readable form. |
224 | */ |
225 | extern const char *fsm_getstate_str(fsm_instance *fi); |
226 | |
227 | /** |
228 | * Initializes a timer for an FSM. |
229 | * This prepares an fsm_timer for usage with fsm_addtimer. |
230 | * |
231 | * @param fi Pointer to FSM |
232 | * @param timer The timer to be initialized. |
233 | */ |
234 | extern void fsm_settimer(fsm_instance *fi, fsm_timer *); |
235 | |
236 | /** |
237 | * Clears a pending timer of an FSM instance. |
238 | * |
239 | * @param timer The timer to clear. |
240 | */ |
241 | extern void fsm_deltimer(fsm_timer *timer); |
242 | |
243 | /** |
244 | * Adds and starts a timer to an FSM instance. |
245 | * |
246 | * @param timer The timer to be added. The field fi of that timer |
247 | * must have been set to point to the instance. |
248 | * @param millisec Duration, after which the timer should expire. |
249 | * @param event Event, to trigger if timer expires. |
250 | * @param arg Generic argument, provided to expiry function. |
251 | * |
252 | * @return 0 on success, -1 if timer is already active. |
253 | */ |
254 | extern int fsm_addtimer(fsm_timer *timer, int millisec, int event, void *arg); |
255 | |
256 | /** |
257 | * Modifies a timer of an FSM. |
258 | * |
259 | * @param timer The timer to modify. |
260 | * @param millisec Duration, after which the timer should expire. |
261 | * @param event Event, to trigger if timer expires. |
262 | * @param arg Generic argument, provided to expiry function. |
263 | */ |
264 | extern void fsm_modtimer(fsm_timer *timer, int millisec, int event, void *arg); |
265 | |
266 | #endif /* _FSM_H_ */ |
267 | |