18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * finite state machine implementation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author Karsten Keil <kkeil@novell.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Thanks to Jan den Ouden 88c2ecf20Sopenharmony_ci * Fritz Elfert 98c2ecf20Sopenharmony_ci * Copyright 2008 by Karsten Keil <kkeil@novell.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/string.h> 168c2ecf20Sopenharmony_ci#include "fsm.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define FSM_TIMER_DEBUG 0 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciint 218c2ecf20Sopenharmony_cimISDN_FsmNew(struct Fsm *fsm, 228c2ecf20Sopenharmony_ci struct FsmNode *fnlist, int fncount) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci int i; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci fsm->jumpmatrix = 278c2ecf20Sopenharmony_ci kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count, 288c2ecf20Sopenharmony_ci fsm->event_count), 298c2ecf20Sopenharmony_ci GFP_KERNEL); 308c2ecf20Sopenharmony_ci if (fsm->jumpmatrix == NULL) 318c2ecf20Sopenharmony_ci return -ENOMEM; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci for (i = 0; i < fncount; i++) 348c2ecf20Sopenharmony_ci if ((fnlist[i].state >= fsm->state_count) || 358c2ecf20Sopenharmony_ci (fnlist[i].event >= fsm->event_count)) { 368c2ecf20Sopenharmony_ci printk(KERN_ERR 378c2ecf20Sopenharmony_ci "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n", 388c2ecf20Sopenharmony_ci i, (long)fnlist[i].state, (long)fsm->state_count, 398c2ecf20Sopenharmony_ci (long)fnlist[i].event, (long)fsm->event_count); 408c2ecf20Sopenharmony_ci } else 418c2ecf20Sopenharmony_ci fsm->jumpmatrix[fsm->state_count * fnlist[i].event + 428c2ecf20Sopenharmony_ci fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmNew); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_civoid 488c2ecf20Sopenharmony_cimISDN_FsmFree(struct Fsm *fsm) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci kfree((void *) fsm->jumpmatrix); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmFree); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciint 558c2ecf20Sopenharmony_cimISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci FSMFNPTR r; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if ((fi->state >= fi->fsm->state_count) || 608c2ecf20Sopenharmony_ci (event >= fi->fsm->event_count)) { 618c2ecf20Sopenharmony_ci printk(KERN_ERR 628c2ecf20Sopenharmony_ci "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", 638c2ecf20Sopenharmony_ci (long)fi->state, (long)fi->fsm->state_count, event, 648c2ecf20Sopenharmony_ci (long)fi->fsm->event_count); 658c2ecf20Sopenharmony_ci return 1; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; 688c2ecf20Sopenharmony_ci if (r) { 698c2ecf20Sopenharmony_ci if (fi->debug) 708c2ecf20Sopenharmony_ci fi->printdebug(fi, "State %s Event %s", 718c2ecf20Sopenharmony_ci fi->fsm->strState[fi->state], 728c2ecf20Sopenharmony_ci fi->fsm->strEvent[event]); 738c2ecf20Sopenharmony_ci r(fi, event, arg); 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci } else { 768c2ecf20Sopenharmony_ci if (fi->debug) 778c2ecf20Sopenharmony_ci fi->printdebug(fi, "State %s Event %s no action", 788c2ecf20Sopenharmony_ci fi->fsm->strState[fi->state], 798c2ecf20Sopenharmony_ci fi->fsm->strEvent[event]); 808c2ecf20Sopenharmony_ci return 1; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmEvent); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_civoid 868c2ecf20Sopenharmony_cimISDN_FsmChangeState(struct FsmInst *fi, int newstate) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci fi->state = newstate; 898c2ecf20Sopenharmony_ci if (fi->debug) 908c2ecf20Sopenharmony_ci fi->printdebug(fi, "ChangeState %s", 918c2ecf20Sopenharmony_ci fi->fsm->strState[newstate]); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmChangeState); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void 968c2ecf20Sopenharmony_ciFsmExpireTimer(struct timer_list *t) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct FsmTimer *ft = from_timer(ft, t, tl); 998c2ecf20Sopenharmony_ci#if FSM_TIMER_DEBUG 1008c2ecf20Sopenharmony_ci if (ft->fi->debug) 1018c2ecf20Sopenharmony_ci ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); 1028c2ecf20Sopenharmony_ci#endif 1038c2ecf20Sopenharmony_ci mISDN_FsmEvent(ft->fi, ft->event, ft->arg); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_civoid 1078c2ecf20Sopenharmony_cimISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci ft->fi = fi; 1108c2ecf20Sopenharmony_ci#if FSM_TIMER_DEBUG 1118c2ecf20Sopenharmony_ci if (ft->fi->debug) 1128c2ecf20Sopenharmony_ci ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); 1138c2ecf20Sopenharmony_ci#endif 1148c2ecf20Sopenharmony_ci timer_setup(&ft->tl, FsmExpireTimer, 0); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmInitTimer); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_civoid 1198c2ecf20Sopenharmony_cimISDN_FsmDelTimer(struct FsmTimer *ft, int where) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci#if FSM_TIMER_DEBUG 1228c2ecf20Sopenharmony_ci if (ft->fi->debug) 1238c2ecf20Sopenharmony_ci ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", 1248c2ecf20Sopenharmony_ci (long) ft, where); 1258c2ecf20Sopenharmony_ci#endif 1268c2ecf20Sopenharmony_ci del_timer(&ft->tl); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmDelTimer); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciint 1318c2ecf20Sopenharmony_cimISDN_FsmAddTimer(struct FsmTimer *ft, 1328c2ecf20Sopenharmony_ci int millisec, int event, void *arg, int where) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#if FSM_TIMER_DEBUG 1368c2ecf20Sopenharmony_ci if (ft->fi->debug) 1378c2ecf20Sopenharmony_ci ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", 1388c2ecf20Sopenharmony_ci (long) ft, millisec, where); 1398c2ecf20Sopenharmony_ci#endif 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (timer_pending(&ft->tl)) { 1428c2ecf20Sopenharmony_ci if (ft->fi->debug) { 1438c2ecf20Sopenharmony_ci printk(KERN_WARNING 1448c2ecf20Sopenharmony_ci "mISDN_FsmAddTimer: timer already active!\n"); 1458c2ecf20Sopenharmony_ci ft->fi->printdebug(ft->fi, 1468c2ecf20Sopenharmony_ci "mISDN_FsmAddTimer already active!"); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci return -1; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci ft->event = event; 1518c2ecf20Sopenharmony_ci ft->arg = arg; 1528c2ecf20Sopenharmony_ci ft->tl.expires = jiffies + (millisec * HZ) / 1000; 1538c2ecf20Sopenharmony_ci add_timer(&ft->tl); 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmAddTimer); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_civoid 1598c2ecf20Sopenharmony_cimISDN_FsmRestartTimer(struct FsmTimer *ft, 1608c2ecf20Sopenharmony_ci int millisec, int event, void *arg, int where) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#if FSM_TIMER_DEBUG 1648c2ecf20Sopenharmony_ci if (ft->fi->debug) 1658c2ecf20Sopenharmony_ci ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", 1668c2ecf20Sopenharmony_ci (long) ft, millisec, where); 1678c2ecf20Sopenharmony_ci#endif 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (timer_pending(&ft->tl)) 1708c2ecf20Sopenharmony_ci del_timer(&ft->tl); 1718c2ecf20Sopenharmony_ci ft->event = event; 1728c2ecf20Sopenharmony_ci ft->arg = arg; 1738c2ecf20Sopenharmony_ci ft->tl.expires = jiffies + (millisec * HZ) / 1000; 1748c2ecf20Sopenharmony_ci add_timer(&ft->tl); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmRestartTimer); 177