1ab9953ffSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2*478a3140SHeiko Carstens /* 31da177e4SLinus Torvalds * A generic FSM based on fsm used in isdn4linux 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds #include "fsm.h" 81da177e4SLinus Torvalds #include <linux/module.h> 95a0e3ad6STejun Heo #include <linux/slab.h> 101da177e4SLinus Torvalds #include <linux/timer.h> 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)"); 131da177e4SLinus Torvalds MODULE_DESCRIPTION("Finite state machine helper functions"); 141da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds fsm_instance * 171da177e4SLinus Torvalds init_fsm(char *name, const char **state_names, const char **event_names, int nr_states, 18b4e3ca1aSAl Viro int nr_events, const fsm_node *tmpl, int tmpl_len, gfp_t order) 191da177e4SLinus Torvalds { 201da177e4SLinus Torvalds int i; 211da177e4SLinus Torvalds fsm_instance *this; 221da177e4SLinus Torvalds fsm_function_t *m; 231da177e4SLinus Torvalds fsm *f; 241da177e4SLinus Torvalds 2588abaab4SEric Sesterhenn this = kzalloc(sizeof(fsm_instance), order); 261da177e4SLinus Torvalds if (this == NULL) { 271da177e4SLinus Torvalds printk(KERN_WARNING 281da177e4SLinus Torvalds "fsm(%s): init_fsm: Couldn't alloc instance\n", name); 291da177e4SLinus Torvalds return NULL; 301da177e4SLinus Torvalds } 311da177e4SLinus Torvalds strlcpy(this->name, name, sizeof(this->name)); 321e1815beSFrank Blaschka init_waitqueue_head(&this->wait_q); 331da177e4SLinus Torvalds 3488abaab4SEric Sesterhenn f = kzalloc(sizeof(fsm), order); 351da177e4SLinus Torvalds if (f == NULL) { 361da177e4SLinus Torvalds printk(KERN_WARNING 371da177e4SLinus Torvalds "fsm(%s): init_fsm: Couldn't alloc fsm\n", name); 381da177e4SLinus Torvalds kfree_fsm(this); 391da177e4SLinus Torvalds return NULL; 401da177e4SLinus Torvalds } 411da177e4SLinus Torvalds f->nr_events = nr_events; 421da177e4SLinus Torvalds f->nr_states = nr_states; 431da177e4SLinus Torvalds f->event_names = event_names; 441da177e4SLinus Torvalds f->state_names = state_names; 451da177e4SLinus Torvalds this->f = f; 461da177e4SLinus Torvalds 4788abaab4SEric Sesterhenn m = kcalloc(nr_states*nr_events, sizeof(fsm_function_t), order); 481da177e4SLinus Torvalds if (m == NULL) { 491da177e4SLinus Torvalds printk(KERN_WARNING 501da177e4SLinus Torvalds "fsm(%s): init_fsm: Couldn't alloc jumptable\n", name); 511da177e4SLinus Torvalds kfree_fsm(this); 521da177e4SLinus Torvalds return NULL; 531da177e4SLinus Torvalds } 541da177e4SLinus Torvalds f->jumpmatrix = m; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds for (i = 0; i < tmpl_len; i++) { 571da177e4SLinus Torvalds if ((tmpl[i].cond_state >= nr_states) || 581da177e4SLinus Torvalds (tmpl[i].cond_event >= nr_events) ) { 591da177e4SLinus Torvalds printk(KERN_ERR 601da177e4SLinus Torvalds "fsm(%s): init_fsm: Bad template l=%d st(%ld/%ld) ev(%ld/%ld)\n", 611da177e4SLinus Torvalds name, i, (long)tmpl[i].cond_state, (long)f->nr_states, 621da177e4SLinus Torvalds (long)tmpl[i].cond_event, (long)f->nr_events); 631da177e4SLinus Torvalds kfree_fsm(this); 641da177e4SLinus Torvalds return NULL; 651da177e4SLinus Torvalds } else 661da177e4SLinus Torvalds m[nr_states * tmpl[i].cond_event + tmpl[i].cond_state] = 671da177e4SLinus Torvalds tmpl[i].function; 681da177e4SLinus Torvalds } 691da177e4SLinus Torvalds return this; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds void 731da177e4SLinus Torvalds kfree_fsm(fsm_instance *this) 741da177e4SLinus Torvalds { 751da177e4SLinus Torvalds if (this) { 761da177e4SLinus Torvalds if (this->f) { 771da177e4SLinus Torvalds kfree(this->f->jumpmatrix); 781da177e4SLinus Torvalds kfree(this->f); 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds kfree(this); 811da177e4SLinus Torvalds } else 821da177e4SLinus Torvalds printk(KERN_WARNING 831da177e4SLinus Torvalds "fsm: kfree_fsm called with NULL argument\n"); 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY 871da177e4SLinus Torvalds void 881da177e4SLinus Torvalds fsm_print_history(fsm_instance *fi) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds int idx = 0; 911da177e4SLinus Torvalds int i; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds if (fi->history_size >= FSM_HISTORY_SIZE) 941da177e4SLinus Torvalds idx = fi->history_index; 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): History:\n", fi->name); 971da177e4SLinus Torvalds for (i = 0; i < fi->history_size; i++) { 981da177e4SLinus Torvalds int e = fi->history[idx].event; 991da177e4SLinus Torvalds int s = fi->history[idx++].state; 1001da177e4SLinus Torvalds idx %= FSM_HISTORY_SIZE; 1011da177e4SLinus Torvalds if (e == -1) 1021da177e4SLinus Torvalds printk(KERN_DEBUG " S=%s\n", 1031da177e4SLinus Torvalds fi->f->state_names[s]); 1041da177e4SLinus Torvalds else 1051da177e4SLinus Torvalds printk(KERN_DEBUG " S=%s E=%s\n", 1061da177e4SLinus Torvalds fi->f->state_names[s], 1071da177e4SLinus Torvalds fi->f->event_names[e]); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds fi->history_size = fi->history_index = 0; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds void 1131da177e4SLinus Torvalds fsm_record_history(fsm_instance *fi, int state, int event) 1141da177e4SLinus Torvalds { 1151da177e4SLinus Torvalds fi->history[fi->history_index].state = state; 1161da177e4SLinus Torvalds fi->history[fi->history_index++].event = event; 1171da177e4SLinus Torvalds fi->history_index %= FSM_HISTORY_SIZE; 1181da177e4SLinus Torvalds if (fi->history_size < FSM_HISTORY_SIZE) 1191da177e4SLinus Torvalds fi->history_size++; 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds #endif 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds const char * 1241da177e4SLinus Torvalds fsm_getstate_str(fsm_instance *fi) 1251da177e4SLinus Torvalds { 1261da177e4SLinus Torvalds int st = atomic_read(&fi->state); 1271da177e4SLinus Torvalds if (st >= fi->f->nr_states) 1281da177e4SLinus Torvalds return "Invalid"; 1291da177e4SLinus Torvalds return fi->f->state_names[st]; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds static void 133e99e88a9SKees Cook fsm_expire_timer(struct timer_list *t) 1341da177e4SLinus Torvalds { 135e99e88a9SKees Cook fsm_timer *this = from_timer(this, t, tl); 1361da177e4SLinus Torvalds #if FSM_TIMER_DEBUG 1371da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): Timer %p expired\n", 1381da177e4SLinus Torvalds this->fi->name, this); 1391da177e4SLinus Torvalds #endif 1401da177e4SLinus Torvalds fsm_event(this->fi, this->expire_event, this->event_arg); 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds void 1441da177e4SLinus Torvalds fsm_settimer(fsm_instance *fi, fsm_timer *this) 1451da177e4SLinus Torvalds { 1461da177e4SLinus Torvalds this->fi = fi; 1471da177e4SLinus Torvalds #if FSM_TIMER_DEBUG 1481da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): Create timer %p\n", fi->name, 1491da177e4SLinus Torvalds this); 1501da177e4SLinus Torvalds #endif 151e99e88a9SKees Cook timer_setup(&this->tl, fsm_expire_timer, 0); 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds void 1551da177e4SLinus Torvalds fsm_deltimer(fsm_timer *this) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds #if FSM_TIMER_DEBUG 1581da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): Delete timer %p\n", this->fi->name, 1591da177e4SLinus Torvalds this); 1601da177e4SLinus Torvalds #endif 1611da177e4SLinus Torvalds del_timer(&this->tl); 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds int 1651da177e4SLinus Torvalds fsm_addtimer(fsm_timer *this, int millisec, int event, void *arg) 1661da177e4SLinus Torvalds { 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds #if FSM_TIMER_DEBUG 1691da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): Add timer %p %dms\n", 1701da177e4SLinus Torvalds this->fi->name, this, millisec); 1711da177e4SLinus Torvalds #endif 1721da177e4SLinus Torvalds 173e99e88a9SKees Cook timer_setup(&this->tl, fsm_expire_timer, 0); 1741da177e4SLinus Torvalds this->expire_event = event; 1751da177e4SLinus Torvalds this->event_arg = arg; 1761da177e4SLinus Torvalds this->tl.expires = jiffies + (millisec * HZ) / 1000; 1771da177e4SLinus Torvalds add_timer(&this->tl); 1781da177e4SLinus Torvalds return 0; 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds /* FIXME: this function is never used, why */ 1821da177e4SLinus Torvalds void 1831da177e4SLinus Torvalds fsm_modtimer(fsm_timer *this, int millisec, int event, void *arg) 1841da177e4SLinus Torvalds { 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds #if FSM_TIMER_DEBUG 1871da177e4SLinus Torvalds printk(KERN_DEBUG "fsm(%s): Restart timer %p %dms\n", 1881da177e4SLinus Torvalds this->fi->name, this, millisec); 1891da177e4SLinus Torvalds #endif 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds del_timer(&this->tl); 192e99e88a9SKees Cook timer_setup(&this->tl, fsm_expire_timer, 0); 1931da177e4SLinus Torvalds this->expire_event = event; 1941da177e4SLinus Torvalds this->event_arg = arg; 1951da177e4SLinus Torvalds this->tl.expires = jiffies + (millisec * HZ) / 1000; 1961da177e4SLinus Torvalds add_timer(&this->tl); 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds EXPORT_SYMBOL(init_fsm); 2001da177e4SLinus Torvalds EXPORT_SYMBOL(kfree_fsm); 2011da177e4SLinus Torvalds EXPORT_SYMBOL(fsm_settimer); 2021da177e4SLinus Torvalds EXPORT_SYMBOL(fsm_deltimer); 2031da177e4SLinus Torvalds EXPORT_SYMBOL(fsm_addtimer); 2041da177e4SLinus Torvalds EXPORT_SYMBOL(fsm_modtimer); 2051da177e4SLinus Torvalds EXPORT_SYMBOL(fsm_getstate_str); 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds #if FSM_DEBUG_HISTORY 2081da177e4SLinus Torvalds EXPORT_SYMBOL(fsm_print_history); 2091da177e4SLinus Torvalds EXPORT_SYMBOL(fsm_record_history); 2101da177e4SLinus Torvalds #endif 211