]> git.neil.brown.name Git - history.git/commitdiff
ISDN: Make the state machine explicit
authorKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
Sun, 6 Oct 2002 14:39:54 +0000 (09:39 -0500)
committerKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
Sun, 6 Oct 2002 14:39:54 +0000 (09:39 -0500)
Add a finite state machine helper module, which is basically copied over
from the hisax driver with a little bit of beautification.

Eventually, all ISDN should be converted to using these routines.

drivers/isdn/i4l/Makefile
drivers/isdn/i4l/isdn_fsm.c [new file with mode: 0644]
drivers/isdn/i4l/isdn_fsm.h [new file with mode: 0644]
drivers/isdn/i4l/isdn_net.c
drivers/isdn/i4l/isdn_net.h
drivers/isdn/i4l/isdn_net_lib.c
include/linux/isdn.h

index c98022e5becbaf3029082663e6f5db5eed93a8c8..407501c0d6b19a299ce4d59218ea6b3b7191cfda 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_ISDN_PPP_BSDCOMP)                += isdn_bsdcomp.o
 # Multipart objects.
 
 isdn-objs                              := isdn_net.o isdn_net_lib.o \
+                                          isdn_fsm.o \
                                           isdn_ciscohdlck.o \
                                           isdn_tty.o isdn_v110.o \
                                           isdn_common.o \
