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