xref: /kernel/linux/linux-6.6/drivers/isdn/mISDN/fsm.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * finite state machine implementation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author       Karsten Keil <kkeil@novell.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Thanks to    Jan den Ouden
862306a36Sopenharmony_ci *              Fritz Elfert
962306a36Sopenharmony_ci * Copyright 2008  by Karsten Keil <kkeil@novell.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include "fsm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define FSM_TIMER_DEBUG 0
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint
2162306a36Sopenharmony_cimISDN_FsmNew(struct Fsm *fsm,
2262306a36Sopenharmony_ci	     struct FsmNode *fnlist, int fncount)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	int i;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	fsm->jumpmatrix =
2762306a36Sopenharmony_ci		kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count,
2862306a36Sopenharmony_ci				    fsm->event_count),
2962306a36Sopenharmony_ci			GFP_KERNEL);
3062306a36Sopenharmony_ci	if (fsm->jumpmatrix == NULL)
3162306a36Sopenharmony_ci		return -ENOMEM;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	for (i = 0; i < fncount; i++)
3462306a36Sopenharmony_ci		if ((fnlist[i].state >= fsm->state_count) ||
3562306a36Sopenharmony_ci		    (fnlist[i].event >= fsm->event_count)) {
3662306a36Sopenharmony_ci			printk(KERN_ERR
3762306a36Sopenharmony_ci			       "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
3862306a36Sopenharmony_ci			       i, (long)fnlist[i].state, (long)fsm->state_count,
3962306a36Sopenharmony_ci			       (long)fnlist[i].event, (long)fsm->event_count);
4062306a36Sopenharmony_ci		} else
4162306a36Sopenharmony_ci			fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
4262306a36Sopenharmony_ci					fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmNew);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_civoid
4862306a36Sopenharmony_cimISDN_FsmFree(struct Fsm *fsm)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	kfree((void *) fsm->jumpmatrix);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmFree);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciint
5562306a36Sopenharmony_cimISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	FSMFNPTR r;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if ((fi->state >= fi->fsm->state_count) ||
6062306a36Sopenharmony_ci	    (event >= fi->fsm->event_count)) {
6162306a36Sopenharmony_ci		printk(KERN_ERR
6262306a36Sopenharmony_ci		       "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
6362306a36Sopenharmony_ci		       (long)fi->state, (long)fi->fsm->state_count, event,
6462306a36Sopenharmony_ci		       (long)fi->fsm->event_count);
6562306a36Sopenharmony_ci		return 1;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci	r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
6862306a36Sopenharmony_ci	if (r) {
6962306a36Sopenharmony_ci		if (fi->debug)
7062306a36Sopenharmony_ci			fi->printdebug(fi, "State %s Event %s",
7162306a36Sopenharmony_ci				       fi->fsm->strState[fi->state],
7262306a36Sopenharmony_ci				       fi->fsm->strEvent[event]);
7362306a36Sopenharmony_ci		r(fi, event, arg);
7462306a36Sopenharmony_ci		return 0;
7562306a36Sopenharmony_ci	} else {
7662306a36Sopenharmony_ci		if (fi->debug)
7762306a36Sopenharmony_ci			fi->printdebug(fi, "State %s Event %s no action",
7862306a36Sopenharmony_ci				       fi->fsm->strState[fi->state],
7962306a36Sopenharmony_ci				       fi->fsm->strEvent[event]);
8062306a36Sopenharmony_ci		return 1;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmEvent);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_civoid
8662306a36Sopenharmony_cimISDN_FsmChangeState(struct FsmInst *fi, int newstate)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	fi->state = newstate;
8962306a36Sopenharmony_ci	if (fi->debug)
9062306a36Sopenharmony_ci		fi->printdebug(fi, "ChangeState %s",
9162306a36Sopenharmony_ci			       fi->fsm->strState[newstate]);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmChangeState);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void
9662306a36Sopenharmony_ciFsmExpireTimer(struct timer_list *t)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct FsmTimer *ft = from_timer(ft, t, tl);
9962306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
10062306a36Sopenharmony_ci	if (ft->fi->debug)
10162306a36Sopenharmony_ci		ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
10262306a36Sopenharmony_ci#endif
10362306a36Sopenharmony_ci	mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_civoid
10762306a36Sopenharmony_cimISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	ft->fi = fi;
11062306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
11162306a36Sopenharmony_ci	if (ft->fi->debug)
11262306a36Sopenharmony_ci		ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
11362306a36Sopenharmony_ci#endif
11462306a36Sopenharmony_ci	timer_setup(&ft->tl, FsmExpireTimer, 0);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmInitTimer);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_civoid
11962306a36Sopenharmony_cimISDN_FsmDelTimer(struct FsmTimer *ft, int where)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
12262306a36Sopenharmony_ci	if (ft->fi->debug)
12362306a36Sopenharmony_ci		ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
12462306a36Sopenharmony_ci				   (long) ft, where);
12562306a36Sopenharmony_ci#endif
12662306a36Sopenharmony_ci	del_timer(&ft->tl);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmDelTimer);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciint
13162306a36Sopenharmony_cimISDN_FsmAddTimer(struct FsmTimer *ft,
13262306a36Sopenharmony_ci		  int millisec, int event, void *arg, int where)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
13662306a36Sopenharmony_ci	if (ft->fi->debug)
13762306a36Sopenharmony_ci		ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d",
13862306a36Sopenharmony_ci				   (long) ft, millisec, where);
13962306a36Sopenharmony_ci#endif
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (timer_pending(&ft->tl)) {
14262306a36Sopenharmony_ci		if (ft->fi->debug) {
14362306a36Sopenharmony_ci			printk(KERN_WARNING
14462306a36Sopenharmony_ci			       "mISDN_FsmAddTimer: timer already active!\n");
14562306a36Sopenharmony_ci			ft->fi->printdebug(ft->fi,
14662306a36Sopenharmony_ci					   "mISDN_FsmAddTimer already active!");
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci		return -1;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	ft->event = event;
15162306a36Sopenharmony_ci	ft->arg = arg;
15262306a36Sopenharmony_ci	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
15362306a36Sopenharmony_ci	add_timer(&ft->tl);
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmAddTimer);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_civoid
15962306a36Sopenharmony_cimISDN_FsmRestartTimer(struct FsmTimer *ft,
16062306a36Sopenharmony_ci		      int millisec, int event, void *arg, int where)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci#if FSM_TIMER_DEBUG
16462306a36Sopenharmony_ci	if (ft->fi->debug)
16562306a36Sopenharmony_ci		ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d",
16662306a36Sopenharmony_ci				   (long) ft, millisec, where);
16762306a36Sopenharmony_ci#endif
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (timer_pending(&ft->tl))
17062306a36Sopenharmony_ci		del_timer(&ft->tl);
17162306a36Sopenharmony_ci	ft->event = event;
17262306a36Sopenharmony_ci	ft->arg = arg;
17362306a36Sopenharmony_ci	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
17462306a36Sopenharmony_ci	add_timer(&ft->tl);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ciEXPORT_SYMBOL(mISDN_FsmRestartTimer);
177