diff --git a/drivers/isdn/i4l/isdn_fsm.c b/drivers/isdn/i4l/isdn_fsm.c
new file mode 100644 (file)
index 0000000..53fe9ca
--- /dev/null
@@ -0,0 +1,169 @@
+/* $Id: fsm.c,v 1.14.6.4 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include "isdn_fsm.h"
+
+int
+fsm_new(struct fsm *fsm)
+{
+       int i;
+       int size = sizeof(fsm_fn) * fsm->st_cnt * fsm->ev_cnt;
+
+       fsm->jumpmatrix = kmalloc(size, GFP_KERNEL);
+       if (!fsm->jumpmatrix)
+               return -ENOMEM;
+
+       memset(fsm->jumpmatrix, 0, size);
+
+       for (i = 0; i < fsm->fn_cnt; i++) {
+               if (fsm->fn_tbl[i].st >= fsm->st_cnt || 
+                   fsm->fn_tbl[i].ev >= fsm->ev_cnt) {
+                       printk(KERN_ERR "FsmNew Error line %d st(%d/%d) ev(%d/%d)\n", i,
+                              fsm->fn_tbl[i].st, fsm->st_cnt, 
+                              fsm->fn_tbl[i].ev, fsm->ev_cnt);
+                       continue;
+               }
+               fsm->jumpmatrix[fsm->st_cnt * fsm->fn_tbl[i].ev + fsm->fn_tbl[i].st] = fsm->fn_tbl[i].routine;
+       }
+       return 0;
+}
+
+void
+fsm_free(struct fsm *fsm)
+{
+       kfree(fsm->jumpmatrix);
+}
+
+int
+fsm_event(struct fsm_inst *fi, int event, void *arg)
+{
+       fsm_fn fn;
+
+       if (fi->state >= fi->fsm->st_cnt || 
+           event >= fi->fsm->ev_cnt) {
+               printk(KERN_ERR "FsmEvent Error st(%d/%d) ev(%d/%d)\n",
+                      fi->state, fi->fsm->st_cnt,event, 
+                      fi->fsm->ev_cnt);
+               return -EINVAL;
+       }
+       fn = fi->fsm->jumpmatrix[fi->fsm->st_cnt * event + fi->state];
+       if (!fn) {
+               if (fi->debug)
+                       fi->printdebug(fi, "State %s Event %s no routine",
+                                      fi->fsm->st_str[fi->state],
+                                      fi->fsm->ev_str[event]);
+               return -ESRCH;
+       }
+       if (fi->debug)
+               fi->printdebug(fi, "State %s Event %s",
+                              fi->fsm->st_str[fi->state],
+                              fi->fsm->ev_str[event]);
+
+       fn(fi, event, arg);
+       return 0;
+}
+
+void
+fsm_change_state(struct fsm_inst *fi, int newstate)
+{
+       fi->state = newstate;
+       if (fi->debug)
+               fi->printdebug(fi, "ChangeState %s",
+                              fi->fsm->st_str[newstate]);
+}
+
+#if 0
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
+#endif
+       FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+       ft->fi = fi;
+       ft->tl.function = (void *) FsmExpireTimer;
+       ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
+#endif
+       init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
+#endif
+       del_timer(&ft->tl);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+           int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
+                       (long) ft, millisec, where);
+#endif
+
+       if (timer_pending(&ft->tl)) {
+               printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+               ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
+               return -1;
+       }
+       init_timer(&ft->tl);
+       ft->event = event;
+       ft->arg = arg;
+       ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+       add_timer(&ft->tl);
+       return 0;
+}
+
+void
+FsmRestartTimer(struct FsmTimer *ft,
+           int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
+                       (long) ft, millisec, where);
+#endif
+
+       if (timer_pending(&ft->tl))
+               del_timer(&ft->tl);
+       init_timer(&ft->tl);
+       ft->event = event;
+       ft->arg = arg;
+       ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+       add_timer(&ft->tl);
+}
+#endif
diff --git a/drivers/isdn/i4l/isdn_fsm.h b/drivers/isdn/i4l/isdn_fsm.h
new file mode 100644 (file)
index 0000000..75c7303
--- /dev/null
@@ -0,0 +1,60 @@
+/* $Id: fsm.h,v 1.3.2.2 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __FSM_H__
+#define __FSM_H__
+
+#include <linux/timer.h>
+
+struct fsm_inst;
+
+typedef void (*fsm_fn)(struct fsm_inst *, int, void *);
+
+struct fsm {
+       fsm_fn *jumpmatrix;
+       int st_cnt, ev_cnt, fn_cnt;
+       char **st_str, **ev_str;
+       struct fsm_node *fn_tbl;
+};
+
+struct fsm_inst {
+       struct fsm *fsm;
+       int state;
+       int debug;
+       void *userdata;
+       int userint;
+       void (*printdebug) (struct fsm_inst *, char *, ...);
+};
+
+struct fsm_node {
+       int st, ev;
+       void (*routine) (struct fsm_inst *, int, void *);
+};
+
+struct fsm_timer {
+       struct fsm_inst *fi;
+       struct timer_list tl;
+       int ev;
+       void *arg;
+};
+
+int  fsm_new(struct fsm *fsm);
+void fsm_free(struct fsm *fsm);
+int  fsm_event(struct fsm_inst *fi, int event, void *arg);
+void fsm_change_state(struct fsm_inst *fi, int newstate);
+void fsm_init_timer(struct fsm_inst *fi, struct fsm_timer *ft);
+int  fsm_add_timer(struct fsm_timer *ft, int timeout, int event);
+void fsm_mod_timer(struct fsm_timer *ft, int timeout, int event);
+void fsm_del_timer(struct fsm_timer *ft);
+
+#endif
index 7c6fa3fd36ede09f73405d13bcb42072452acae0..72a67a6643336377ac18db7fe8b3971167136a59 100644 (file)
@@ -163,25 +163,6 @@ isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
        dst_link_failure(skb);
 }
 
-/*
- * Handle status-messages from ISDN-interfacecard.
- * This function is called from within the main-status-dispatcher
- * isdn_status_callback, which itself is called from the low-level driver.
- * Return: 1 = Event handled, 0 = not for us or unknown Event.
- */
-int
-isdn_net_stat_callback(int idx, isdn_ctrl *c)
-{
-       isdn_net_dev *idev = isdn_slot_idev(idx);
-
-       if (!idev) {
-               HERE;
-               return 0;
-       }
-
-       return isdn_net_handle_event(idev, c->command, c);
-}
-
 static void
 isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev)
 {
@@ -510,7 +491,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
 
                 /* check acceptable call types for DOV */
                dbg_net_icall("n_fi: if='%s', l.msn=%s, l.flags=%#x, l.dstate=%d\n",
-                             idev->name, mlp->msn, mlp->flags, idev->dialstate);
+                             idev->name, mlp->msn, mlp->flags, idev->fi.state);
 
                 my_eaz = isdn_slot_map_eaz2msn(slot, mlp->msn);
                 if (si1 == 1) { /* it's a DOV call, check if we allow it */
@@ -768,4 +749,7 @@ isdn_net_init(void)
 #ifdef CONFIG_ISDN_PPP
        register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP,    &isdn_ppp_ops);
 #endif
