199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller/*
299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * finite state machine implementation
399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller *
43e9ebd3cf48d9181e0931c6fd8f1a7dc4d6cb27cHans J. Koch * Author       Karsten Keil <kkeil@novell.com>
50909c1ec6f016b3f580fa2f4630659a5874a8ef8Marc Kleine-Budde *
699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * Thanks to    Jan den Ouden
799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller *              Fritz Elfert
899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * Copyright 2008  by Karsten Keil <kkeil@novell.com>
999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller *
1099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * This program is free software; you can redistribute it and/or modify
1199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * it under the terms of the GNU General Public License version 2 as
1299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * published by the Free Software Foundation.
1399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller *
1499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * This program is distributed in the hope that it will be useful,
1599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * but WITHOUT ANY WARRANTY; without even the implied warranty of
1699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller * GNU General Public License for more details.
1899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller *
1999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller */
2099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
2199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#include <linux/kernel.h>
2299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#include <linux/slab.h>
2399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#include <linux/module.h>
2499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#include <linux/string.h>
2599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#include "fsm.h"
2699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
2799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#define FSM_TIMER_DEBUG 0
2899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
2999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millervoid
3099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillermISDN_FsmNew(struct Fsm *fsm,
3199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	     struct FsmNode *fnlist, int fncount)
3299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
3399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	int i;
3499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
3599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count *
3699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				  fsm->event_count, GFP_KERNEL);
3799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
3899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	for (i = 0; i < fncount; i++)
3999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		if ((fnlist[i].state >= fsm->state_count) ||
4099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		    (fnlist[i].event >= fsm->event_count)) {
4199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			printk(KERN_ERR
4299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			       "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
439e0a2d1ca3de6e284e99ad5cae1ae33ecb74c479Marc Kleine-Budde			       i, (long)fnlist[i].state, (long)fsm->state_count,
4499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			       (long)fnlist[i].event, (long)fsm->event_count);
4599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		} else
4699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
4799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller					fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
4899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
499e0a2d1ca3de6e284e99ad5cae1ae33ecb74c479Marc Kleine-BuddeEXPORT_SYMBOL(mISDN_FsmNew);
5099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
5199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millervoid
529e0a2d1ca3de6e284e99ad5cae1ae33ecb74c479Marc Kleine-BuddemISDN_FsmFree(struct Fsm *fsm)
5399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
5499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	kfree((void *) fsm->jumpmatrix);
5599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
5699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerEXPORT_SYMBOL(mISDN_FsmFree);
5799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
580909c1ec6f016b3f580fa2f4630659a5874a8ef8Marc Kleine-Buddeint
590909c1ec6f016b3f580fa2f4630659a5874a8ef8Marc Kleine-BuddemISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
6099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
6199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	FSMFNPTR r;
6299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
6399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if ((fi->state >= fi->fsm->state_count) ||
6499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	    (event >= fi->fsm->event_count)) {
6599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		printk(KERN_ERR
6699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		       "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
6799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		       (long)fi->state, (long)fi->fsm->state_count, event,
6899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		       (long)fi->fsm->event_count);
6999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		return 1;
7099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	}
7199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
7299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (r) {
7399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		if (fi->debug)
7499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			fi->printdebug(fi, "State %s Event %s",
7599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				       fi->fsm->strState[fi->state],
7699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				       fi->fsm->strEvent[event]);
7799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		r(fi, event, arg);
7899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		return 0;
7999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	} else {
8099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		if (fi->debug)
8199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			fi->printdebug(fi, "State %s Event %s no action",
8299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				       fi->fsm->strState[fi->state],
8399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				       fi->fsm->strEvent[event]);
8499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		return 1;
8599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	}
8699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
8799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerEXPORT_SYMBOL(mISDN_FsmEvent);
8899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
8999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millervoid
9099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillermISDN_FsmChangeState(struct FsmInst *fi, int newstate)
9199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
9299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	fi->state = newstate;
9399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (fi->debug)
9499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		fi->printdebug(fi, "ChangeState %s",
9599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			       fi->fsm->strState[newstate]);
9699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
9799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerEXPORT_SYMBOL(mISDN_FsmChangeState);
9899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
9999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millerstatic void
10099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerFsmExpireTimer(struct FsmTimer *ft)
10199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
10299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#if FSM_TIMER_DEBUG
10399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (ft->fi->debug)
10499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
10599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#endif
10699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
10799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
10899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
10999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millervoid
11099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillermISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
11199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
11299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->fi = fi;
11399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->tl.function = (void *) FsmExpireTimer;
11499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->tl.data = (long) ft;
11599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#if FSM_TIMER_DEBUG
11699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (ft->fi->debug)
11799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
11899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#endif
11999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	init_timer(&ft->tl);
12099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
12199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerEXPORT_SYMBOL(mISDN_FsmInitTimer);
12299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
12399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millervoid
12499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillermISDN_FsmDelTimer(struct FsmTimer *ft, int where)
12599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
12699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#if FSM_TIMER_DEBUG
12799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (ft->fi->debug)
12899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
12999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				   (long) ft, where);
13099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#endif
13199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	del_timer(&ft->tl);
13299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
13399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerEXPORT_SYMBOL(mISDN_FsmDelTimer);
13499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
13599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millerint
13699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillermISDN_FsmAddTimer(struct FsmTimer *ft,
13799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		  int millisec, int event, void *arg, int where)
13899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
13999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
14099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#if FSM_TIMER_DEBUG
14199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (ft->fi->debug)
14299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d",
14399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				   (long) ft, millisec, where);
14499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#endif
14599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
14699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (timer_pending(&ft->tl)) {
14799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		if (ft->fi->debug) {
14899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			printk(KERN_WARNING
14999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			       "mISDN_FsmAddTimer: timer already active!\n");
15099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller			ft->fi->printdebug(ft->fi,
15199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller					   "mISDN_FsmAddTimer already active!");
15299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		}
15399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		return -1;
15499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	}
15599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	init_timer(&ft->tl);
15699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->event = event;
15799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->arg = arg;
15899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
15999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	add_timer(&ft->tl);
16099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	return 0;
16199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
16299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerEXPORT_SYMBOL(mISDN_FsmAddTimer);
16399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
16499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Millervoid
16599c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillermISDN_FsmRestartTimer(struct FsmTimer *ft,
16699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		      int millisec, int event, void *arg, int where)
16799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller{
16899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
16999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#if FSM_TIMER_DEBUG
17099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	if (ft->fi->debug)
17199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d",
17299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller				   (long) ft, millisec, where);
17399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller#endif
17499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller
17500389b0820c44cceec3f6981cb4d217982ec73b2Marc Kleine-Budde	if (timer_pending(&ft->tl))
17699c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller		del_timer(&ft->tl);
17799c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	init_timer(&ft->tl);
17899c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->event = event;
17999c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->arg = arg;
18099c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
18199c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller	add_timer(&ft->tl);
18299c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller}
18399c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. MillerEXPORT_SYMBOL(mISDN_FsmRestartTimer);
18499c4a6344f6574c97019ac16e8d54bfe5ad21f2dDavid S. Miller