1 // SPDX-License-Identifier: GPL-2.0
2 /**
3  * A generic FSM based on fsm used in isdn4linux
4  *
5  */
6 
7 #include "fsm.h"
8 #include <linux/module.h>
9 #include <linux/slab.h>
10 #include <linux/timer.h>
11 
12 MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
13 MODULE_DESCRIPTION("Finite state machine helper functions");
14 MODULE_LICENSE("GPL");
15 
16 fsm_instance *
init_fsm(char * name,const char ** state_names,const char ** event_names,int nr_states,int nr_events,const fsm_node * tmpl,int tmpl_len,gfp_t order)17 init_fsm(char *name, const char **state_names, const char **event_names, int nr_states,
18 		int nr_events, const fsm_node *tmpl, int tmpl_len, gfp_t order)
19 {
20 	int i;
21 	fsm_instance *this;
22 	fsm_function_t *m;
23 	fsm *f;
24 
25 	this = kzalloc(sizeof(fsm_instance), order);
26 	if (this == NULL) {
27 		printk(KERN_WARNING
28 			"fsm(%s): init_fsm: Couldn't alloc instance\n", name);
29 		return NULL;
30 	}
31 	strlcpy(this->name, name, sizeof(this->name));
32 	init_waitqueue_head(&this->wait_q);
33 
34 	f = kzalloc(sizeof(fsm), order);
35 	if (f == NULL) {
36 		printk(KERN_WARNING
37 			"fsm(%s): init_fsm: Couldn't alloc fsm\n", name);
38 		kfree_fsm(this);
39 		return NULL;
40 	}
41 	f->nr_events = nr_events;
42 	f->nr_states = nr_states;
43 	f->event_names = event_names;
44 	f->state_names = state_names;
45 	this->f = f;
46 
47 	m = kcalloc(nr_states*nr_events, sizeof(fsm_function_t), order);
48 	if (m == NULL) {
49 		printk(KERN_WARNING
50 			"fsm(%s): init_fsm: Couldn't alloc jumptable\n", name);
51 		kfree_fsm(this);
52 		return NULL;
53 	}
54 	f->jumpmatrix = m;
55 
56 	for (i = 0; i < tmpl_len; i++) {
57 		if ((tmpl[i].cond_state >= nr_states) ||
58 		    (tmpl[i].cond_event >= nr_events)   ) {
59 			printk(KERN_ERR
60 				"fsm(%s): init_fsm: Bad template l=%d st(%ld/%ld) ev(%ld/%ld)\n",
61 				name, i, (long)tmpl[i].cond_state, (long)f->nr_states,
62 				(long)tmpl[i].cond_event, (long)f->nr_events);
63 			kfree_fsm(this);
64 			return NULL;
65 		} else
66 			m[nr_states * tmpl[i].cond_event + tmpl[i].cond_state] =
67 				tmpl[i].function;
68 	}
69 	return this;
70 }
71 
72 void
kfree_fsm(fsm_instance * this)73 kfree_fsm(fsm_instance *this)
74 {
75 	if (this) {
76 		if (this->f) {
77 			kfree(this->f->jumpmatrix);
78 			kfree(this->f);
79 		}
80 		kfree(this);
81 	} else
82 		printk(KERN_WARNING
83 			"fsm: kfree_fsm called with NULL argument\n");
84 }
85 
86 #if FSM_DEBUG_HISTORY
87 void
fsm_print_history(fsm_instance * fi)88 fsm_print_history(fsm_instance *fi)
89 {
90 	int idx = 0;
91 	int i;
92 
93 	if (fi->history_size >= FSM_HISTORY_SIZE)
94 		idx = fi->history_index;
95 
96 	printk(KERN_DEBUG "fsm(%s): History:\n", fi->name);
97 	for (i = 0; i < fi->history_size; i++) {
98 		int e = fi->history[idx].event;
99 		int s = fi->history[idx++].state;
100 		idx %= FSM_HISTORY_SIZE;
101 		if (e == -1)
102 			printk(KERN_DEBUG "  S=%s\n",
103 			       fi->f->state_names[s]);
104 		else
105 			printk(KERN_DEBUG "  S=%s E=%s\n",
106 			       fi->f->state_names[s],
107 			       fi->f->event_names[e]);
108 	}
109 	fi->history_size = fi->history_index = 0;
110 }
111 
112 void
fsm_record_history(fsm_instance * fi,int state,int event)113 fsm_record_history(fsm_instance *fi, int state, int event)
114 {
115 	fi->history[fi->history_index].state = state;
116 	fi->history[fi->history_index++].event = event;
117 	fi->history_index %= FSM_HISTORY_SIZE;
118 	if (fi->history_size < FSM_HISTORY_SIZE)
119 		fi->history_size++;
120 }
121 #endif
122 
123 const char *
fsm_getstate_str(fsm_instance * fi)124 fsm_getstate_str(fsm_instance *fi)
125 {
126 	int st = atomic_read(&fi->state);
127 	if (st >= fi->f->nr_states)
128 		return "Invalid";
129 	return fi->f->state_names[st];
130 }
131 
132 static void
fsm_expire_timer(struct timer_list * t)133 fsm_expire_timer(struct timer_list *t)
134 {
135 	fsm_timer *this = from_timer(this, t, tl);
136 #if FSM_TIMER_DEBUG
137 	printk(KERN_DEBUG "fsm(%s): Timer %p expired\n",
138 	       this->fi->name, this);
139 #endif
140 	fsm_event(this->fi, this->expire_event, this->event_arg);
141 }
142 
143 void
fsm_settimer(fsm_instance * fi,fsm_timer * this)144 fsm_settimer(fsm_instance *fi, fsm_timer *this)
145 {
146 	this->fi = fi;
147 #if FSM_TIMER_DEBUG
148 	printk(KERN_DEBUG "fsm(%s): Create timer %p\n", fi->name,
149 	       this);
150 #endif
151 	timer_setup(&this->tl, fsm_expire_timer, 0);
152 }
153 
154 void
fsm_deltimer(fsm_timer * this)155 fsm_deltimer(fsm_timer *this)
156 {
157 #if FSM_TIMER_DEBUG
158 	printk(KERN_DEBUG "fsm(%s): Delete timer %p\n", this->fi->name,
159 		this);
160 #endif
161 	del_timer(&this->tl);
162 }
163 
164 int
fsm_addtimer(fsm_timer * this,int millisec,int event,void * arg)165 fsm_addtimer(fsm_timer *this, int millisec, int event, void *arg)
166 {
167 
168 #if FSM_TIMER_DEBUG
169 	printk(KERN_DEBUG "fsm(%s): Add timer %p %dms\n",
170 	       this->fi->name, this, millisec);
171 #endif
172 
173 	timer_setup(&this->tl, fsm_expire_timer, 0);
174 	this->expire_event = event;
175 	this->event_arg = arg;
176 	this->tl.expires = jiffies + (millisec * HZ) / 1000;
177 	add_timer(&this->tl);
178 	return 0;
179 }
180 
181 /* FIXME: this function is never used, why */
182 void
fsm_modtimer(fsm_timer * this,int millisec,int event,void * arg)183 fsm_modtimer(fsm_timer *this, int millisec, int event, void *arg)
184 {
185 
186 #if FSM_TIMER_DEBUG
187 	printk(KERN_DEBUG "fsm(%s): Restart timer %p %dms\n",
188 		this->fi->name, this, millisec);
189 #endif
190 
191 	del_timer(&this->tl);
192 	timer_setup(&this->tl, fsm_expire_timer, 0);
193 	this->expire_event = event;
194 	this->event_arg = arg;
195 	this->tl.expires = jiffies + (millisec * HZ) / 1000;
196 	add_timer(&this->tl);
197 }
198 
199 EXPORT_SYMBOL(init_fsm);
200 EXPORT_SYMBOL(kfree_fsm);
201 EXPORT_SYMBOL(fsm_settimer);
202 EXPORT_SYMBOL(fsm_deltimer);
203 EXPORT_SYMBOL(fsm_addtimer);
204 EXPORT_SYMBOL(fsm_modtimer);
205 EXPORT_SYMBOL(fsm_getstate_str);
206 
207 #if FSM_DEBUG_HISTORY
208 EXPORT_SYMBOL(fsm_print_history);
209 EXPORT_SYMBOL(fsm_record_history);
210 #endif
211