+
+       isdn_net_lib_init();
 }
+
index e1598e22b74c981d686f26fc277361b09318a22c..cf7917a8922a7092663c8929c94217d3767ec5e0 100644 (file)
@@ -34,6 +34,8 @@
 
 extern void isdn_net_init(void);
 extern void isdn_net_exit(void);
+extern void isdn_net_lib_init(void);
+extern void isdn_net_lib_exit(void);
 extern void isdn_net_hangup_all(void);
 extern int isdn_net_ioctl(struct inode *, struct file *, uint, ulong);
 
index 702d54aa0f10b04f83e9909a0a8dfd22c741cb91..7761e2a00497f34ef61415faf40c3e0e11819064 100644 (file)
@@ -19,6 +19,7 @@
 #include "isdn_common.h"
 #include "isdn_net.h"
 #include "isdn_ppp.h"
+#include "isdn_fsm.h"
 
 #define ISDN_NET_TX_TIMEOUT (20*HZ) 
 
@@ -32,6 +33,61 @@ int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg); /* FIXME */
 static void isdn_net_tasklet(unsigned long data);
 static void isdn_net_dial_timer(unsigned long data);
 static int isdn_init_netif(struct net_device *ndev);
+static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...);
+
+static struct fsm isdn_net_fsm;
+
+enum {
+       ST_NULL,
+       ST_OUT_WAIT_DCONN,
+       ST_OUT_WAIT_BCONN,
+       ST_IN_WAIT_DCONN,
+       ST_IN_WAIT_BCONN,
+       ST_ACTIVE,
+       ST_WAIT_BEFORE_CB,
+       ST_OUT_DIAL_WAIT,
+};
+
+static char *isdn_net_st_str[] = {
+       "ST_NULL",
+       "ST_OUT_WAIT_DCONN",
+       "ST_OUT_WAIT_BCONN",
+       "ST_IN_WAIT_DCONN",
+       "ST_IN_WAIT_BCONN",
+       "ST_ACTIVE",
+       "ST_WAIT_BEFORE_CB",
+       "ST_OUT_DIAL_WAIT",
+};
+
+enum {
+       EV_TIMER_INCOMING,
+       EV_TIMER_DIAL,
+       EV_TIMER_DIAL_WAIT,
+       EV_TIMER_CB_OUT,
+       EV_TIMER_CB_IN,
+       EV_TIMER_HUP,
+       EV_STAT_DCONN,
+       EV_STAT_BCONN,
+       EV_STAT_DHUP,
+       EV_STAT_BHUP,
+       EV_STAT_CINF,
+       EV_STAT_BSENT,
+};
+
+static char *isdn_net_ev_str[] = {
+       "EV_NET_TIMER_INCOMING",
+       "EV_NET_TIMER_DIAL",
+       "EV_NET_TIMER_DIAL_WAIT",
+       "EV_NET_TIMER_CB_OUT",
+       "EV_NET_TIMER_CB_IN",
+       "EV_NET_TIMER_HUP",
+       "EV_STAT_DCONN",
+       "EV_STAT_BCONN",
+       "EV_STAT_DHUP",
+       "EV_STAT_BHUP",
+       "EV_STAT_CINF",
+       "EV_STAT_BSENT",
+};
 
 /* ====================================================================== */
 /* Registration of ISDN network interface types                           */
@@ -248,6 +304,12 @@ isdn_net_addif(char *name, isdn_net_local *mlp)
        idev->dial_timer.data = (unsigned long) idev;
        idev->dial_timer.function = isdn_net_dial_timer;
 
+       idev->fi.fsm = &isdn_net_fsm;
+       idev->fi.state = ST_NULL;
+       idev->fi.debug = 1;
+       idev->fi.userdata = idev;
+       idev->fi.printdebug = isdn_net_dev_debug;
+
        if (!mlp) {
                /* Device shall be a master */
                mlp = kmalloc(sizeof(*mlp), GFP_KERNEL);
@@ -902,8 +964,10 @@ isdn_net_exit(void)
                if (retval)
                        isdn_BUG();
        }
