Commit f31b5ddd authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN: Make the state machine explicit

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.
parent 4e339dac
...@@ -12,6 +12,7 @@ obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o ...@@ -12,6 +12,7 @@ obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
# Multipart objects. # Multipart objects.
isdn-objs := isdn_net.o isdn_net_lib.o \ isdn-objs := isdn_net.o isdn_net_lib.o \
isdn_fsm.o \
isdn_ciscohdlck.o \ isdn_ciscohdlck.o \
isdn_tty.o isdn_v110.o \ isdn_tty.o isdn_v110.o \
isdn_common.o \ isdn_common.o \
......
/* $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
/* $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
...@@ -163,25 +163,6 @@ isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason) ...@@ -163,25 +163,6 @@ isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
dst_link_failure(skb); 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 static void
isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev) 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) ...@@ -510,7 +491,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
/* check acceptable call types for DOV */ /* check acceptable call types for DOV */
dbg_net_icall("n_fi: if='%s', l.msn=%s, l.flags=%#x, l.dstate=%d\n", 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); my_eaz = isdn_slot_map_eaz2msn(slot, mlp->msn);
if (si1 == 1) { /* it's a DOV call, check if we allow it */ if (si1 == 1) { /* it's a DOV call, check if we allow it */
...@@ -768,4 +749,7 @@ isdn_net_init(void) ...@@ -768,4 +749,7 @@ isdn_net_init(void)
#ifdef CONFIG_ISDN_PPP #ifdef CONFIG_ISDN_PPP
register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP, &isdn_ppp_ops); register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP, &isdn_ppp_ops);
#endif #endif
isdn_net_lib_init();
} }
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
extern void isdn_net_init(void); extern void isdn_net_init(void);
extern void isdn_net_exit(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 void isdn_net_hangup_all(void);
extern int isdn_net_ioctl(struct inode *, struct file *, uint, ulong); extern int isdn_net_ioctl(struct inode *, struct file *, uint, ulong);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "isdn_common.h" #include "isdn_common.h"
#include "isdn_net.h" #include "isdn_net.h"
#include "isdn_ppp.h" #include "isdn_ppp.h"
#include "isdn_fsm.h"
#define ISDN_NET_TX_TIMEOUT (20*HZ) #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 */ ...@@ -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_tasklet(unsigned long data);
static void isdn_net_dial_timer(unsigned long data); static void isdn_net_dial_timer(unsigned long data);
static int isdn_init_netif(struct net_device *ndev); 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 */ /* Registration of ISDN network interface types */
...@@ -248,6 +304,12 @@ isdn_net_addif(char *name, isdn_net_local *mlp) ...@@ -248,6 +304,12 @@ isdn_net_addif(char *name, isdn_net_local *mlp)
idev->dial_timer.data = (unsigned long) idev; idev->dial_timer.data = (unsigned long) idev;
idev->dial_timer.function = isdn_net_dial_timer; 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) { if (!mlp) {
/* Device shall be a master */ /* Device shall be a master */
mlp = kmalloc(sizeof(*mlp), GFP_KERNEL); mlp = kmalloc(sizeof(*mlp), GFP_KERNEL);
...@@ -902,8 +964,10 @@ isdn_net_exit(void) ...@@ -902,8 +964,10 @@ isdn_net_exit(void)
if (retval) if (retval)
isdn_BUG(); isdn_BUG();
} }
up(&sem); up(&sem);
// FIXME
isdn_net_lib_exit();
} }
/* ====================================================================== */ /* ====================================================================== */
...@@ -1019,34 +1083,13 @@ isdn_net_tasklet(unsigned long data) ...@@ -1019,34 +1083,13 @@ isdn_net_tasklet(unsigned long data)
/* call control state machine */ /* call control state machine */
/* ====================================================================== */ /* ====================================================================== */
enum { static void dialout_first(struct fsm_inst *fi, int pr, void *arg);
ST_NULL, static void dialout_next(struct fsm_inst *fi, int pr, void *arg);
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);
// FIXME
int isdn_net_online(isdn_net_dev *idev) int isdn_net_online(isdn_net_dev *idev)
{ {
return idev->dialstate == ST_ACTIVE; return idev->fi.state == ST_ACTIVE;
} }
static void static void
...@@ -1105,7 +1148,7 @@ isdn_net_unbind_channel(isdn_net_dev *idev) ...@@ -1105,7 +1148,7 @@ isdn_net_unbind_channel(isdn_net_dev *idev)
skb_queue_purge(&idev->super_tx_queue); 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_set_idev(idev->isdn_slot, NULL);
isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET); isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET);
...@@ -1139,7 +1182,7 @@ isdn_net_dial(isdn_net_dev *idev) ...@@ -1139,7 +1182,7 @@ isdn_net_dial(isdn_net_dev *idev)
goto err; goto err;
/* Initiate dialing */ /* Initiate dialing */
init_dialout(idev); dialout_first(&idev->fi, 0, NULL); // FIXME
return 0; return 0;
...@@ -1169,9 +1212,9 @@ isdn_net_accept(isdn_net_dev *idev, int slot, char *nr) ...@@ -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); isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL3, &cmd);
idev->dial_timer.expires = jiffies + mlp->dialtimeout; 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); add_timer(&idev->dial_timer);
idev->dialstate = ST_IN_WAIT_DCONN; fsm_change_state(&idev->fi, ST_IN_WAIT_DCONN);
} }
int int
...@@ -1204,9 +1247,9 @@ isdn_net_do_callback(isdn_net_dev *idev) ...@@ -1204,9 +1247,9 @@ isdn_net_do_callback(isdn_net_dev *idev)
/* Setup dialstate. */ /* Setup dialstate. */
idev->dial_timer.expires = jiffies + mlp->cbdelay; 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); 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 */ /* Initiate dialing by returning 2 or 4 */
return (mlp->flags & ISDN_NET_CBHUP) ? 2 : 4; return (mlp->flags & ISDN_NET_CBHUP) ? 2 : 4;
...@@ -1237,27 +1280,32 @@ get_outgoing_phone(isdn_net_dev *idev) ...@@ -1237,27 +1280,32 @@ get_outgoing_phone(isdn_net_dev *idev)
/* Initiate dialout. */ /* Initiate dialout. */
static int static void
init_dialout(isdn_net_dev *idev) dialout_first(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
return 1; isdn_net_unbind_channel(idev);
return;
if (list_empty(&mlp->phone[1])) }
return 1; if (list_empty(&mlp->phone[1])) {
isdn_net_unbind_channel(idev);
return;
}
idev->dial = 0; idev->dial = 0;
idev->dialretry = 0; idev->dialretry = 0;
return do_dialout(idev); dialout_next(fi, pr, arg);
} }
/* Try dialing the next number. */ /* Try dialing the next number. */
static int static void
do_dialout(isdn_net_dev *idev) dialout_next(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
struct dial_info dial = { struct dial_info dial = {
.l2_proto = mlp->l2_proto, .l2_proto = mlp->l2_proto,
...@@ -1280,29 +1328,29 @@ do_dialout(isdn_net_dev *idev) ...@@ -1280,29 +1328,29 @@ do_dialout(isdn_net_dev *idev)
/* For outgoing callback, use cbdelay instead of dialtimeout */ /* For outgoing callback, use cbdelay instead of dialtimeout */
if (mlp->cbdelay && (mlp->flags & ISDN_NET_CBOUT)) { if (mlp->cbdelay && (mlp->flags & ISDN_NET_CBOUT)) {
idev->dial_timer.expires = jiffies + mlp->cbdelay; idev->dial_timer.expires = jiffies + mlp->cbdelay;
idev->dial_event = EV_NET_TIMER_CB_OUT; idev->dial_event = EV_TIMER_CB_OUT;
} else { } else {
idev->dial_timer.expires = jiffies + mlp->dialtimeout; 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); add_timer(&idev->dial_timer);
/* Dial */ /* Dial */
isdn_slot_dial(idev->isdn_slot, &dial); isdn_slot_dial(idev->isdn_slot, &dial);
return 1;
} }
/* If we didn't connect within dialtimeout, we give up for now /* If we didn't connect within dialtimeout, we give up for now
* and wait for dialwait jiffies before trying again. * and wait for dialwait jiffies before trying again.
*/ */
static int static void
dial_timeout(isdn_net_dev *idev) dial_timeout(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
isdn_ctrl cmd; 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); isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
/* get next phone number */ /* get next phone number */
...@@ -1313,53 +1361,54 @@ dial_timeout(isdn_net_dev *idev) ...@@ -1313,53 +1361,54 @@ dial_timeout(isdn_net_dev *idev)
} }
if (idev->dialretry >= mlp->dialmax) { if (idev->dialretry >= mlp->dialmax) {
isdn_net_hangup(idev); 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); mod_timer(&idev->dial_timer, jiffies + mlp->dialwait);
return 1;
} }
static int static void
isdn_net_connect_failure(isdn_net_dev *idev) connect_fail(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
del_timer(&idev->dial_timer); del_timer(&idev->dial_timer);
isdn_slot_all_eaz(idev->isdn_slot); isdn_slot_all_eaz(idev->isdn_slot);
printk(KERN_INFO "%s: connection failed\n", idev->name); printk(KERN_INFO "%s: connection failed\n", idev->name);
isdn_net_unbind_channel(idev); isdn_net_unbind_channel(idev);
return 1;
} }
static int static void
isdn_net_out_dconn(isdn_net_dev *idev) out_dconn(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_ctrl cmd; 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); isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
return 1;
} }
static int static void
isdn_net_in_dconn(isdn_net_dev *idev) in_dconn(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_ctrl cmd; 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); isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
return 1;
} }
static int static void
isdn_net_bconn(isdn_net_dev *idev) bconn(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
idev->dialstate = ST_ACTIVE; fsm_change_state(&idev->fi, ST_ACTIVE);
if (mlp->onhtime) { if (mlp->onhtime) {
idev->huptimer = 0; idev->huptimer = 0;
idev->dial_event = EV_NET_TIMER_HUP; idev->dial_event = EV_TIMER_HUP;
mod_timer(&idev->dial_timer, jiffies + HZ); mod_timer(&idev->dial_timer, jiffies + HZ);
} else { } else {
del_timer(&idev->dial_timer); del_timer(&idev->dial_timer);
...@@ -1381,15 +1430,14 @@ isdn_net_bconn(isdn_net_dev *idev) ...@@ -1381,15 +1430,14 @@ isdn_net_bconn(isdn_net_dev *idev)
mlp->ops->connected(idev); mlp->ops->connected(idev);
else else
isdn_net_dev_wake_queue(idev); isdn_net_dev_wake_queue(idev);
return 1;
} }
/* Check if it's time for idle hang-up */ /* Check if it's time for idle hang-up */
static int static void
isdn_net_check_hup(isdn_net_dev *idev) check_hup(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
dbg_net_dial("%s: huptimer %d onhtime %d chargetime %ld chargeint %d\n", 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) ...@@ -1404,19 +1452,21 @@ isdn_net_check_hup(isdn_net_dev *idev)
+ idev->chargeint - 2 * HZ)) + idev->chargeint - 2 * HZ))
goto mod_timer; goto mod_timer;
} }
if (idev->outgoing || mlp->hupflags & ISDN_INHUP) if (idev->outgoing || mlp->hupflags & ISDN_INHUP) {
return isdn_net_hangup(idev); isdn_net_hangup(idev);
return;
}
mod_timer: mod_timer:
mod_timer(&idev->dial_timer, idev->dial_timer.expires + HZ); mod_timer(&idev->dial_timer, idev->dial_timer.expires + HZ);
return 1;
} }
/* Charge-info from TelCo. */ /* Charge-info from TelCo. */
static int static void
isdn_net_cinf(isdn_net_dev *idev) got_cinf(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
idev->charge++; idev->charge++;
switch (idev->charge_state) { switch (idev->charge_state) {
case ST_CHARGE_NULL: case ST_CHARGE_NULL:
...@@ -1431,12 +1481,12 @@ isdn_net_cinf(isdn_net_dev *idev) ...@@ -1431,12 +1481,12 @@ isdn_net_cinf(isdn_net_dev *idev)
} }
idev->chargetime = jiffies; idev->chargetime = jiffies;
dbg_net_dial("%s: got CINF\n", idev->name); dbg_net_dial("%s: got CINF\n", idev->name);
return 1;
} }
static int static void
isdn_net_disconnected(isdn_net_dev *idev) disconnected(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
del_timer(&idev->dial_timer); del_timer(&idev->dial_timer);
...@@ -1450,8 +1500,6 @@ isdn_net_disconnected(isdn_net_dev *idev) ...@@ -1450,8 +1500,6 @@ isdn_net_disconnected(isdn_net_dev *idev)
idev->charge); idev->charge);
isdn_slot_all_eaz(idev->isdn_slot); isdn_slot_all_eaz(idev->isdn_slot);
isdn_net_unbind_channel(idev); isdn_net_unbind_channel(idev);
return 1;
} }
/* Perform hangup for a net-interface. */ /* Perform hangup for a net-interface. */
...@@ -1471,136 +1519,122 @@ isdn_net_hangup(isdn_net_dev *idev) ...@@ -1471,136 +1519,122 @@ isdn_net_hangup(isdn_net_dev *idev)
return 1; return 1;
} }
/*
static int * Handle status-messages from ISDN-interfacecard.
isdn_net_event_out_wait_dconn(isdn_net_dev *idev, int pr, void *arg) * 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) { isdn_net_dev *idev = isdn_slot_idev(idx);
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;
}
static int if (!idev) {
isdn_net_event_out_wait_bconn(isdn_net_dev *idev, int pr, void *arg) HERE;
{ return 0;
switch (pr) { }
case EV_NET_TIMER_DIAL: switch (c->command) {
return dial_timeout(idev); case ISDN_STAT_DCONN:
return fsm_event(&idev->fi, EV_STAT_DCONN, c);
case ISDN_STAT_BCONN: 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: case ISDN_STAT_DHUP:
return isdn_net_connect_failure(idev); return fsm_event(&idev->fi, EV_STAT_DHUP, c);
} case ISDN_STAT_CINF:
isdn_BUG(); 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; return 0;
}
} }
static int int
isdn_net_event_in_wait_dconn(isdn_net_dev *idev, int pr, void *arg) isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
{ {
switch (pr) { fsm_event(&idev->fi, pr, arg);
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;
} }
static int static void
isdn_net_event_in_wait_bconn(isdn_net_dev *idev, int pr, void *arg) hang_up(struct fsm_inst *fi, int pr, void *arg)
{ {
switch (pr) { isdn_net_dev *idev = fi->userdata;
case EV_NET_TIMER_INCOMING:
return isdn_net_hangup(idev); 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;
} }
static int static void
isdn_net_event_wait_before_cb(isdn_net_dev *idev, int pr, void *arg) got_bsent(struct fsm_inst *fi, int pr, void *arg)
{ {
switch (pr) { isdn_net_dev *idev = fi->userdata;
case EV_NET_TIMER_CB_IN: isdn_ctrl *c = arg;
return init_dialout(idev);
} isdn_net_bsent(idev, c);
isdn_BUG();
return 0;
} }
static int static struct fsm_node isdn_net_fn_tbl[] = {
isdn_net_event_active(isdn_net_dev *idev, int pr, void *arg) { 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) { va_list args;
case EV_NET_TIMER_HUP: isdn_net_dev *idev = fi->userdata;
return isdn_net_check_hup(idev); char buf[128];
case ISDN_STAT_BSENT: char *p = buf;
return isdn_net_bsent(idev, arg);
case ISDN_STAT_BHUP: va_start(args, fmt);
case ISDN_STAT_DHUP: p += sprintf(p, "%s: ", idev->name);
return isdn_net_disconnected(idev); p += vsprintf(p, fmt, args);
case ISDN_STAT_CINF: va_end(args);
return isdn_net_cinf(idev); printk(KERN_DEBUG "%s\n", buf);
}
isdn_BUG();
return 0;
} }
static int void
isdn_net_event_dial_wait(isdn_net_dev *idev, int pr, void *arg) isdn_net_lib_init(void)
{ {
switch (pr) { fsm_new(&isdn_net_fsm);
case EV_NET_TIMER_DIAL_WAIT:
return do_dialout(idev);
}
isdn_BUG();
return 0;
} }
/* void
* For ISDN_STAT_*, return 1 if event was for us isdn_net_lib_exit(void)
*/
int
isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
{ {
dbg_net_dial("%s: dialstate=%d pr=%#x\n", idev->name, fsm_free(&isdn_net_fsm);
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;
}
} }
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
// FIXME!!!
#include <../drivers/isdn/i4l/isdn_fsm.h>
#ifdef CONFIG_COBALT_MICRO_SERVER #ifdef CONFIG_COBALT_MICRO_SERVER
/* Save memory */ /* Save memory */
#define ISDN_MAX_DRIVERS 2 #define ISDN_MAX_DRIVERS 2
...@@ -367,8 +370,8 @@ typedef struct isdn_net_dev_s { ...@@ -367,8 +370,8 @@ typedef struct isdn_net_dev_s {
int exclusive; /* -1 if non excl./idx to excl chan */ int exclusive; /* -1 if non excl./idx to excl chan */
struct timer_list dial_timer; /* dial events timer */ 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 dial_event; /* event in case of timer expiry */
int dialstate; /* State for dialing */
int dial; /* # of phone number just dialed */ int dial; /* # of phone number just dialed */
int outgoing; /* Flag: outgoing call */ int outgoing; /* Flag: outgoing call */
int dialretry; /* Counter for Dialout-retries */ int dialretry; /* Counter for Dialout-retries */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment