Commit 3b1022c5 authored by Linus Torvalds's avatar Linus Torvalds

Merge http://linux-isdn.bkbits.net/linux-2.5.isdn

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 798c67c3 c2ee3a2d
...@@ -503,11 +503,14 @@ isdn_status_callback(isdn_ctrl * c) ...@@ -503,11 +503,14 @@ isdn_status_callback(isdn_ctrl * c)
retval = 2; retval = 2;
} }
break; break;
case 1: case 1: /* incoming call accepted by net interface */
list_for_each(l, &isdn_net_devs) { list_for_each(l, &isdn_net_devs) {
isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list); isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
if (p->isdn_slot == i) { if (p->isdn_slot == i) {
strcpy(cmd.parm.setup.eazmsn, p->mlp->msn); strcpy(cmd.parm.setup.eazmsn, p->mlp->msn);
isdn_slot_set_usage(i, (isdn_slot_usage(i) & ISDN_USAGE_EXCLUSIVE) | ISDN_USAGE_NET);
strcpy(isdn_slot_num(i), c->parm.setup.phone);
isdn_slot_command(i, ISDN_CMD_ACCEPTD, &cmd); isdn_slot_command(i, ISDN_CMD_ACCEPTD, &cmd);
retval = 1; retval = 1;
break; break;
...@@ -1535,26 +1538,20 @@ isdn_get_free_slot(int usage, int l2_proto, int l3_proto, ...@@ -1535,26 +1538,20 @@ isdn_get_free_slot(int usage, int l2_proto, int l3_proto,
if (USG_NONE(slot[i].usage) && if (USG_NONE(slot[i].usage) &&
(slot[i].di != -1)) { (slot[i].di != -1)) {
int d = slot[i].di; int d = slot[i].di;
if ((slot[i].usage & ISDN_USAGE_EXCLUSIVE) &&
((pre_dev != d) || (pre_chan != slot[i].ch)))
continue;
if (!strcmp(isdn_map_eaz2msn(msn, d), "-")) if (!strcmp(isdn_map_eaz2msn(msn, d), "-"))
continue; continue;
if (slot[i].usage & ISDN_USAGE_DISABLED) if (slot[i].usage & ISDN_USAGE_DISABLED)
continue; /* usage not allowed */ continue; /* usage not allowed */
if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { if (!dev->drv[d]->flags & DRV_FLAG_RUNNING)
continue;
if (((dev->drv[d]->interface->features & features) == features) || if (((dev->drv[d]->interface->features & features) == features) ||
(((dev->drv[d]->interface->features & vfeatures) == vfeatures) && (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
(dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) { (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
if ((pre_dev < 0) || (pre_chan < 0)) { if (pre_dev < 0 || pre_chan < 0 ||
isdn_slot_set_usage(i, (isdn_slot_usage(i) & ISDN_USAGE_EXCLUSIVE) | usage); (pre_dev == d && pre_chan == slot[i].ch)) {
isdn_slot_set_usage(i, usage);
restore_flags(flags); restore_flags(flags);
return i; return i;
} else if ((pre_dev == d) && (pre_chan == slot[i].ch)) {
isdn_slot_set_usage(i, (isdn_slot_usage(i) & ISDN_USAGE_EXCLUSIVE) | usage);
restore_flags(flags);
return i;
}
} }
} }
} }
...@@ -1571,17 +1568,16 @@ isdn_free_channel(int di, int ch, int usage) ...@@ -1571,17 +1568,16 @@ isdn_free_channel(int di, int ch, int usage)
int sl; int sl;
sl = isdn_dc2minor(di, ch); sl = isdn_dc2minor(di, ch);
isdn_slot_free(sl, usage); isdn_slot_free(sl);
} }
void void
isdn_slot_free(int sl, int usage) isdn_slot_free(int sl)
{ {
unsigned long flags; unsigned long flags;
save_flags(flags); save_flags(flags);
cli(); cli();
if (!usage || (slot[sl].usage & ISDN_USAGE_MASK) == usage) {
strcpy(isdn_slot_num(sl), "???"); strcpy(isdn_slot_num(sl), "???");
slot[sl].ibytes = 0; slot[sl].ibytes = 0;
slot[sl].obytes = 0; slot[sl].obytes = 0;
...@@ -1591,30 +1587,8 @@ isdn_slot_free(int sl, int usage) ...@@ -1591,30 +1587,8 @@ isdn_slot_free(int sl, int usage)
isdn_v110_close(slot[sl].iv110.v110); isdn_v110_close(slot[sl].iv110.v110);
slot[sl].iv110.v110 = NULL; slot[sl].iv110.v110 = NULL;
// 20.10.99 JIM, try to reinitialize v110 ! // 20.10.99 JIM, try to reinitialize v110 !
isdn_slot_set_usage(sl, isdn_slot_usage(sl) & (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE)); isdn_slot_set_usage(sl, ISDN_USAGE_NONE);
skb_queue_purge(&dev->drv[isdn_slot_driver(sl)]->rpqueue[isdn_slot_channel(sl)]); skb_queue_purge(&dev->drv[isdn_slot_driver(sl)]->rpqueue[isdn_slot_channel(sl)]);
}
restore_flags(flags);
}
/*
* Cancel Exclusive-Flag for ISDN-channel
*/
void
isdn_unexclusive_channel(int di, int ch)
{
int i;
ulong flags;
save_flags(flags);
cli();
for (i = 0; i < ISDN_MAX_CHANNELS; i++)
if ((slot[i].di == di) &&
(slot[i].ch == ch)) {
isdn_slot_set_usage(i, isdn_slot_usage(i) & ~ISDN_USAGE_EXCLUSIVE);
restore_flags(flags);
return;
}
restore_flags(flags); restore_flags(flags);
} }
...@@ -1959,6 +1933,7 @@ isdn_slot_command(int sl, int cmd, isdn_ctrl *ctrl) ...@@ -1959,6 +1933,7 @@ isdn_slot_command(int sl, int cmd, isdn_ctrl *ctrl)
case ISDN_CMD_DIAL: case ISDN_CMD_DIAL:
if (dev->global_flags & ISDN_GLOBAL_STOPPED) if (dev->global_flags & ISDN_GLOBAL_STOPPED)
return -EBUSY; return -EBUSY;
/* fall through */ /* fall through */
default: default:
ctrl->arg = isdn_slot_channel(sl); ctrl->arg = isdn_slot_channel(sl);
......
...@@ -64,7 +64,6 @@ extern int isdn_dc2minor(int di, int ch); ...@@ -64,7 +64,6 @@ extern int isdn_dc2minor(int di, int ch);
extern void isdn_info_update(void); extern void isdn_info_update(void);
extern char *isdn_map_eaz2msn(char *msn, int di); extern char *isdn_map_eaz2msn(char *msn, int di);
extern void isdn_timer_ctrl(int tf, int onoff); extern void isdn_timer_ctrl(int tf, int onoff);
extern void isdn_unexclusive_channel(int di, int ch);
extern int isdn_getnum(char **); extern int isdn_getnum(char **);
extern int isdn_msncmp( const char *, const char *); extern int isdn_msncmp( const char *, const char *);
extern int isdn_add_channels(driver *, int, int, int); extern int isdn_add_channels(driver *, int, int, int);
...@@ -85,7 +84,7 @@ struct dial_info { ...@@ -85,7 +84,7 @@ struct dial_info {
}; };
extern int isdn_get_free_slot(int, int, int, int, int, char *); extern int isdn_get_free_slot(int, int, int, int, int, char *);
extern void isdn_slot_free(int slot, int usage); extern void isdn_slot_free(int slot);
extern void isdn_slot_all_eaz(int slot); extern void isdn_slot_all_eaz(int slot);
extern int isdn_slot_command(int slot, int cmd, isdn_ctrl *); extern int isdn_slot_command(int slot, int cmd, isdn_ctrl *);
extern int isdn_slot_dial(int slot, struct dial_info *dial); extern int isdn_slot_dial(int slot, struct dial_info *dial);
......
...@@ -21,8 +21,6 @@ ...@@ -21,8 +21,6 @@
#include "isdn_concap.h" #include "isdn_concap.h"
#include <linux/if_arp.h> #include <linux/if_arp.h>
#ifdef CONFIG_ISDN_X25
/* The following set of device service operations are for encapsulation /* The following set of device service operations are for encapsulation
protocols that require for reliable datalink semantics. That means: protocols that require for reliable datalink semantics. That means:
...@@ -255,5 +253,3 @@ struct isdn_netif_ops isdn_x25_ops = { ...@@ -255,5 +253,3 @@ struct isdn_netif_ops isdn_x25_ops = {
.open = isdn_x25_open, .open = isdn_x25_open,
.close = isdn_x25_close, .close = isdn_x25_close,
}; };
#endif /* CONFIG_ISDN_X25 */
...@@ -423,163 +423,6 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb) ...@@ -423,163 +423,6 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb)
return 0; return 0;
} }
/*
* An incoming call-request has arrived.
* Search the interface-chain for an appropriate interface.
* If found, connect the interface to the ISDN-channel and initiate
* D- and B-Channel-setup. If secure-flag is set, accept only
* configured phone-numbers. If callback-flag is set, initiate
* callback-dialing.
*
* Return-Value: 0 = No appropriate interface for this call.
* 1 = Call accepted
* 2 = Reject call, wait cbdelay, then call back
* 3 = Reject call
* 4 = Wait cbdelay, then call back
* 5 = No appropriate interface for this call,
* would eventually match if CID was longer.
*/
int
isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
{
char *eaz;
unsigned char si1, si2;
int match_more = 0;
int retval;
struct list_head *l;
struct isdn_net_phone *n;
ulong flags;
char nr[32];
char *my_eaz;
int slot = isdn_dc2minor(di, ch);
/* Search name in netdev-chain */
save_flags(flags);
cli();
if (!setup->phone[0]) {
nr[0] = '0';
nr[1] = '\0';
printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
} else {
strcpy(nr, setup->phone);
}
si1 = setup->si1;
si2 = setup->si2;
if (!setup->eazmsn[0]) {
printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
eaz = "0";
} else {
eaz = setup->eazmsn;
}
if (dev->net_verbose > 1)
printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
/* Accept DATA and VOICE calls at this stage
local eaz is checked later for allowed call types */
if ((si1 != 7) && (si1 != 1)) {
restore_flags(flags);
if (dev->net_verbose > 1)
printk(KERN_INFO "isdn_net: Service-Indicator not 1 or 7, ignored\n");
return 0;
}
n = NULL;
dbg_net_icall("n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
isdn_slot_usage(idx));
list_for_each(l, &isdn_net_devs) {
isdn_net_dev *idev = list_entry(l, isdn_net_dev, global_list);
isdn_net_local *mlp = idev->mlp;
/* 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->fi.state);
my_eaz = isdn_slot_map_eaz2msn(slot, mlp->msn);
if (si1 == 1) { /* it's a DOV call, check if we allow it */
if (*my_eaz == 'v' || *my_eaz == 'V' ||
*my_eaz == 'b' || *my_eaz == 'B')
my_eaz++; /* skip to allow a match */
else
continue; /* force non match */
} else { /* it's a DATA call, check if we allow it */
if (*my_eaz == 'b' || *my_eaz == 'B')
my_eaz++; /* skip to allow a match */
}
switch (isdn_msncmp(eaz, my_eaz)) {
case 1:
continue;
case 2:
match_more = 1;
continue;
}
if (isdn_net_bound(idev))
continue;
if (!USG_NONE(isdn_slot_usage(idx)))
continue;
dbg_net_icall("n_fi: match1, pdev=%d pch=%d\n",
idev->pre_device, idev->pre_channel);
if (isdn_slot_usage(idx) & ISDN_USAGE_EXCLUSIVE &&
(idev->pre_channel != ch || idev->pre_device != di)) {
dbg_net_icall("n_fi: excl check failed\n");
continue;
}
dbg_net_icall("n_fi: match2\n");
if (mlp->flags & ISDN_NET_SECURE) {
list_for_each_entry(n, &mlp->phone[0], list) {
if (!isdn_msncmp(nr, n->num)) {
goto found;
}
}
continue;
}
found:
dbg_net_icall("n_fi: match3\n");
/* matching interface found */
/*
* Is the state STOPPED?
* If so, no dialin is allowed,
* so reject actively.
* */
if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
restore_flags(flags);
printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
idev->name);
return 3;
}
/*
* Is the interface up?
* If not, reject the call actively.
*/
if (!isdn_net_device_started(idev)) {
restore_flags(flags);
printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
idev->name);
return 3;
}
if (mlp->flags & ISDN_NET_CALLBACK) {
retval = isdn_net_do_callback(idev);
restore_flags(flags);
return retval;
}
printk(KERN_DEBUG "%s: call from %s -> %s accepted\n",
idev->name, nr, eaz);
isdn_net_accept(idev, idx, nr);
restore_flags(flags);
return 1;
}
if (dev->net_verbose)
printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, slot, eaz);
restore_flags(flags);
return (match_more == 2) ? 5:0;
}
/* /*
* This is called from certain upper protocol layers (multilink ppp * This is called from certain upper protocol layers (multilink ppp
* and x25iface encapsulation module) that want to initiate dialing * and x25iface encapsulation module) that want to initiate dialing
......
...@@ -43,11 +43,7 @@ extern int register_isdn_netif(int encap, struct isdn_netif_ops *ops); ...@@ -43,11 +43,7 @@ extern int register_isdn_netif(int encap, struct isdn_netif_ops *ops);
extern int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev); extern int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev);
extern int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev); extern int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev);
extern int isdn_net_bind_channel(isdn_net_dev *idev, int slot);
extern void isdn_net_unbind_channel(isdn_net_dev *idev);
extern int isdn_net_dial(isdn_net_dev *idev); extern int isdn_net_dial(isdn_net_dev *idev);
extern void isdn_net_accept(isdn_net_dev *idev, int slot, char *nr);
extern int isdn_net_do_callback(isdn_net_dev *idev);
extern int isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c); extern int isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c);
......
...@@ -10,6 +10,33 @@ ...@@ -10,6 +10,33 @@
* of the GNU General Public License, incorporated herein by reference. * of the GNU General Public License, incorporated herein by reference.
*/ */
/* Locking works as follows:
*
* The configuration of isdn_net_devs works via ioctl on
* /dev/isdnctrl (for legacy reasons).
* All configuration accesses are globally serialized by means of
* the global semaphore &sem.
*
* All other uses of isdn_net_dev will only happen when the corresponding
* struct net_device has been opened. So in the non-config code we can
* rely on the config data not changing under us.
*
* To achieve this, in the "writing" ioctls, that is those which may change
* data, additionally grep the rtnl semaphore and check to make sure
* that the net_device has not been openend ("netif_running()")
*
* isdn_net_dev's are added to the global list "isdn_net_devs" in the
* configuration ioctls, so accesses to that list are protected by
* &sem as well.
*
* Incoming calls are signalled in IRQ context, so we cannot take &sem
* while walking the list of devices. To handle this, we put devices
* onto a "running" list, which is protected by a spin lock and can thus
* be traversed in IRQ context. If a matching isdn_net_dev is found,
* it's ref count shall be incremented, to make sure no racing
* net_device::close() can take it away under us.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
...@@ -28,6 +55,31 @@ ...@@ -28,6 +55,31 @@
static DECLARE_MUTEX(sem); static DECLARE_MUTEX(sem);
LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */ // FIXME static LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */ // FIXME static
/* Reference counting for net devices (they work on isdn_net_local *,
* but count references to the related isdn_net_dev's as well.
* Basic rule: When state of isdn_net_dev changes from ST_NULL -> sth,
* get a reference, when it changes back to ST_NULL, put it
*/
static inline void
lp_get(isdn_net_local *lp)
{
if (atomic_read(&lp->refcnt) < 1)
isdn_BUG();
atomic_inc(&lp->refcnt);
}
static inline void
lp_put(isdn_net_local *lp)
{
atomic_dec(&lp->refcnt);
/* the last reference, the list should always remain */
if (atomic_read(&lp->refcnt) < 1)
isdn_BUG();
}
int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg); /* FIXME */ 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);
...@@ -39,6 +91,7 @@ static struct fsm isdn_net_fsm; ...@@ -39,6 +91,7 @@ static struct fsm isdn_net_fsm;
enum { enum {
ST_NULL, ST_NULL,
ST_OUT_BOUND,
ST_OUT_WAIT_DCONN, ST_OUT_WAIT_DCONN,
ST_OUT_WAIT_BCONN, ST_OUT_WAIT_BCONN,
ST_IN_WAIT_DCONN, ST_IN_WAIT_DCONN,
...@@ -51,6 +104,7 @@ enum { ...@@ -51,6 +104,7 @@ enum {
static char *isdn_net_st_str[] = { static char *isdn_net_st_str[] = {
"ST_NULL", "ST_NULL",
"ST_OUT_BOUND",
"ST_OUT_WAIT_DCONN", "ST_OUT_WAIT_DCONN",
"ST_OUT_WAIT_BCONN", "ST_OUT_WAIT_BCONN",
"ST_IN_WAIT_DCONN", "ST_IN_WAIT_DCONN",
...@@ -74,7 +128,9 @@ enum { ...@@ -74,7 +128,9 @@ enum {
EV_STAT_BHUP, EV_STAT_BHUP,
EV_STAT_CINF, EV_STAT_CINF,
EV_STAT_BSENT, EV_STAT_BSENT,
EV_CMD_DIAL, EV_DO_DIAL,
EV_DO_CALLBACK,
EV_DO_ACCEPT,
}; };
static char *isdn_net_ev_str[] = { static char *isdn_net_ev_str[] = {
...@@ -90,7 +146,9 @@ static char *isdn_net_ev_str[] = { ...@@ -90,7 +146,9 @@ static char *isdn_net_ev_str[] = {
"EV_STAT_BHUP", "EV_STAT_BHUP",
"EV_STAT_CINF", "EV_STAT_CINF",
"EV_STAT_BSENT", "EV_STAT_BSENT",
"EV_CMD_DIAL", "EV_DO_DIAL",
"EV_DO_CALLBACK",
"EV_DO_ACCEPT",
}; };
/* ====================================================================== */ /* ====================================================================== */
...@@ -225,21 +283,18 @@ isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg) ...@@ -225,21 +283,18 @@ isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg)
goto out; goto out;
} }
if (idev->exclusive >= 0) { if (idev->exclusive >= 0) {
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel); isdn_slot_free(idev->exclusive);
isdn_free_channel(idev->pre_device, idev->pre_channel, ISDN_USAGE_NET);
idev->exclusive = -1; idev->exclusive = -1;
} }
if (cfg->exclusive) { if (cfg->exclusive) {
/* If binding is exclusive, try to grab the channel */ /* If binding is exclusive, try to grab the channel */
idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto, idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET | ISDN_USAGE_EXCLUSIVE,
mlp->l3_proto, drvidx, chidx, cfg->eaz); mlp->l2_proto, mlp->l3_proto, drvidx, chidx, cfg->eaz);
if (idev->exclusive < 0) { if (idev->exclusive < 0) {
/* Grab failed, because desired channel is in use */ /* Grab failed, because desired channel is in use */
retval = -EBUSY; retval = -EBUSY;
goto out; goto out;
} }
/* All went ok, so update isdninfo */
isdn_slot_set_usage(idev->exclusive, ISDN_USAGE_EXCLUSIVE);
} }
idev->pre_device = drvidx; idev->pre_device = drvidx;
idev->pre_channel = chidx; idev->pre_channel = chidx;
...@@ -360,6 +415,7 @@ isdn_net_addif(char *name, isdn_net_local *mlp) ...@@ -360,6 +415,7 @@ isdn_net_addif(char *name, isdn_net_local *mlp)
} }
} }
list_add(&idev->global_list, &isdn_net_devs); list_add(&idev->global_list, &isdn_net_devs);
return 0; return 0;
} }
...@@ -417,7 +473,7 @@ isdn_net_dev_delete(isdn_net_dev *idev) ...@@ -417,7 +473,7 @@ isdn_net_dev_delete(isdn_net_dev *idev)
isdn_net_rmallphone(idev); isdn_net_rmallphone(idev);
if (idev->exclusive >= 0) if (idev->exclusive >= 0)
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel); isdn_slot_free(idev->exclusive);
list_del(&idev->slaves); list_del(&idev->slaves);
...@@ -978,6 +1034,9 @@ isdn_net_exit(void) ...@@ -978,6 +1034,9 @@ isdn_net_exit(void)
/* interface to network layer */ /* interface to network layer */
/* ====================================================================== */ /* ====================================================================== */
static spinlock_t running_devs_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(running_devs);
/* /*
* Open/initialize the board. * Open/initialize the board.
*/ */
...@@ -985,6 +1044,7 @@ static int ...@@ -985,6 +1044,7 @@ static int
isdn_net_open(struct net_device *dev) isdn_net_open(struct net_device *dev)
{ {
isdn_net_local *lp = dev->priv; isdn_net_local *lp = dev->priv;
unsigned long flags;
int retval = 0; int retval = 0;
if (!lp->ops) if (!lp->ops)
...@@ -997,6 +1057,12 @@ isdn_net_open(struct net_device *dev) ...@@ -997,6 +1057,12 @@ isdn_net_open(struct net_device *dev)
return retval; return retval;
netif_start_queue(dev); netif_start_queue(dev);
atomic_set(&lp->refcnt, 1);
spin_lock_irqsave(&running_devs_lock, flags);
list_add(&lp->running_devs, &running_devs);
spin_unlock_irqrestore(&running_devs_lock, flags);
return 0; return 0;
} }
...@@ -1008,8 +1074,9 @@ static int ...@@ -1008,8 +1074,9 @@ static int
isdn_net_close(struct net_device *dev) isdn_net_close(struct net_device *dev)
{ {
isdn_net_local *lp = dev->priv; isdn_net_local *lp = dev->priv;
struct list_head *l, *n;
isdn_net_dev *sdev; isdn_net_dev *sdev;
struct list_head *l, *n;
unsigned long flags;
if (lp->ops->close) if (lp->ops->close)
lp->ops->close(lp); lp->ops->close(lp);
...@@ -1020,6 +1087,20 @@ isdn_net_close(struct net_device *dev) ...@@ -1020,6 +1087,20 @@ isdn_net_close(struct net_device *dev)
sdev = list_entry(l, isdn_net_dev, online); sdev = list_entry(l, isdn_net_dev, online);
isdn_net_hangup(sdev); isdn_net_hangup(sdev);
} }
/* The hangup will make the refcnt drop back to
* 1 (referenced by list only) soon. */
spin_lock_irqsave(&running_devs_lock, flags);
while (atomic_read(&dev->refcnt) != 1) {
spin_unlock_irqrestore(&running_devs_lock, flags);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
spin_lock_irqsave(&running_devs_lock, flags);
}
/* We have the only reference and list lock, so
* nobody can get another reference. */
list_del(&lp->running_devs);
spin_unlock_irqrestore(&running_devs_lock, flags);
return 0; return 0;
} }
...@@ -1101,43 +1182,13 @@ isdn_net_dial_timer(unsigned long data) ...@@ -1101,43 +1182,13 @@ isdn_net_dial_timer(unsigned long data)
isdn_net_handle_event(idev, idev->dial_event, NULL); isdn_net_handle_event(idev, idev->dial_event, NULL);
} }
/*
* Assign an ISDN-channel to a net-interface
*/
int
isdn_net_bind_channel(isdn_net_dev *idev, int slot)
{
isdn_net_local *mlp = idev->mlp;
int retval = 0;
unsigned long flags;
save_flags(flags);
cli();
idev->isdn_slot = slot;
isdn_slot_set_idev(idev->isdn_slot, idev);
if (mlp->ops->bind)
retval = mlp->ops->bind(idev);
if (retval < 0)
isdn_net_unbind_channel(idev);
restore_flags(flags);
return retval;
}
/* /*
* Unbind a net-interface * Unbind a net-interface
*/ */
void static void
isdn_net_unbind_channel(isdn_net_dev *idev) isdn_net_unbind_channel(isdn_net_dev *idev)
{ {
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
ulong flags;
save_flags(flags);
cli();
if (idev->isdn_slot < 0) { if (idev->isdn_slot < 0) {
isdn_BUG(); isdn_BUG();
...@@ -1147,57 +1198,65 @@ isdn_net_unbind_channel(isdn_net_dev *idev) ...@@ -1147,57 +1198,65 @@ isdn_net_unbind_channel(isdn_net_dev *idev)
if (mlp->ops->unbind) if (mlp->ops->unbind)
mlp->ops->unbind(idev); mlp->ops->unbind(idev);
skb_queue_purge(&idev->super_tx_queue); isdn_slot_set_idev(idev->isdn_slot, NULL);
fsm_change_state(&idev->fi, ST_NULL); skb_queue_purge(&idev->super_tx_queue);
isdn_slot_set_idev(idev->isdn_slot, NULL); if (idev->isdn_slot != idev->exclusive)
isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET); isdn_slot_free(idev->isdn_slot);
idev->isdn_slot = -1; idev->isdn_slot = -1;
restore_flags(flags); if (idev->fi.state != ST_NULL) {
lp_put(mlp);
fsm_change_state(&idev->fi, ST_NULL);
}
} }
int /*
isdn_net_dial(isdn_net_dev *idev) * Assign an ISDN-channel to a net-interface
*/
static int
isdn_net_bind_channel(isdn_net_dev *idev, int slot)
{ {
int slot;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
int retval = 0;
if (isdn_net_bound(idev)) idev->isdn_slot = slot;
return -EBUSY; isdn_slot_set_idev(idev->isdn_slot, idev);
if (idev->exclusive >= 0) if (mlp->ops->bind)
slot = idev->exclusive; retval = mlp->ops->bind(idev);
else
slot = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto, if (retval < 0)
mlp->l3_proto, idev->pre_device, isdn_net_unbind_channel(idev);
idev->pre_channel, mlp->msn);
if (slot < 0)
goto err;
isdn_slot_set_usage(slot, isdn_slot_usage(slot) | ISDN_USAGE_OUTGOING); return retval;
}
if (isdn_net_bind_channel(idev, slot) < 0) int
goto err; isdn_net_dial(isdn_net_dev *idev)
{
int retval;
/* Initiate dialing */ lp_get(idev->mlp);
fsm_event(&idev->fi, EV_CMD_DIAL, NULL); retval = fsm_event(&idev->fi, EV_DO_DIAL, NULL);
return 0; if (retval == -ESRCH) /* event not handled in this state */
retval = -EBUSY;
err: if (retval)
return -EAGAIN; lp_put(idev->mlp);
return retval;
} }
void static int
isdn_net_accept(isdn_net_dev *idev, int slot, char *nr) accept_icall(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;
int slot = (int) arg;
strcpy(isdn_slot_num(slot), nr);
isdn_slot_set_usage(slot, (isdn_slot_usage(slot) & ISDN_USAGE_EXCLUSIVE) | ISDN_USAGE_NET);
isdn_net_bind_channel(idev, slot); isdn_net_bind_channel(idev, slot);
...@@ -1215,47 +1274,191 @@ isdn_net_accept(isdn_net_dev *idev, int slot, char *nr) ...@@ -1215,47 +1274,191 @@ isdn_net_accept(isdn_net_dev *idev, int slot, char *nr)
idev->dial_event = EV_TIMER_INCOMING; idev->dial_event = EV_TIMER_INCOMING;
add_timer(&idev->dial_timer); add_timer(&idev->dial_timer);
fsm_change_state(&idev->fi, ST_IN_WAIT_DCONN); fsm_change_state(&idev->fi, ST_IN_WAIT_DCONN);
return 0;
} }
int static int
isdn_net_do_callback(isdn_net_dev *idev) do_callback(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;
int slot;
/*
* Is the state MANUAL?
* If so, no callback can be made,
* so reject actively.
*/
if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
idev->name);
return 3;
}
printk(KERN_DEBUG "%s: start callback\n", idev->name);
/* Grab a free ISDN-Channel */
slot = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto, mlp->l3_proto,
idev->pre_device, idev->pre_channel, mlp->msn);
if (slot < 0)
goto err;
isdn_slot_set_usage(slot, isdn_slot_usage(slot) | ISDN_USAGE_OUTGOING);
if (isdn_net_bind_channel(idev, slot) < 0) printk(KERN_DEBUG "%s: start callback\n", idev->name);
goto err;
/* Setup dialstate. */
idev->dial_timer.expires = jiffies + mlp->cbdelay; idev->dial_timer.expires = jiffies + mlp->cbdelay;
idev->dial_event = EV_TIMER_CB_IN; idev->dial_event = EV_TIMER_CB_IN;
add_timer(&idev->dial_timer); add_timer(&idev->dial_timer);
fsm_change_state(&idev->fi, ST_WAIT_BEFORE_CB); fsm_change_state(&idev->fi, ST_WAIT_BEFORE_CB);
return 0;
}
static int
isdn_net_dev_icall(isdn_net_dev *idev, int di, int ch, int si1,
char *eaz, char *nr)
{
isdn_net_local *mlp = idev->mlp;
struct isdn_net_phone *ph;
int slot = isdn_dc2minor(di, ch);
char *my_eaz;
/* 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->fi.state);
my_eaz = isdn_slot_map_eaz2msn(slot, mlp->msn);
if (si1 == 1) { /* it's a DOV call, check if we allow it */
if (*my_eaz == 'v' || *my_eaz == 'V' ||
*my_eaz == 'b' || *my_eaz == 'B')
my_eaz++; /* skip to allow a match */
else
return 0; /* no match */
} else { /* it's a DATA call, check if we allow it */
if (*my_eaz == 'b' || *my_eaz == 'B')
my_eaz++; /* skip to allow a match */
}
if (!USG_NONE(isdn_slot_usage(slot))) // FIXME?
return 0;
/* check called number */
switch (isdn_msncmp(eaz, my_eaz)) {
case 1: /* no match */
return 0;
case 2: /* matches so far */
return 5;
}
dbg_net_icall("%s: pdev=%d di=%d pch=%d ch = %d\n", idev->name,
idev->pre_device, di, idev->pre_channel, ch);
/* check if exclusive */
if ((isdn_slot_usage(slot) & ISDN_USAGE_EXCLUSIVE) &&
(idev->pre_channel != ch || idev->pre_device != di)) {
dbg_net_icall("%s: excl check failed\n", idev->name);
return 0;
}
/* check calling number */
dbg_net_icall("%s: secure\n", idev->name);
if (mlp->flags & ISDN_NET_SECURE) {
list_for_each_entry(ph, &mlp->phone[0], list) {
if (isdn_msncmp(nr, ph->num) == 0)
goto found;
}
return 0;
}
found:
/* check dial mode */
if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
printk(KERN_INFO "%s: incoming call, stopped -> rejected\n",
idev->name);
return 3;
}
lp_get(mlp);
/* check callback */
if (mlp->flags & ISDN_NET_CALLBACK) {
if (fsm_event(&idev->fi, EV_DO_CALLBACK, NULL)) {
lp_put(mlp);
return 0;
}
/* 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;
}
printk(KERN_INFO "%s: call from %s -> %s accepted\n",
idev->name, nr, eaz);
err: if (fsm_event(&idev->fi, EV_DO_ACCEPT, (void *) slot)) {
lp_put(mlp);
return 0; return 0;
}
return 1; // accepted
}
/*
* An incoming call-request has arrived.
* Search the interface-chain for an appropriate interface.
* If found, connect the interface to the ISDN-channel and initiate
* D- and B-Channel-setup. If secure-flag is set, accept only
* configured phone-numbers. If callback-flag is set, initiate
* callback-dialing.
*
* Return-Value: 0 = No appropriate interface for this call.
* 1 = Call accepted
* 2 = Reject call, wait cbdelay, then call back
* 3 = Reject call
* 4 = Wait cbdelay, then call back
* 5 = No appropriate interface for this call,
* would eventually match if CID was longer.
*/
int
isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
{
isdn_net_local *lp;
isdn_net_dev *idev;
char *nr, *eaz;
unsigned char si1, si2;
int retval;
unsigned long flags;
/* fix up calling number */
if (!setup->phone[0]) {
printk(KERN_INFO
"isdn_net: Incoming call without OAD, assuming '0'\n");
nr = "0";
} else {
nr = setup->phone;
}
/* fix up called number */
if (!setup->eazmsn[0]) {
printk(KERN_INFO
"isdn_net: Incoming call without CPN, assuming '0'\n");
eaz = "0";
} else {
eaz = setup->eazmsn;
}
si1 = setup->si1;
si2 = setup->si2;
if (dev->net_verbose > 1)
printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n",
nr, si1, si2, eaz);
/* check service indicator */
/* Accept DATA and VOICE calls at this stage
local eaz is checked later for allowed call types */
if ((si1 != 7) && (si1 != 1)) {
if (dev->net_verbose > 1)
printk(KERN_INFO "isdn_net: "
"Service-Indicator not 1 or 7, ignored\n");
return 0;
}
dbg_net_icall("n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
isdn_slot_usage(idx));
retval = 0;
spin_lock_irqsave(&running_devs_lock, flags);
list_for_each_entry(lp, &running_devs, running_devs) {
lp_get(lp);
spin_unlock_irqrestore(&running_devs_lock, flags);
list_for_each_entry(idev, &lp->slaves, slaves) {
retval = isdn_net_dev_icall(idev, di, ch, si1, eaz, nr);
if (retval > 0)
break;
}
spin_lock_irqsave(&running_devs_lock, flags);
lp_put(lp);
if (retval > 0)
break;
}
spin_unlock_irqrestore(&running_devs_lock, flags);
if (!retval) {
if (dev->net_verbose)
printk(KERN_INFO "isdn_net: call "
"from %s -> %s ignored\n", nr, eaz);
}
return retval;
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
...@@ -1283,23 +1486,39 @@ static int dialout_next(struct fsm_inst *fi, int pr, void *arg); ...@@ -1283,23 +1486,39 @@ static int dialout_next(struct fsm_inst *fi, int pr, void *arg);
/* Initiate dialout. */ /* Initiate dialout. */
static int static int
dialout_first(struct fsm_inst *fi, int pr, void *arg) do_dial(struct fsm_inst *fi, int pr, void *arg)
{ {
isdn_net_dev *idev = fi->userdata; isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp; isdn_net_local *mlp = idev->mlp;
int slot;
if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) { if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF)
isdn_net_unbind_channel(idev);
return -EPERM; return -EPERM;
}
if (list_empty(&mlp->phone[1])) { if (list_empty(&mlp->phone[1])) /* no number to dial ? */
isdn_net_unbind_channel(idev);
return -EINVAL; return -EINVAL;
if (idev->exclusive >= 0)
slot = idev->exclusive;
else
slot = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto,
mlp->l3_proto, idev->pre_device,
idev->pre_channel, mlp->msn);
if (slot < 0)
return -EAGAIN;
if (isdn_net_bind_channel(idev, slot) < 0) {
/* has freed the slot as well */
return -EAGAIN;
} }
fsm_change_state(fi, ST_OUT_BOUND);
idev->dial = 0; idev->dial = 0;
idev->dialretry = 0; idev->dialretry = 0;
return dialout_next(fi, pr, arg);
dialout_next(fi, pr, arg);
return 0;
} }
/* Try dialing the next number. */ /* Try dialing the next number. */
...@@ -1598,7 +1817,9 @@ got_bsent(struct fsm_inst *fi, int pr, void *arg) ...@@ -1598,7 +1817,9 @@ got_bsent(struct fsm_inst *fi, int pr, void *arg)
} }
static struct fsm_node isdn_net_fn_tbl[] = { static struct fsm_node isdn_net_fn_tbl[] = {
{ ST_NULL, EV_CMD_DIAL, dialout_first }, { ST_NULL, EV_DO_DIAL, do_dial },
{ ST_NULL, EV_DO_ACCEPT, accept_icall },
{ ST_NULL, EV_DO_CALLBACK, do_callback },
{ ST_OUT_WAIT_DCONN, EV_TIMER_DIAL, dial_timeout }, { ST_OUT_WAIT_DCONN, EV_TIMER_DIAL, dial_timeout },
{ ST_OUT_WAIT_DCONN, EV_STAT_DCONN, out_dconn }, { ST_OUT_WAIT_DCONN, EV_STAT_DCONN, out_dconn },
...@@ -1624,7 +1845,7 @@ static struct fsm_node isdn_net_fn_tbl[] = { ...@@ -1624,7 +1845,7 @@ static struct fsm_node isdn_net_fn_tbl[] = {
{ ST_WAIT_DHUP, EV_STAT_DHUP, dhup }, { ST_WAIT_DHUP, EV_STAT_DHUP, dhup },
{ ST_WAIT_BEFORE_CB, EV_TIMER_CB_IN, dialout_first }, { ST_WAIT_BEFORE_CB, EV_TIMER_CB_IN, do_dial },
{ ST_OUT_DIAL_WAIT, EV_TIMER_DIAL_WAIT, dialout_next }, { ST_OUT_DIAL_WAIT, EV_TIMER_DIAL_WAIT, dialout_next },
}; };
......
...@@ -745,7 +745,7 @@ isdn_tty_modem_hup(modem_info * info, int local) ...@@ -745,7 +745,7 @@ isdn_tty_modem_hup(modem_info * info, int local)
isdn_slot_all_eaz(slot); isdn_slot_all_eaz(slot);
info->emu.mdmreg[REG_RINGCNT] = 0; info->emu.mdmreg[REG_RINGCNT] = 0;
isdn_slot_free(slot, 0); isdn_slot_free(slot);
isdn_slot_set_m_idx(slot, -1); isdn_slot_set_m_idx(slot, -1);
info->isdn_slot = -1; info->isdn_slot = -1;
} }
......
...@@ -382,7 +382,7 @@ isdn_tty_cmd_FCLASS1(char **p, modem_info * info) ...@@ -382,7 +382,7 @@ isdn_tty_cmd_FCLASS1(char **p, modem_info * info)
info->isdn_slot = i; info->isdn_slot = i;
isdn_slot_set_m_idx(i, info->line); isdn_slot_set_m_idx(i, info->line);
isdn_slot_command(info->isdn_slot, ISDN_CMD_FAXCMD, &c); isdn_slot_command(info->isdn_slot, ISDN_CMD_FAXCMD, &c);
isdn_slot_free(info->isdn_slot, ISDN_USAGE_FAX); isdn_slot_free(info->isdn_slot);
isdn_slot_set_m_idx(i, -1); isdn_slot_set_m_idx(i, -1);
info->isdn_slot = -1; info->isdn_slot = -1;
restore_flags(flags); restore_flags(flags);
......
...@@ -339,10 +339,11 @@ typedef struct isdn_net_local_s { ...@@ -339,10 +339,11 @@ typedef struct isdn_net_local_s {
/* phone[1] = Outgoing Numbers */ /* phone[1] = Outgoing Numbers */
struct list_head slaves; /* list of all bundled channels */ struct list_head slaves; /* list of all bundled channels */
struct list_head online; /* circular list of all bundled struct list_head online; /* list of all bundled channels,
channels, which are currently which are currently online */
online */ spinlock_t online_lock; /* lock to protect online list */
spinlock_t online_lock; /* lock to protect queue */ struct list_head running_devs; /* member of global running_devs */
atomic_t refcnt; /* references held by ISDN code */
#ifdef CONFIG_ISDN_X25 #ifdef CONFIG_ISDN_X25
struct concap_device_ops *dops; /* callbacks used by encapsulator */ struct concap_device_ops *dops; /* callbacks used by encapsulator */
...@@ -403,8 +404,8 @@ typedef struct isdn_net_dev_s { ...@@ -403,8 +404,8 @@ typedef struct isdn_net_dev_s {
isdn_net_local *mlp; /* Ptr to master device for all devs*/ isdn_net_local *mlp; /* Ptr to master device for all devs*/
struct list_head slaves; /* Members of local->slaves */ struct list_head slaves; /* member of local->slaves */
struct list_head online; /* Members of local->online */ struct list_head online; /* member of local->online */
char name[10]; /* Name of device */ char name[10]; /* Name of device */
struct list_head global_list; /* global list of all isdn_net_devs */ struct list_head global_list; /* global list of all isdn_net_devs */
......
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