-
        up(&sem);
+
+       // FIXME
+       isdn_net_lib_exit();
 }
 
 /* ====================================================================== */
@@ -1019,34 +1083,13 @@ isdn_net_tasklet(unsigned long data)
 /* call control state machine                                             */
 /* ====================================================================== */
 
-enum {
-       ST_NULL,
-       ST_OUT_WAIT_DCONN,
-       ST_OUT_WAIT_BCONN,
-       ST_IN_WAIT_DCONN,
-       ST_IN_WAIT_BCONN,
-       ST_ACTIVE,
-       ST_WAIT_BEFORE_CB,
-       ST_OUT_DIAL_WAIT,
-};
-
-/* keep clear of ISDN_CMD_* and ISDN_STAT_* */
-enum {
-       EV_NET_DIAL            = 0x200,
-       EV_NET_TIMER_INCOMING  = 0x201,
-       EV_NET_TIMER_DIAL      = 0x203,
-       EV_NET_TIMER_DIAL_WAIT = 0x204,
-       EV_NET_TIMER_CB_OUT    = 0x205,
-       EV_NET_TIMER_CB_IN     = 0x206,
-       EV_NET_TIMER_HUP       = 0x207,
-};
-
-static int init_dialout(isdn_net_dev *idev);
-static int do_dialout(isdn_net_dev *idev);
+static void dialout_first(struct fsm_inst *fi, int pr, void *arg);
+static void dialout_next(struct fsm_inst *fi, int pr, void *arg);
 
+// FIXME
 int isdn_net_online(isdn_net_dev *idev)
 {
-       return idev->dialstate == ST_ACTIVE;
+       return idev->fi.state == ST_ACTIVE;
 }
 
 static void
@@ -1105,7 +1148,7 @@ isdn_net_unbind_channel(isdn_net_dev *idev)
 
        skb_queue_purge(&idev->super_tx_queue);
 
-       idev->dialstate = ST_NULL;
+       fsm_change_state(&idev->fi, ST_NULL);
 
        isdn_slot_set_idev(idev->isdn_slot, NULL);
        isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET);
@@ -1139,7 +1182,7 @@ isdn_net_dial(isdn_net_dev *idev)
                goto err;
 
        /* Initiate dialing */
-       init_dialout(idev);
+       dialout_first(&idev->fi, 0, NULL); // FIXME
 
        return 0;
 
@@ -1169,9 +1212,9 @@ isdn_net_accept(isdn_net_dev *idev, int slot, char *nr)
        isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL3, &cmd);
        
        idev->dial_timer.expires = jiffies + mlp->dialtimeout;
-       idev->dial_event = EV_NET_TIMER_INCOMING;
+       idev->dial_event = EV_TIMER_INCOMING;
        add_timer(&idev->dial_timer);
-       idev->dialstate = ST_IN_WAIT_DCONN;
+       fsm_change_state(&idev->fi, ST_IN_WAIT_DCONN);
 }
 
 int
@@ -1204,9 +1247,9 @@ isdn_net_do_callback(isdn_net_dev *idev)
 
        /* Setup dialstate. */
        idev->dial_timer.expires = jiffies + mlp->cbdelay;
-       idev->dial_event = EV_NET_TIMER_CB_IN;
+       idev->dial_event = EV_TIMER_CB_IN;
        add_timer(&idev->dial_timer);
-       idev->dialstate = ST_WAIT_BEFORE_CB;
+       fsm_change_state(&idev->fi, ST_WAIT_BEFORE_CB);
 
        /* Initiate dialing by returning 2 or 4 */
        return (mlp->flags & ISDN_NET_CBHUP) ? 2 : 4;
@@ -1237,27 +1280,32 @@ get_outgoing_phone(isdn_net_dev *idev)
 
 /* Initiate dialout. */
 
