xref: /kernel/linux/linux-5.10/drivers/isdn/mISDN/fsm.c (revision 8c2ecf20)
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