162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * A generic FSM based on fsm used in isdn4linux
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "fsm.h"
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/timer.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciMODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
1362306a36Sopenharmony_ciMODULE_DESCRIPTION("Finite state machine helper functions");
1462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cifsm_instance *
1762306a36Sopenharmony_ciinit_fsm(char *name, const char **state_names, const char **event_names, int nr_states,
1862306a36Sopenharmony_ci		int nr_events, const fsm_node *tmpl, int tmpl_len, gfp_t order)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	int i;
2162306a36Sopenharmony_ci	fsm_instance *this;
2262306a36Sopenharmony_ci	fsm_function_t *m;
2362306a36Sopenharmony_ci	fsm *f;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	this = kzalloc(sizeof(fsm_instance), order);
2662306a36Sopenharmony_ci	if (this == NULL) {
2762306a36Sopenharmony_ci		printk(KERN_WARNING
2862306a36Sopenharmony_ci			"fsm(%s): init_fsm: Couldn't alloc instance\n", name);
2962306a36Sopenharmony_ci		return NULL;
3062306a36Sopenharmony_ci	}
3162306a36Sopenharmony_ci	strscpy(this->name, name, sizeof(this->name));
3262306a36Sopenharmony_ci	init_waitqueue_head(&this->wait_q);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	f = kzalloc(sizeof(fsm), order);
3562306a36Sopenharmony_ci	if (f == NULL) {
3662306a36Sopenharmony_ci		printk(KERN_WARNING
3762306a36Sopenharmony_ci			"fsm(%s): init_fsm: Couldn't alloc fsm\n", name);
3862306a36Sopenharmony_ci		kfree_fsm(this);
3962306a36Sopenharmony_ci		return NULL;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci	f->nr_events = nr_events;
4262306a36Sopenharmony_ci	f->nr_states = nr_states;
4362306a36Sopenharmony_ci	f->event_names = event_names;
4462306a36Sopenharmony_ci	f->state_names = state_names;
4562306a36Sopenharmony_ci	this->f = f;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	m = kcalloc(nr_states*nr_events, sizeof(fsm_function_t), order);
4862306a36Sopenharmony_ci	if (m == NULL) {
4962306a36Sopenharmony_ci		printk(KERN_WARNING
5062306a36Sopenharmony_ci			"fsm(%s): init_fsm: Couldn't alloc jumptable\n", name);
5162306a36Sopenharmony_ci		kfree_fsm(this);
5262306a36Sopenharmony_ci		return NULL;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	f->jumpmatrix = m;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	for (i = 0; i < tmpl_len; i++) {
5762306a36Sopenharmony_ci		if ((tmpl[i].cond_state >= nr_states) ||
5862306a36Sopenharmony_ci		    (tmpl[i].cond_event >= nr_events)   ) {
5962306a36Sopenharmony_ci			printk(KERN_ERR
6062306a36Sopenharmony_ci				"fsm(%s): init_fsm: Bad template l=%d st(%ld/%ld) ev(%ld/%ld)\n",
6162306a36Sopenharmony_ci				name, i, (long)tmpl[i].cond_state, (long)f->nr_states,
6262306a36Sopenharmony_ci				(long)tmpl[i].cond_event, (long)f->nr_events);
6362306a36Sopenharmony_ci			kfree_fsm(this);
6462306a36Sopenharmony_ci			return NULL;
6562306a36Sopenharmony_ci		} else
6662306a36Sopenharmony_ci			m[nr_states * tmpl[i].cond_event + tmpl[i].cond_state] =
6762306a36Sopenharmony_ci				tmpl[i].function;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	return this;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_civoid
7362306a36Sopenharmony_cikfree_fsm(fsm_instance *this)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if (this) {
7662306a36Sopenharmony_ci		if (this->f) {
7762306a36Sopenharmony_ci			kfree(this->f->jumpmatrix);
7862306a36Sopenharmony_ci			kfree(this->f);
7962306a36Sopenharmony_ci		}
8062306a36Sopenharmony_ci		kfree(this);
8162306a36Sopenharmony_ci	} else
8262306a36Sopenharmony_ci		printk(KERN_WARNING
8362306a36Sopenharmony_ci			"fsm: kfree_fsm called with NULL argument\n");
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#if FSM_DEBUG_HISTORY
8762306a36Sopenharmony_civoid
8862306a36Sopenharmony_cifsm_print_history(fsm_instance *fi)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	int idx = 0;
9162306a36Sopenharmony_ci	int i;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (fi->history_size >= FSM_HISTORY_SIZE)
9462306a36Sopenharmony_ci		idx = fi->history_index;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	printk(KERN_DEBUG "fsm(%s): History:\n", fi->name);
9762306a36Sopenharmony_ci	for (i = 0; i < fi->history_size; i++) {
9862306a36Sopenharmony_ci		int e = fi->history[idx].event;
9962306a36Sopenharmony_ci		int s = fi->history[idx++].state;
10062306a36Sopenharmony_ci		idx %= FSM_HISTORY_SIZE;
10162306a36Sopenharmony_ci		if (e == -1)
10262306a36Sopenharmony_ci			printk(KERN_DEBUG "  S=%s\n",
10362306a36Sopenharmony_ci			       fi->f->state_names[s]);
10462306a36Sopenharmony_ci		else
10562306a36Sopenharmony_ci			printk(KERN_DEBUG "  S=%s E=%s\n",
10662306a36Sopenharmony_ci			       fi->f->state_names[s],
10762306a36Sopenharmony_ci			       fi->f->event_names[e]);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	fi->history_size = fi->history_index = 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid
11362306a36Sopenharmony_cifsm_record_history(fsm_instance *fi, int state, int event)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	fi->history[fi->history_index].state = state;
11662306a36Sopenharmony_ci	fi->history[fi->history_index++].event = event;
11762306a36Sopenharmony_ci	fi->history_index %= FSM_HISTORY_SIZE;
11862306a36Sopenharmony_ci	if (fi->history_size < FSM_HISTORY_SIZE)
11962306a36Sopenharmony_ci		fi->history_size++;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciconst char *
12462306a36Sopenharmony_cifsm_getstate_str(fsm_instance *fi)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int st = atomic_read(&fi->state);
12762306a36Sopenharmony_ci	if (st >= fi->f->nr_states)
12862306a36Sopenharmony_ci		return "Invalid";
12962306a36Sopenharmony_ci	return fi->f->state_names[st];
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void
13362306a36Sopenharmony_cifsm_expire_timer(struct timer_list *t)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	fsm_timer *this = from_timer(this, t, tl);
13662306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
13762306a36Sopenharmony_ci	printk(KERN_DEBUG "fsm(%s): Timer %p expired\n",
13862306a36Sopenharmony_ci	       this->fi->name, this);
13962306a36Sopenharmony_ci#endif
14062306a36Sopenharmony_ci	fsm_event(this->fi, this->expire_event, this->event_arg);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_civoid
14462306a36Sopenharmony_cifsm_settimer(fsm_instance *fi, fsm_timer *this)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	this->fi = fi;
14762306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
14862306a36Sopenharmony_ci	printk(KERN_DEBUG "fsm(%s): Create timer %p\n", fi->name,
14962306a36Sopenharmony_ci	       this);
15062306a36Sopenharmony_ci#endif
15162306a36Sopenharmony_ci	timer_setup(&this->tl, fsm_expire_timer, 0);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_civoid
15562306a36Sopenharmony_cifsm_deltimer(fsm_timer *this)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
15862306a36Sopenharmony_ci	printk(KERN_DEBUG "fsm(%s): Delete timer %p\n", this->fi->name,
15962306a36Sopenharmony_ci		this);
16062306a36Sopenharmony_ci#endif
16162306a36Sopenharmony_ci	del_timer(&this->tl);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciint
16562306a36Sopenharmony_cifsm_addtimer(fsm_timer *this, int millisec, int event, void *arg)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
16962306a36Sopenharmony_ci	printk(KERN_DEBUG "fsm(%s): Add timer %p %dms\n",
17062306a36Sopenharmony_ci	       this->fi->name, this, millisec);
17162306a36Sopenharmony_ci#endif
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	timer_setup(&this->tl, fsm_expire_timer, 0);
17462306a36Sopenharmony_ci	this->expire_event = event;
17562306a36Sopenharmony_ci	this->event_arg = arg;
17662306a36Sopenharmony_ci	this->tl.expires = jiffies + (millisec * HZ) / 1000;
17762306a36Sopenharmony_ci	add_timer(&this->tl);
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/* FIXME: this function is never used, why */
18262306a36Sopenharmony_civoid
18362306a36Sopenharmony_cifsm_modtimer(fsm_timer *this, int millisec, int event, void *arg)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
18762306a36Sopenharmony_ci	printk(KERN_DEBUG "fsm(%s): Restart timer %p %dms\n",
18862306a36Sopenharmony_ci		this->fi->name, this, millisec);
18962306a36Sopenharmony_ci#endif
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	del_timer(&this->tl);
19262306a36Sopenharmony_ci	timer_setup(&this->tl, fsm_expire_timer, 0);
19362306a36Sopenharmony_ci	this->expire_event = event;
19462306a36Sopenharmony_ci	this->event_arg = arg;
19562306a36Sopenharmony_ci	this->tl.expires = jiffies + (millisec * HZ) / 1000;
19662306a36Sopenharmony_ci	add_timer(&this->tl);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciEXPORT_SYMBOL(init_fsm);
20062306a36Sopenharmony_ciEXPORT_SYMBOL(kfree_fsm);
20162306a36Sopenharmony_ciEXPORT_SYMBOL(fsm_settimer);
20262306a36Sopenharmony_ciEXPORT_SYMBOL(fsm_deltimer);
20362306a36Sopenharmony_ciEXPORT_SYMBOL(fsm_addtimer);
20462306a36Sopenharmony_ciEXPORT_SYMBOL(fsm_modtimer);
20562306a36Sopenharmony_ciEXPORT_SYMBOL(fsm_getstate_str);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci#if FSM_DEBUG_HISTORY
20862306a36Sopenharmony_ciEXPORT_SYMBOL(fsm_print_history);
20962306a36Sopenharmony_ciEXPORT_SYMBOL(fsm_record_history);
21062306a36Sopenharmony_ci#endif
211