-static int
-init_dialout(isdn_net_dev *idev)
+static void
+dialout_first(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_net_local *mlp = idev->mlp;
 
-       if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF)
-               return 1;
-       
-       if (list_empty(&mlp->phone[1]))
-               return 1;
+       if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
+               isdn_net_unbind_channel(idev);
+               return;
+       }
+       if (list_empty(&mlp->phone[1])) {
+               isdn_net_unbind_channel(idev);
+               return;
+       }
 
        idev->dial = 0;
        idev->dialretry = 0;
-       return do_dialout(idev);
+       dialout_next(fi, pr, arg);
 }
 
 /* Try dialing the next number. */
 
-static int
-do_dialout(isdn_net_dev *idev)
+static void
+dialout_next(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_net_local *mlp = idev->mlp;
        struct dial_info dial = {
                .l2_proto = mlp->l2_proto,
@@ -1280,29 +1328,29 @@ do_dialout(isdn_net_dev *idev)
        /* For outgoing callback, use cbdelay instead of dialtimeout */
        if (mlp->cbdelay && (mlp->flags & ISDN_NET_CBOUT)) {
                idev->dial_timer.expires = jiffies + mlp->cbdelay;
-               idev->dial_event = EV_NET_TIMER_CB_OUT;
+               idev->dial_event = EV_TIMER_CB_OUT;
        } else {
                idev->dial_timer.expires = jiffies + mlp->dialtimeout;
-               idev->dial_event = EV_NET_TIMER_DIAL;
+               idev->dial_event = EV_TIMER_DIAL;
        }
-       idev->dialstate = ST_OUT_WAIT_DCONN;
+       fsm_change_state(&idev->fi, ST_OUT_WAIT_DCONN);
        add_timer(&idev->dial_timer);
 
        /* Dial */
        isdn_slot_dial(idev->isdn_slot, &dial);
-       return 1;
 }
 
 /* If we didn't connect within dialtimeout, we give up for now
  * and wait for dialwait jiffies before trying again.
  */
-static int
-dial_timeout(isdn_net_dev *idev)
+static void
+dial_timeout(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_net_local *mlp = idev->mlp;
        isdn_ctrl cmd;
 
-       idev->dialstate = ST_OUT_DIAL_WAIT;
+       fsm_change_state(&idev->fi, ST_OUT_DIAL_WAIT);
        isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
        
        /* get next phone number */
@@ -1313,53 +1361,54 @@ dial_timeout(isdn_net_dev *idev)
        }
        if (idev->dialretry >= mlp->dialmax) {
                isdn_net_hangup(idev);
-               return 1;
+               return;
        }
-       idev->dial_event = EV_NET_TIMER_DIAL_WAIT;
+       idev->dial_event = EV_TIMER_DIAL_WAIT;
        mod_timer(&idev->dial_timer, jiffies + mlp->dialwait);
-       return 1;
 }
 
-static int
-isdn_net_connect_failure(isdn_net_dev *idev)
+static void
+connect_fail(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
+
        del_timer(&idev->dial_timer);
        isdn_slot_all_eaz(idev->isdn_slot);
        printk(KERN_INFO "%s: connection failed\n", idev->name);
        isdn_net_unbind_channel(idev);
-       return 1;
 }
 
-static int
-isdn_net_out_dconn(isdn_net_dev *idev)
+static void
+out_dconn(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_ctrl cmd;
 
-       idev->dialstate = ST_OUT_WAIT_BCONN;
+       fsm_change_state(&idev->fi, ST_OUT_WAIT_BCONN);
        isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
-       return 1;
 }
 
-static int
-isdn_net_in_dconn(isdn_net_dev *idev)
+static void
+in_dconn(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_ctrl cmd;
 
-       idev->dialstate = ST_IN_WAIT_BCONN;
+       fsm_change_state(&idev->fi, ST_IN_WAIT_BCONN);
        isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
-       return 1;
 }
 
