mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
4.7 KiB
210 lines
4.7 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/** |
|
* A generic FSM based on fsm used in isdn4linux |
|
* |
|
*/ |
|
|
|
#include "fsm.h" |
|
#include <linux/module.h> |
|
#include <linux/slab.h> |
|
#include <linux/timer.h> |
|
|
|
MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert ([email protected])"); |
|
MODULE_DESCRIPTION("Finite state machine helper functions"); |
|
MODULE_LICENSE("GPL"); |
|
|
|
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) |
|
{ |
|
int i; |
|
fsm_instance *this; |
|
fsm_function_t *m; |
|
fsm *f; |
|
|
|
this = kzalloc(sizeof(fsm_instance), order); |
|
if (this == NULL) { |
|
printk(KERN_WARNING |
|
"fsm(%s): init_fsm: Couldn't alloc instance\n", name); |
|
return NULL; |
|
} |
|
strlcpy(this->name, name, sizeof(this->name)); |
|
init_waitqueue_head(&this->wait_q); |
|
|
|
f = kzalloc(sizeof(fsm), order); |
|
if (f == NULL) { |
|
printk(KERN_WARNING |
|
"fsm(%s): init_fsm: Couldn't alloc fsm\n", name); |
|
kfree_fsm(this); |
|
return NULL; |
|
} |
|
f->nr_events = nr_events; |
|
f->nr_states = nr_states; |
|
f->event_names = event_names; |
|
f->state_names = state_names; |
|
this->f = f; |
|
|
|
m = kcalloc(nr_states*nr_events, sizeof(fsm_function_t), order); |
|
if (m == NULL) { |
|
printk(KERN_WARNING |
|
"fsm(%s): init_fsm: Couldn't alloc jumptable\n", name); |
|
kfree_fsm(this); |
|
return NULL; |
|
} |
|
f->jumpmatrix = m; |
|
|
|
for (i = 0; i < tmpl_len; i++) { |
|
if ((tmpl[i].cond_state >= nr_states) || |
|
(tmpl[i].cond_event >= nr_events) ) { |
|
printk(KERN_ERR |
|
"fsm(%s): init_fsm: Bad template l=%d st(%ld/%ld) ev(%ld/%ld)\n", |
|
name, i, (long)tmpl[i].cond_state, (long)f->nr_states, |
|
(long)tmpl[i].cond_event, (long)f->nr_events); |
|
kfree_fsm(this); |
|
return NULL; |
|
} else |
|
m[nr_states * tmpl[i].cond_event + tmpl[i].cond_state] = |
|
tmpl[i].function; |
|
} |
|
return this; |
|
} |
|
|
|
void |
|
kfree_fsm(fsm_instance *this) |
|
{ |
|
if (this) { |
|
if (this->f) { |
|
kfree(this->f->jumpmatrix); |
|
kfree(this->f); |
|
} |
|
kfree(this); |
|
} else |
|
printk(KERN_WARNING |
|
"fsm: kfree_fsm called with NULL argument\n"); |
|
} |
|
|
|
#if FSM_DEBUG_HISTORY |
|
void |
|
fsm_print_history(fsm_instance *fi) |
|
{ |
|
int idx = 0; |
|
int i; |
|
|
|
if (fi->history_size >= FSM_HISTORY_SIZE) |
|
idx = fi->history_index; |
|
|
|
printk(KERN_DEBUG "fsm(%s): History:\n", fi->name); |
|
for (i = 0; i < fi->history_size; i++) { |
|
int e = fi->history[idx].event; |
|
int s = fi->history[idx++].state; |
|
idx %= FSM_HISTORY_SIZE; |
|
if (e == -1) |
|
printk(KERN_DEBUG " S=%s\n", |
|
fi->f->state_names[s]); |
|
else |
|
printk(KERN_DEBUG " S=%s E=%s\n", |
|
fi->f->state_names[s], |
|
fi->f->event_names[e]); |
|
} |
|
fi->history_size = fi->history_index = 0; |
|
} |
|
|
|
void |
|
fsm_record_history(fsm_instance *fi, int state, int event) |
|
{ |
|
fi->history[fi->history_index].state = state; |
|
fi->history[fi->history_index++].event = event; |
|
fi->history_index %= FSM_HISTORY_SIZE; |
|
if (fi->history_size < FSM_HISTORY_SIZE) |
|
fi->history_size++; |
|
} |
|
#endif |
|
|
|
const char * |
|
fsm_getstate_str(fsm_instance *fi) |
|
{ |
|
int st = atomic_read(&fi->state); |
|
if (st >= fi->f->nr_states) |
|
return "Invalid"; |
|
return fi->f->state_names[st]; |
|
} |
|
|
|
static void |
|
fsm_expire_timer(struct timer_list *t) |
|
{ |
|
fsm_timer *this = from_timer(this, t, tl); |
|
#if FSM_TIMER_DEBUG |
|
printk(KERN_DEBUG "fsm(%s): Timer %p expired\n", |
|
this->fi->name, this); |
|
#endif |
|
fsm_event(this->fi, this->expire_event, this->event_arg); |
|
} |
|
|
|
void |
|
fsm_settimer(fsm_instance *fi, fsm_timer *this) |
|
{ |
|
this->fi = fi; |
|
#if FSM_TIMER_DEBUG |
|
printk(KERN_DEBUG "fsm(%s): Create timer %p\n", fi->name, |
|
this); |
|
#endif |
|
timer_setup(&this->tl, fsm_expire_timer, 0); |
|
} |
|
|
|
void |
|
fsm_deltimer(fsm_timer *this) |
|
{ |
|
#if FSM_TIMER_DEBUG |
|
printk(KERN_DEBUG "fsm(%s): Delete timer %p\n", this->fi->name, |
|
this); |
|
#endif |
|
del_timer(&this->tl); |
|
} |
|
|
|
int |
|
fsm_addtimer(fsm_timer *this, int millisec, int event, void *arg) |
|
{ |
|
|
|
#if FSM_TIMER_DEBUG |
|
printk(KERN_DEBUG "fsm(%s): Add timer %p %dms\n", |
|
this->fi->name, this, millisec); |
|
#endif |
|
|
|
timer_setup(&this->tl, fsm_expire_timer, 0); |
|
this->expire_event = event; |
|
this->event_arg = arg; |
|
this->tl.expires = jiffies + (millisec * HZ) / 1000; |
|
add_timer(&this->tl); |
|
return 0; |
|
} |
|
|
|
/* FIXME: this function is never used, why */ |
|
void |
|
fsm_modtimer(fsm_timer *this, int millisec, int event, void *arg) |
|
{ |
|
|
|
#if FSM_TIMER_DEBUG |
|
printk(KERN_DEBUG "fsm(%s): Restart timer %p %dms\n", |
|
this->fi->name, this, millisec); |
|
#endif |
|
|
|
del_timer(&this->tl); |
|
timer_setup(&this->tl, fsm_expire_timer, 0); |
|
this->expire_event = event; |
|
this->event_arg = arg; |
|
this->tl.expires = jiffies + (millisec * HZ) / 1000; |
|
add_timer(&this->tl); |
|
} |
|
|
|
EXPORT_SYMBOL(init_fsm); |
|
EXPORT_SYMBOL(kfree_fsm); |
|
EXPORT_SYMBOL(fsm_settimer); |
|
EXPORT_SYMBOL(fsm_deltimer); |
|
EXPORT_SYMBOL(fsm_addtimer); |
|
EXPORT_SYMBOL(fsm_modtimer); |
|
EXPORT_SYMBOL(fsm_getstate_str); |
|
|
|
#if FSM_DEBUG_HISTORY |
|
EXPORT_SYMBOL(fsm_print_history); |
|
EXPORT_SYMBOL(fsm_record_history); |
|
#endif
|
|
|