211 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * A generic FSM based on fsm used in isdn4linux4 *5 */6 7#include "fsm.h"8#include <linux/module.h>9#include <linux/slab.h>10#include <linux/timer.h>11 12MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert <felfert@millenux.com>");13MODULE_DESCRIPTION("Finite state machine helper functions");14MODULE_LICENSE("GPL");15 16fsm_instance *17init_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_WARNING28 "fsm(%s): init_fsm: Couldn't alloc instance\n", name);29 return NULL;30 }31 strscpy(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_WARNING37 "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_WARNING50 "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_ERR60 "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 } else66 m[nr_states * tmpl[i].cond_event + tmpl[i].cond_state] =67 tmpl[i].function;68 }69 return this;70}71 72void73kfree_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 } else82 printk(KERN_WARNING83 "fsm: kfree_fsm called with NULL argument\n");84}85 86#if FSM_DEBUG_HISTORY87void88fsm_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 else105 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 112void113fsm_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#endif122 123const char *124fsm_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 132static void133fsm_expire_timer(struct timer_list *t)134{135 fsm_timer *this = from_timer(this, t, tl);136#if FSM_TIMER_DEBUG137 printk(KERN_DEBUG "fsm(%s): Timer %p expired\n",138 this->fi->name, this);139#endif140 fsm_event(this->fi, this->expire_event, this->event_arg);141}142 143void144fsm_settimer(fsm_instance *fi, fsm_timer *this)145{146 this->fi = fi;147#if FSM_TIMER_DEBUG148 printk(KERN_DEBUG "fsm(%s): Create timer %p\n", fi->name,149 this);150#endif151 timer_setup(&this->tl, fsm_expire_timer, 0);152}153 154void155fsm_deltimer(fsm_timer *this)156{157#if FSM_TIMER_DEBUG158 printk(KERN_DEBUG "fsm(%s): Delete timer %p\n", this->fi->name,159 this);160#endif161 del_timer(&this->tl);162}163 164int165fsm_addtimer(fsm_timer *this, int millisec, int event, void *arg)166{167 168#if FSM_TIMER_DEBUG169 printk(KERN_DEBUG "fsm(%s): Add timer %p %dms\n",170 this->fi->name, this, millisec);171#endif172 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 */182void183fsm_modtimer(fsm_timer *this, int millisec, int event, void *arg)184{185 186#if FSM_TIMER_DEBUG187 printk(KERN_DEBUG "fsm(%s): Restart timer %p %dms\n",188 this->fi->name, this, millisec);189#endif190 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 199EXPORT_SYMBOL(init_fsm);200EXPORT_SYMBOL(kfree_fsm);201EXPORT_SYMBOL(fsm_settimer);202EXPORT_SYMBOL(fsm_deltimer);203EXPORT_SYMBOL(fsm_addtimer);204EXPORT_SYMBOL(fsm_modtimer);205EXPORT_SYMBOL(fsm_getstate_str);206 207#if FSM_DEBUG_HISTORY208EXPORT_SYMBOL(fsm_print_history);209EXPORT_SYMBOL(fsm_record_history);210#endif211