-static int
-isdn_net_bconn(isdn_net_dev *idev)
+static void
+bconn(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_net_local *mlp = idev->mlp;
 
-       idev->dialstate = ST_ACTIVE;
+       fsm_change_state(&idev->fi, ST_ACTIVE);
 
        if (mlp->onhtime) {
                idev->huptimer = 0;
-               idev->dial_event = EV_NET_TIMER_HUP;
+               idev->dial_event = EV_TIMER_HUP;
                mod_timer(&idev->dial_timer, jiffies + HZ);
        } else {
                del_timer(&idev->dial_timer);
@@ -1381,15 +1430,14 @@ isdn_net_bconn(isdn_net_dev *idev)
                mlp->ops->connected(idev);
        else
                isdn_net_dev_wake_queue(idev);
-
-       return 1;
 }
 
 /* Check if it's time for idle hang-up */
 
-static int
-isdn_net_check_hup(isdn_net_dev *idev)
+static void
+check_hup(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_net_local *mlp = idev->mlp;
 
        dbg_net_dial("%s: huptimer %d onhtime %d chargetime %ld chargeint %d\n",
@@ -1404,19 +1452,21 @@ isdn_net_check_hup(isdn_net_dev *idev)
                                + idev->chargeint - 2 * HZ))
                        goto mod_timer;
        }
-       if (idev->outgoing || mlp->hupflags & ISDN_INHUP)
-               return isdn_net_hangup(idev);
-
+       if (idev->outgoing || mlp->hupflags & ISDN_INHUP) {
+               isdn_net_hangup(idev);
+               return;
+       }
  mod_timer:
        mod_timer(&idev->dial_timer, idev->dial_timer.expires + HZ);
-       return 1;
 }
 
 /* Charge-info from TelCo. */
 
-static int
-isdn_net_cinf(isdn_net_dev *idev)
+static void
+got_cinf(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
+
        idev->charge++;
        switch (idev->charge_state) {
        case ST_CHARGE_NULL:
@@ -1431,12 +1481,12 @@ isdn_net_cinf(isdn_net_dev *idev)
        }
        idev->chargetime = jiffies;
        dbg_net_dial("%s: got CINF\n", idev->name);
-       return 1;
 }
 
-static int
-isdn_net_disconnected(isdn_net_dev *idev)
+static void
+disconnected(struct fsm_inst *fi, int pr, void *arg)
 {
+       isdn_net_dev *idev = fi->userdata;
        isdn_net_local *mlp = idev->mlp;
 
        del_timer(&idev->dial_timer);
@@ -1450,8 +1500,6 @@ isdn_net_disconnected(isdn_net_dev *idev)
               idev->charge);
        isdn_slot_all_eaz(idev->isdn_slot);
        isdn_net_unbind_channel(idev);
-
-       return 1;
 }
 
 /* Perform hangup for a net-interface. */
@@ -1471,136 +1519,122 @@ isdn_net_hangup(isdn_net_dev *idev)
        return 1;
 }
 
-
-static int
-isdn_net_event_out_wait_dconn(isdn_net_dev *idev, int pr, void *arg)
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the low-level driver.
+ * Return: 1 = event handled, 0 = not handled
+ */
+int
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
 {
-       switch (pr) {
-       case EV_NET_TIMER_DIAL:
-               return dial_timeout(idev);
-       case EV_NET_TIMER_CB_OUT:
-               return isdn_net_hangup(idev);
-       case ISDN_STAT_DCONN:
-               return isdn_net_out_dconn(idev);
-       case ISDN_STAT_DHUP:
-               return isdn_net_connect_failure(idev);
-       }
-       isdn_BUG();
-       return 0;
-}
+       isdn_net_dev *idev = isdn_slot_idev(idx);
 
