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