-static int
-isdn_net_event_out_wait_bconn(isdn_net_dev *idev, int pr, void *arg)
-{
-       switch (pr) {
-       case EV_NET_TIMER_DIAL:
-               return dial_timeout(idev);
+       if (!idev) {
+               HERE;
+               return 0;
+       }
+       switch (c->command) {
+       case ISDN_STAT_DCONN:
+               return fsm_event(&idev->fi, EV_STAT_DCONN, c);
        case ISDN_STAT_BCONN:
-               return isdn_net_bconn(idev);
+               return fsm_event(&idev->fi, EV_STAT_BCONN, c);
+       case ISDN_STAT_BHUP:
+               return fsm_event(&idev->fi, EV_STAT_BHUP, c);
        case ISDN_STAT_DHUP:
-               return isdn_net_connect_failure(idev);
+               return fsm_event(&idev->fi, EV_STAT_DHUP, c);
+       case ISDN_STAT_CINF:
+               return fsm_event(&idev->fi, EV_STAT_CINF, c);
+       case ISDN_STAT_BSENT:
+               return fsm_event(&idev->fi, EV_STAT_BSENT, c);
+       default:
+               printk("unknown stat %d\n", c->command);
+               return 0;
        }
-       isdn_BUG();
-       return 0;
 }
 
-static int
-isdn_net_event_in_wait_dconn(isdn_net_dev *idev, int pr, void *arg)
+int
+isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
 {
-       switch (pr) {
-       case EV_NET_TIMER_INCOMING:
-               return isdn_net_hangup(idev);
-       case ISDN_STAT_DCONN:
-               return isdn_net_in_dconn(idev);
-       case ISDN_STAT_DHUP:
-               return isdn_net_connect_failure(idev);
-       }
-       isdn_BUG();
-       return 0;
+       fsm_event(&idev->fi, pr, arg);
 }
 
-static int
-isdn_net_event_in_wait_bconn(isdn_net_dev *idev, int pr, void *arg)
+static void
+hang_up(struct fsm_inst *fi, int pr, void *arg)
 {
-       switch (pr) {
-       case EV_NET_TIMER_INCOMING:
-               return isdn_net_hangup(idev);
-       case ISDN_STAT_BCONN:
-               return isdn_net_bconn(idev);
-       case ISDN_STAT_DHUP:
-               return isdn_net_connect_failure(idev);
-       }
-       isdn_BUG();
-       return 0;
+       isdn_net_dev *idev = fi->userdata;
+
+       isdn_net_hangup(idev);
 }
 
-static int
-isdn_net_event_wait_before_cb(isdn_net_dev *idev, int pr, void *arg)
+static void
+got_bsent(struct fsm_inst *fi, int pr, void *arg)
 {
-       switch (pr) {
-       case EV_NET_TIMER_CB_IN:
-               return init_dialout(idev);
-       }
-       isdn_BUG();
-       return 0;
+       isdn_net_dev *idev = fi->userdata;
+       isdn_ctrl *c = arg;
+       
+       isdn_net_bsent(idev, c);
 }
 
-static int
-isdn_net_event_active(isdn_net_dev *idev, int pr, void *arg)
+static struct fsm_node isdn_net_fn_tbl[] = {
+       { ST_OUT_WAIT_DCONN, EV_TIMER_DIAL,      dial_timeout  },
+       { ST_OUT_WAIT_DCONN, EV_STAT_DCONN,      out_dconn     },
+       { ST_OUT_WAIT_DCONN, EV_STAT_DHUP,       connect_fail  },
+       { ST_OUT_WAIT_DCONN, EV_TIMER_CB_OUT,    hang_up       },
+
+       { ST_OUT_WAIT_BCONN, EV_TIMER_DIAL,      dial_timeout  },
+       { ST_OUT_WAIT_BCONN, EV_STAT_BCONN,      bconn         },
+       { ST_OUT_WAIT_BCONN, EV_STAT_DHUP,       connect_fail  },
+
+       { ST_IN_WAIT_DCONN,  EV_TIMER_INCOMING,  hang_up       },
+       { ST_IN_WAIT_DCONN,  EV_STAT_DCONN,      in_dconn      },
+       { ST_IN_WAIT_DCONN,  EV_STAT_DHUP,       connect_fail  },
+
+       { ST_IN_WAIT_BCONN,  EV_TIMER_INCOMING,  hang_up       },
+       { ST_IN_WAIT_BCONN,  EV_STAT_BCONN,      bconn         },
+       { ST_IN_WAIT_BCONN,  EV_STAT_DHUP,       connect_fail  },
+
+       { ST_ACTIVE,         EV_TIMER_HUP,       check_hup     },
+       { ST_ACTIVE,         EV_STAT_BHUP,       disconnected  },
+       { ST_ACTIVE,         EV_STAT_CINF,       got_cinf      },
+       { ST_ACTIVE,         EV_STAT_BSENT,      got_bsent     },
+
+       { ST_WAIT_BEFORE_CB, EV_TIMER_CB_IN,     dialout_first },
+
+       { ST_OUT_DIAL_WAIT,  EV_TIMER_DIAL_WAIT, dialout_next  },
+};
+
+static struct fsm isdn_net_fsm = {
+       .st_cnt = ARRAY_SIZE(isdn_net_st_str),
+       .st_str = isdn_net_st_str,
+       .ev_cnt = ARRAY_SIZE(isdn_net_ev_str),
+       .ev_str = isdn_net_ev_str,
+       .fn_cnt = ARRAY_SIZE(isdn_net_fn_tbl),
+       .fn_tbl = isdn_net_fn_tbl,
+};
+
+static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...)
 {
-       switch (pr) {
-       case EV_NET_TIMER_HUP:
-               return isdn_net_check_hup(idev);
-       case ISDN_STAT_BSENT:
-               return isdn_net_bsent(idev, arg);
-       case ISDN_STAT_BHUP:
-       case ISDN_STAT_DHUP:
-               return isdn_net_disconnected(idev);
-       case ISDN_STAT_CINF:
-               return isdn_net_cinf(idev);
-       }
-       isdn_BUG();
-       return 0;
+       va_list args;
+       isdn_net_dev *idev = fi->userdata;
+       char buf[128];
+       char *p = buf;
+
+       va_start(args, fmt);
+       p += sprintf(p, "%s: ", idev->name);
+       p += vsprintf(p, fmt, args);
+       va_end(args);
+       printk(KERN_DEBUG "%s\n", buf);
 }
 
-static int
-isdn_net_event_dial_wait(isdn_net_dev *idev, int pr, void *arg)
+void
+isdn_net_lib_init(void)
 {
-       switch (pr) {
-       case EV_NET_TIMER_DIAL_WAIT:
-               return do_dialout(idev);
-       }
-       isdn_BUG();
-       return 0;
+       fsm_new(&isdn_net_fsm);
 }
 
-/*
- * For ISDN_STAT_*, return 1 if event was for us 
- */
-int
-isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
+void
+isdn_net_lib_exit(void)
 {
-       dbg_net_dial("%s: dialstate=%d pr=%#x\n", idev->name, 
-                    idev->dialstate, pr);
-
-       switch (idev->dialstate) {
-       case ST_ACTIVE:
-               return isdn_net_event_active(idev, pr, arg);
-       case ST_OUT_WAIT_DCONN:
-               return isdn_net_event_out_wait_dconn(idev, pr, arg);
-       case ST_OUT_WAIT_BCONN:
-               return isdn_net_event_out_wait_bconn(idev, pr, arg);
-       case ST_IN_WAIT_DCONN:
-               return isdn_net_event_in_wait_dconn(idev, pr, arg);
-       case ST_IN_WAIT_BCONN:
-               return isdn_net_event_in_wait_bconn(idev, pr, arg);
-       case ST_WAIT_BEFORE_CB:
-               return isdn_net_event_wait_before_cb(idev, pr, arg);
-       case ST_OUT_DIAL_WAIT:
-               return isdn_net_event_dial_wait(idev, pr, arg);
-       default:
-               isdn_BUG();
-               return 0;
-       }
+       fsm_free(&isdn_net_fsm);
 }
-
index d35b0961de09865a7f9747efbc6bb938e86cf5f2..5ae4ef0d7ddc6409c83294b1208868c87db0fcd2 100644 (file)
@@ -16,6 +16,9 @@
 
 #include <linux/ioctl.h>
 
+// FIXME!!!
+#include <../drivers/isdn/i4l/isdn_fsm.h>
+
 #ifdef CONFIG_COBALT_MICRO_SERVER
 /* Save memory */
 #define ISDN_MAX_DRIVERS    2
@@ -367,8 +370,8 @@ typedef struct isdn_net_dev_s {
   int                    exclusive;    /* -1 if non excl./idx to excl chan */
 
   struct timer_list      dial_timer;   /* dial events timer                */
+  struct fsm_inst        fi;           /* call control state machine       */
   int                    dial_event;   /* event in case of timer expiry    */
-  int                    dialstate;    /* State for dialing                */
   int                    dial;         /* # of phone number just dialed    */
   int                    outgoing;     /* Flag: outgoing call              */
   int                    dialretry;    /* Counter for Dialout-retries      */