Commit 994b6e06 authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN: Merge Ingo's work_struct and my tasklet changes

parents ab1180be f31b5ddd
......@@ -81,7 +81,7 @@ ifneq ($(filter all,$(MAKECMDGOALS)),)
KBUILD_MODULES := 1
endif
export KBUILD_MODULES KBUILD_BUILTIN
export KBUILD_MODULES KBUILD_BUILTIN KBUILD_VERBOSE
# Beautify output
# ---------------------------------------------------------------------------
......
......@@ -11,9 +11,11 @@ obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
# Multipart objects.
isdn-objs := isdn_net.o isdn_tty.o \
isdn_v110.o isdn_common.o \
isdn_ciscohdlck.o
isdn-objs := isdn_net.o isdn_net_lib.o \
isdn_fsm.o \
isdn_ciscohdlck.o \
isdn_tty.o isdn_v110.o \
isdn_common.o \
# Optional parts of multipart objects.
......
......@@ -24,9 +24,8 @@
* CISCO HDLC keepalive specific stuff
*/
static struct sk_buff*
isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
isdn_net_ciscohdlck_alloc_skb(isdn_net_dev *idev, int len)
{
isdn_net_dev *idev = lp->netdev;
unsigned short hl = isdn_slot_hdrlen(idev->isdn_slot);
struct sk_buff *skb;
......@@ -43,63 +42,60 @@ isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
static int
isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
isdn_net_local *lp = (isdn_net_local *) dev->priv;
isdn_net_dev *idev = lp->netdev;
isdn_net_local *mlp = dev->priv;
unsigned long len = 0;
unsigned long expires = 0;
int tmp = 0;
int period = lp->cisco_keepalive_period;
char debserint = lp->cisco_debserint;
int period;
char debserint;
int rc = 0;
if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
if (mlp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
return -EINVAL;
switch (cmd) {
/* get/set keepalive period */
case SIOCGKEEPPERIOD:
len = (unsigned long)sizeof(lp->cisco_keepalive_period);
len = sizeof(mlp->cisco_keepalive_period);
if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
(int *)&lp->cisco_keepalive_period, len))
(char *)&mlp->cisco_keepalive_period, len))
rc = -EFAULT;
break;
case SIOCSKEEPPERIOD:
tmp = lp->cisco_keepalive_period;
len = (unsigned long)sizeof(lp->cisco_keepalive_period);
if (copy_from_user((int *)&period,
(char *)ifr->ifr_ifru.ifru_data, len))
len = sizeof(mlp->cisco_keepalive_period);
if (copy_from_user((char *)&period,
(char *)ifr->ifr_ifru.ifru_data, len)) {
rc = -EFAULT;
if ((period > 0) && (period <= 32767))
lp->cisco_keepalive_period = period;
else
break;
}
if (period <= 0 || period > 32767) {
rc = -EINVAL;
if (!rc && (tmp != lp->cisco_keepalive_period)) {
expires = (unsigned long)(jiffies +
lp->cisco_keepalive_period * HZ);
mod_timer(&lp->cisco_timer, expires);
printk(KERN_INFO "%s: Keepalive period set "
"to %d seconds.\n",
idev->name, lp->cisco_keepalive_period);
break;
}
mod_timer(&mlp->cisco_timer, jiffies + period * HZ);
printk(KERN_INFO "%s: Keepalive period set "
"to %d seconds.\n", dev->name, period);
mlp->cisco_keepalive_period = period;
break;
/* get/set debugging */
case SIOCGDEBSERINT:
len = (unsigned long)sizeof(lp->cisco_debserint);
len = sizeof(mlp->cisco_debserint);
if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
(char *)&lp->cisco_debserint, len))
(char *)&mlp->cisco_debserint, len))
rc = -EFAULT;
break;
case SIOCSDEBSERINT:
len = (unsigned long)sizeof(lp->cisco_debserint);
len = sizeof(mlp->cisco_debserint);
if (copy_from_user((char *)&debserint,
(char *)ifr->ifr_ifru.ifru_data, len))
(char *)ifr->ifr_ifru.ifru_data, len)) {
rc = -EFAULT;
if ((debserint >= 0) && (debserint <= 64))
lp->cisco_debserint = debserint;
else
break;
}
if (debserint < 0 || debserint > 64) {
rc = -EINVAL;
break;
}
mlp->cisco_debserint = debserint;
break;
default:
rc = -EINVAL;
......@@ -112,42 +108,47 @@ isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static void
isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
{
isdn_net_local *lp = (isdn_net_local *) data;
isdn_net_dev *idev = lp->netdev;
isdn_net_local *mlp = (isdn_net_local *) data;
isdn_net_dev *idev;
struct sk_buff *skb;
unsigned char *p;
unsigned long last_cisco_myseq = lp->cisco_myseq;
unsigned long last_cisco_myseq = mlp->cisco_myseq;
int myseq_diff = 0;
lp->cisco_myseq++;
if (list_empty(&mlp->online)) {
isdn_BUG();
return;
}
idev = list_entry(mlp->online.next, isdn_net_dev, online);
mlp->cisco_myseq++;
myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen);
if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) {
myseq_diff = (mlp->cisco_myseq - mlp->cisco_mineseen);
if (mlp->cisco_line_state && (myseq_diff >= 3 || myseq_diff <= -3)) {
/* line up -> down */
lp->cisco_line_state = 0;
mlp->cisco_line_state = 0;
printk (KERN_WARNING
"UPDOWN: Line protocol on Interface %s,"
" changed state to down\n", idev->name);
/* should stop routing higher-level data accross */
} else if ((!lp->cisco_line_state) &&
(myseq_diff >= 0) && (myseq_diff <= 2)) {
} else if (!mlp->cisco_line_state &&
myseq_diff >= 0 && myseq_diff <= 2) {
/* line down -> up */
lp->cisco_line_state = 1;
mlp->cisco_line_state = 1;
printk (KERN_WARNING
"UPDOWN: Line protocol on Interface %s,"
" changed state to up\n", idev->name);
/* restart routing higher-level data accross */
}
if (lp->cisco_debserint)
if (mlp->cisco_debserint)
printk (KERN_DEBUG "%s: HDLC "
"myseq %lu, mineseen %lu%c, yourseen %lu, %s\n",
idev->name, last_cisco_myseq, lp->cisco_mineseen,
(last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040,
lp->cisco_yourseq,
(lp->cisco_line_state) ? "line up" : "line down");
idev->name, last_cisco_myseq, mlp->cisco_mineseen,
(last_cisco_myseq == mlp->cisco_mineseen) ? '*' : 040,
mlp->cisco_yourseq,
(mlp->cisco_line_state) ? "line up" : "line down");
skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
skb = isdn_net_ciscohdlck_alloc_skb(idev, 4 + 14);
if (!skb)
return;
......@@ -160,24 +161,29 @@ isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
/* slarp keepalive */
p += put_u32(p, CISCO_SLARP_KEEPALIVE);
p += put_u32(p, lp->cisco_myseq);
p += put_u32(p, lp->cisco_yourseq);
p += put_u32(p, mlp->cisco_myseq);
p += put_u32(p, mlp->cisco_yourseq);
p += put_u16(p, 0xffff); // reliablity, always 0xffff
isdn_net_write_super(lp, skb);
lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
isdn_net_write_super(idev, skb);
add_timer(&lp->cisco_timer);
mod_timer(&mlp->cisco_timer, jiffies + mlp->cisco_keepalive_period * HZ);
}
static void
isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *mlp)
{
isdn_net_dev *idev;
struct sk_buff *skb;
unsigned char *p;
skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
if (list_empty(&mlp->online)) {
isdn_BUG();
return;
}
idev = list_entry(mlp->online.next, isdn_net_dev, online);
skb = isdn_net_ciscohdlck_alloc_skb(idev, 4 + 14);
if (!skb)
return;
......@@ -194,12 +200,14 @@ isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
p += put_u32(p, 0); // netmask
p += put_u16(p, 0); // unused
isdn_net_write_super(lp, skb);
isdn_net_write_super(idev, skb);
}
static void
isdn_ciscohdlck_connected(isdn_net_local *lp)
isdn_ciscohdlck_connected(isdn_net_dev *idev)
{
isdn_net_local *lp = idev->mlp;
lp->cisco_myseq = 0;
lp->cisco_mineseen = 0;
lp->cisco_yourseq = 0;
......@@ -218,27 +226,30 @@ isdn_ciscohdlck_connected(isdn_net_local *lp)
lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
add_timer(&lp->cisco_timer);
}
isdn_net_device_wake_queue(lp);
netif_wake_queue(&lp->dev);
}
static void
isdn_ciscohdlck_disconnected(isdn_net_local *lp)
isdn_ciscohdlck_disconnected(isdn_net_dev *idev)
{
isdn_net_local *lp = idev->mlp;
if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) {
del_timer(&lp->cisco_timer);
}
}
static void
isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
isdn_net_ciscohdlck_slarp_send_reply(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
struct sk_buff *skb;
unsigned char *p;
struct in_device *in_dev = NULL;
u32 addr = 0; /* local ipv4 address */
u32 mask = 0; /* local netmask */
if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
if ((in_dev = mlp->dev.ip_ptr) != NULL) {
/* take primary(first) address of interface */
struct in_ifaddr *ifa = in_dev->ifa_list;
if (ifa != NULL) {
......@@ -247,7 +258,7 @@ isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
}
}
skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
skb = isdn_net_ciscohdlck_alloc_skb(idev, 4 + 14);
if (!skb)
return;
......@@ -265,13 +276,13 @@ isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
p += put_u32(p, mask); // netmask
p += put_u16(p, 0); // unused
isdn_net_write_super(lp, skb);
isdn_net_write_super(idev, skb);
}
static void
isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
isdn_net_ciscohdlck_slarp_in(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_dev *idev = lp->netdev;
isdn_net_local *mlp = idev->mlp;
unsigned char *p;
int period;
u32 code;
......@@ -288,8 +299,8 @@ isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
switch (code) {
case CISCO_SLARP_REQUEST:
lp->cisco_yourseq = 0;
isdn_net_ciscohdlck_slarp_send_reply(lp);
mlp->cisco_yourseq = 0;
isdn_net_ciscohdlck_slarp_send_reply(idev);
break;
case CISCO_SLARP_REPLY:
addr = ntohl(*(u32 *)p);
......@@ -315,30 +326,29 @@ isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
HIPQUAD(addr), HIPQUAD(mask));
break;
case CISCO_SLARP_KEEPALIVE:
period = (int)((jiffies - lp->cisco_last_slarp_in
period = (int)((jiffies - mlp->cisco_last_slarp_in
+ HZ/2 - 1) / HZ);
if (lp->cisco_debserint &&
(period != lp->cisco_keepalive_period) &&
lp->cisco_last_slarp_in) {
if (mlp->cisco_debserint &&
(period != mlp->cisco_keepalive_period) &&
mlp->cisco_last_slarp_in) {
printk(KERN_DEBUG "%s: Keepalive period mismatch - "
"is %d but should be %d.\n",
idev->name, period, lp->cisco_keepalive_period);
idev->name, period, mlp->cisco_keepalive_period);
}
lp->cisco_last_slarp_in = jiffies;
mlp->cisco_last_slarp_in = jiffies;
p += get_u32(p, &my_seq);
p += get_u32(p, &your_seq);
p += get_u16(p, &unused);
lp->cisco_yourseq = my_seq;
lp->cisco_mineseen = your_seq;
mlp->cisco_yourseq = my_seq;
mlp->cisco_mineseen = your_seq;
break;
}
}
static void
isdn_ciscohdlck_receive(isdn_net_dev *idev, isdn_net_local *olp,
isdn_ciscohdlck_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb)
{
isdn_net_local *lp = &idev->local;
unsigned char *p;
u8 addr;
u8 ctrl;
......@@ -362,7 +372,7 @@ isdn_ciscohdlck_receive(isdn_net_dev *idev, isdn_net_local *olp,
switch (type) {
case CISCO_TYPE_SLARP:
isdn_net_ciscohdlck_slarp_in(lp, skb);
isdn_net_ciscohdlck_slarp_in(idev, skb);
goto out_free;
case CISCO_TYPE_CDP:
if (lp->cisco_debserint)
......@@ -371,7 +381,7 @@ isdn_ciscohdlck_receive(isdn_net_dev *idev, isdn_net_local *olp,
goto out_free;
default:
/* no special cisco protocol */
isdn_net_reset_huptimer(idev, olp->netdev);
idev->huptimer = 0;
skb->protocol = htons(type);
netif_rx(skb);
return;
......@@ -396,6 +406,7 @@ isdn_ciscohdlck_header(struct sk_buff *skb, struct net_device *dev,
}
struct isdn_netif_ops ciscohdlck_ops = {
.hard_start_xmit = isdn_net_start_xmit,
.hard_header = isdn_ciscohdlck_header,
.do_ioctl = isdn_ciscohdlck_dev_ioctl,
.flags = IFF_NOARP | IFF_POINTOPOINT,
......
......@@ -46,8 +46,7 @@ struct isdn_slot {
unsigned long obytes; /* Statistics outgoing bytes */
struct isdn_v110 iv110; /* For V.110 */
int m_idx; /* Index for mdm.... */
isdn_net_dev *rx_netdev; /* rx netdev-pointers */
isdn_net_dev *st_netdev; /* stat netdev-pointers */
isdn_net_dev *idev; /* pointer to isdn_net_dev */
};
static struct isdn_slot slot[ISDN_MAX_CHANNELS];
......@@ -508,7 +507,7 @@ isdn_status_callback(isdn_ctrl * c)
list_for_each(l, &isdn_net_devs) {
isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
if (p->isdn_slot == i) {
strcpy(cmd.parm.setup.eazmsn, p->local.msn);
strcpy(cmd.parm.setup.eazmsn, p->mlp->msn);
isdn_slot_command(i, ISDN_CMD_ACCEPTD, &cmd);
retval = 1;
break;
......@@ -1010,19 +1009,6 @@ static int
isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{
int ret;
union iocpar {
char name[10];
char bname[22];
isdn_ioctl_struct iocts;
isdn_net_ioctl_phone phone;
isdn_net_ioctl_cfg cfg;
} iocpar;
#define name iocpar.name
#define bname iocpar.bname
#define iocts iocpar.iocts
#define phone iocpar.phone
#define cfg iocpar.cfg
switch (cmd) {
case IIOCGETDVR:
......@@ -1044,26 +1030,11 @@ isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
} else
return -EINVAL;
break;
#ifdef CONFIG_NETDEVICES
case IIOCNETGPN:
/* Get peer phone number of a connected
* isdn network interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
} else
return -EINVAL;
#endif
return isdn_net_ioctl(inode, file, cmd, arg);
default:
return -EINVAL;
}
#undef name
#undef bname
#undef iocts
#undef phone
#undef cfg
}
static struct file_operations isdn_status_fops =
......@@ -1221,19 +1192,15 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
int ret;
int i;
char *p;
union iocpar {
char name[10];
/* save stack space */
union {
char bname[20];
isdn_ioctl_struct iocts;
isdn_net_ioctl_phone phone;
isdn_net_ioctl_cfg cfg;
} iocpar;
#define name iocpar.name
#define bname iocpar.bname
#define iocts iocpar.iocts
#define phone iocpar.phone
#define cfg iocpar.cfg
#define bname iocpar.bname
/*
* isdn net devices manage lots of configuration variables as linked lists.
* Those lists must only be manipulated from user space. Some of the ioctl's
......@@ -1242,134 +1209,19 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
* are serialized by means of a semaphore.
*/
switch (cmd) {
case IIOCNETDWRSET:
printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
return(-EINVAL);
case IIOCNETLCR:
printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
return -ENODEV;
#ifdef CONFIG_NETDEVICES
case IIOCNETAIF:
/* Add a network-interface */
if (copy_from_user(name, (char *) arg, sizeof(name) - 1))
return -EFAULT;
name[sizeof(name)-1] = 0;
ret = down_interruptible(&dev->sem);
if (ret)
return ret;
ret = isdn_net_new(name, NULL);
up(&dev->sem);
return ret;
case IIOCNETASL:
/* Add a slave to a network-interface */
if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1))
return -EFAULT;
bname[sizeof(bname)-1] = 0;
ret = down_interruptible(&dev->sem);
if (ret)
return ret;
ret = isdn_net_newslave(bname);
up(&dev->sem);
return ret;
case IIOCNETDIF:
/* Delete a network-interface */
if (arg) {
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_rm(name);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETSCF:
/* Set configurable parameters of a network-interface */
if (arg) {
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
return -EFAULT;
return isdn_net_setcfg(&cfg);
} else
return -EINVAL;
case IIOCNETGCF:
/* Get configurable parameters of a network-interface */
if (arg) {
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
return -EFAULT;
if (!(ret = isdn_net_getcfg(&cfg))) {
if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
return -EFAULT;
}
return ret;
} else
return -EINVAL;
case IIOCNETANM:
/* Add a phone-number to a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_addphone(&phone);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETGNM:
/* Get list of phone-numbers of a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_getphones(&phone, (char *) arg);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETDNM:
/* Delete a phone-number of a network-interface */
if (arg) {
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
return -EFAULT;
ret = down_interruptible(&dev->sem);
if( ret ) return ret;
ret = isdn_net_delphone(&phone);
up(&dev->sem);
return ret;
} else
return -EINVAL;
case IIOCNETDIL:
/* Force dialing of a network-interface */
if (arg) {
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_net_force_dial(name);
} else
return -EINVAL;
#ifdef CONFIG_ISDN_PPP
case IIOCNETALN:
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_ppp_dial_slave(name);
case IIOCNETDLN:
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_ppp_hangup_slave(name);
#endif
case IIOCNETHUP:
/* Force hangup of a network-interface */
if (!arg)
return -EINVAL;
if (copy_from_user(name, (char *) arg, sizeof(name)))
return -EFAULT;
return isdn_net_force_hangup(name);
break;
#endif /* CONFIG_NETDEVICES */
return isdn_net_ioctl(inode, file, cmd, arg);
case IIOCSETVER:
dev->net_verbose = arg;
printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
......@@ -1577,12 +1429,8 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
} else
return -EINVAL;
}
#undef name
#undef bname
#undef iocts
#undef phone
#undef cfg
#undef bname
}
static struct file_operations isdn_ctrl_fops =
......@@ -2218,35 +2066,19 @@ isdn_slot_num(int sl)
}
void
isdn_slot_set_rx_netdev(int sl, isdn_net_dev *nd)
{
BUG_ON(sl < 0);
slot[sl].rx_netdev = nd;
}
isdn_net_dev *
isdn_slot_rx_netdev(int sl)
{
BUG_ON(sl < 0);
return slot[sl].rx_netdev;
}
void
isdn_slot_set_st_netdev(int sl, isdn_net_dev *nd)
isdn_slot_set_idev(int sl, isdn_net_dev *idev)
{
BUG_ON(sl < 0);
slot[sl].st_netdev = nd;
slot[sl].idev = idev;
}
isdn_net_dev *
isdn_slot_st_netdev(int sl)
isdn_slot_idev(int sl)
{
BUG_ON(sl < 0);
return slot[sl].st_netdev;
return slot[sl].idev;
}
int
......@@ -2430,7 +2262,7 @@ static int __init isdn_init(void)
printk("\n");
#endif
isdn_info_update();
isdn_net_init_module();
isdn_net_init();
return 0;
err_tty_modem:
......@@ -2456,8 +2288,7 @@ static void __exit isdn_exit(void)
#endif
save_flags(flags);
cli();
if (isdn_net_rmall() < 0)
BUG();
isdn_net_exit();
isdn_tty_exit();
if (unregister_chrdev(ISDN_MAJOR, "isdn"))
......
......@@ -22,11 +22,11 @@
#undef ISDN_DEBUG_MODEM_DUMP
#undef ISDN_DEBUG_MODEM_VOICE
#undef ISDN_DEBUG_AT
#undef ISDN_DEBUG_NET_DUMP
#undef ISDN_DEBUG_NET_DIAL
#undef ISDN_DEBUG_NET_ICALL
#undef ISDN_DEBUG_STATCALLB
#undef ISDN_DEBUG_COMMAND
#define ISDN_DEBUG_NET_DUMP
#define ISDN_DEBUG_NET_DIAL
#define ISDN_DEBUG_NET_ICALL
#define ISDN_DEBUG_STATCALLB
#define ISDN_DEBUG_COMMAND
#ifdef ISDN_DEBUG_NET_DIAL
#define dbg_net_dial(arg...) printk(KERN_DEBUG arg)
......@@ -52,6 +52,8 @@ do { printk(KERN_WARNING "ISDN Bug at %s:%d\n", __FILE__, __LINE__); \
#define HERE printk("%s:%d (%s)\n", __FILE__, __LINE__, __FUNCTION__)
extern struct list_head isdn_net_devs;
/* Prototypes */
extern void isdn_MOD_INC_USE_COUNT(void);
extern void isdn_MOD_DEC_USE_COUNT(void);
......@@ -82,8 +84,6 @@ struct dial_info {
unsigned char *phone;
};
extern struct list_head isdn_net_devs;
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_all_eaz(int slot);
......@@ -100,8 +100,6 @@ extern void isdn_slot_set_usage(int slot, int usage);
extern char *isdn_slot_num(int slot);
extern int isdn_slot_m_idx(int slot);
extern void isdn_slot_set_m_idx(int slot, int midx);
extern void isdn_slot_set_rx_netdev(int sl, isdn_net_dev *nd);
extern void isdn_slot_set_st_netdev(int sl, isdn_net_dev *nd);
extern isdn_net_dev *isdn_slot_rx_netdev(int sl);
extern isdn_net_dev *isdn_slot_st_netdev(int sl);
extern void isdn_slot_set_idev(int sl, isdn_net_dev *);
extern isdn_net_dev *isdn_slot_idev(int sl);
extern int isdn_hard_header_len(void);
......@@ -39,7 +39,8 @@
*/
int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
static int
isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
{
struct net_device *ndev = concap -> net_dev;
isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
......@@ -58,7 +59,8 @@ int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
}
int isdn_concap_dl_connect_req(struct concap_proto *concap)
static int
isdn_concap_dl_connect_req(struct concap_proto *concap)
{
struct net_device *ndev = concap -> net_dev;
isdn_net_local *lp = (isdn_net_local *) ndev->priv;
......@@ -71,7 +73,8 @@ int isdn_concap_dl_connect_req(struct concap_proto *concap)
return ret;
}
int isdn_concap_dl_disconn_req(struct concap_proto *concap)
static int
isdn_concap_dl_disconn_req(struct concap_proto *concap)
{
IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name);
......@@ -98,7 +101,8 @@ struct concap_device_ops isdn_concap_demand_dial_dops = {
this sourcefile does not need to include any protocol specific header
files. For now:
*/
struct concap_proto * isdn_concap_new( int encap )
struct concap_proto *
isdn_concap_new( int encap )
{
switch ( encap ) {
case ISDN_NET_ENCAP_X25IFACE:
......@@ -158,7 +162,7 @@ isdn_x25_disconnected(isdn_net_local *lp)
pops -> disconn_ind(cprot);
}
int
static int
isdn_x25_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
/* At this point hard_start_xmit() passes control to the encapsulation
......@@ -237,13 +241,8 @@ isdn_x25_cleanup(isdn_net_dev *p)
restore_flags(flags);
}
void isdn_x25_realrm(isdn_net_dev *p)
{
if( p -> cprot && p -> cprot -> pops )
p -> cprot -> pops -> proto_del ( p -> cprot );
}
struct isdn_netif_ops isdn_x25_ops = {
.hard_start_xmit = isdn_x25_start_xmit,
.flags = IFF_NOARP | IFF_POINTOPOINT,
.type = ARPHRD_X25,
.receive = isdn_x25_receive,
......
......@@ -12,18 +12,4 @@ extern struct concap_device_ops isdn_concap_demand_dial_dops;
struct concap_proto *isdn_concap_new(int);
#ifdef CONFIG_ISDN_X25
extern struct isdn_netif_ops isdn_x25_ops;
int isdn_x25_start_xmit(struct sk_buff *skb, struct net_device *dev);
#else
static inline int
isdn_x25_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
return 0;
}
#endif
/* $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
......@@ -33,34 +33,6 @@
#include "isdn_concap.h"
#include "isdn_ciscohdlck.h"
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,
};
enum {
ST_CHARGE_NULL,
ST_CHARGE_GOT_CINF, /* got a first charge info */
ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
};
/* keep clear of ISDN_CMD_* and ISDN_STAT_* */
enum {
EV_NET_DIAL = 0x200,
EV_NET_TIMER_IN_DCONN = 0x201,
EV_NET_TIMER_IN_BCONN = 0x202,
EV_NET_TIMER_OUT_DCONN = 0x203,
EV_NET_TIMER_OUT_BCONN = 0x204,
EV_NET_TIMER_CB = 0x205,
};
LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */
/*
* Outline of new tbusy handling:
*
......@@ -90,30 +62,20 @@ LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */
/*
* Find out if the netdevice has been ifup-ed yet.
* For slaves, look at the corresponding master.
*/
static __inline__ int isdn_net_device_started(isdn_net_dev *n)
static inline int
isdn_net_device_started(isdn_net_dev *idev)
{
isdn_net_local *lp = &n->local;
struct net_device *dev;
if (lp->master)
dev = lp->master;
else
dev = &n->dev;
return netif_running(dev);
return netif_running(&idev->mlp->dev);
}
/*
* stop the network -> net_device queue.
* For slaves, stop the corresponding master interface.
*/
static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
static inline void
isdn_net_dev_stop_queue(isdn_net_dev *idev)
{
if (lp->master)
netif_stop_queue(lp->master);
else
netif_stop_queue(&lp->netdev->dev);
netif_stop_queue(&idev->mlp->dev);
}
/*
......@@ -121,104 +83,73 @@ static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
* master or slave) is busy. It's busy iff all (master and slave)
* queues are busy
*/
static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
static inline int
isdn_net_device_busy(isdn_net_dev *idev)
{
isdn_net_local *nlp;
isdn_net_dev *nd;
isdn_net_local *mlp = idev->mlp;
unsigned long flags;
int retval = 1;
if (!isdn_net_lp_busy(lp))
if (!isdn_net_dev_busy(idev))
return 0;
if (lp->master)
nd = ((isdn_net_local *) lp->master->priv)->netdev;
else
nd = lp->netdev;
spin_lock_irqsave(&nd->queue_lock, flags);
nlp = lp->next;
while (nlp != lp) {
if (!isdn_net_lp_busy(nlp)) {
spin_unlock_irqrestore(&nd->queue_lock, flags);
return 0;
spin_lock_irqsave(&mlp->online_lock, flags);
list_for_each_entry(idev, &mlp->online, online) {
if (!isdn_net_dev_busy(idev)) {
retval = 0;
break;
}
nlp = nlp->next;
}
spin_unlock_irqrestore(&nd->queue_lock, flags);
return 1;
spin_unlock_irqrestore(&mlp->online_lock, flags);
return retval;
}
static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp)
static inline
void isdn_net_inc_frame_cnt(isdn_net_dev *idev)
{
atomic_inc(&lp->frame_cnt);
if (isdn_net_device_busy(lp))
isdn_net_device_stop_queue(lp);
atomic_inc(&idev->frame_cnt);
if (isdn_net_device_busy(idev))
isdn_net_dev_stop_queue(idev);
}
static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp)
static inline void
isdn_net_dec_frame_cnt(isdn_net_dev *idev)
{
atomic_dec(&lp->frame_cnt);
atomic_dec(&idev->frame_cnt);
if (!(isdn_net_device_busy(lp))) {
if (!skb_queue_empty(&lp->super_tx_queue)) {
schedule_work(&lp->tqueue);
} else {
isdn_net_device_wake_queue(lp);
}
if (!isdn_net_device_busy(idev)) {
if (!skb_queue_empty(&idev->super_tx_queue))
tasklet_schedule(&idev->tlet);
else
isdn_net_dev_wake_queue(idev);
}
}
static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp)
static inline
void isdn_net_zero_frame_cnt(isdn_net_dev *idev)
{
atomic_set(&lp->frame_cnt, 0);
atomic_set(&idev->frame_cnt, 0);
}
/* For 2.2.x we leave the transmitter busy timeout at 2 secs, just
* to be safe.
* For 2.3.x we push it up to 20 secs, because call establishment
* (in particular callback) may take such a long time, and we
* don't want confusing messages in the log. However, there is a slight
* possibility that this large timeout will break other things like MPPP,
* which might rely on the tx timeout. If so, we'll find out this way...
*/
/* Prototypes */
#define ISDN_NET_TX_TIMEOUT (20*HZ)
int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
char *isdn_net_revision = "$Revision: 1.140.6.11 $";
static struct isdn_netif_ops *netif_ops[ISDN_NET_ENCAP_NR];
/* A packet has successfully been sent out. */
int
register_isdn_netif(int encap, struct isdn_netif_ops *ops)
isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c)
{
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR)
return -EINVAL;
isdn_net_local *mlp = idev->mlp;
if (netif_ops[encap])
return -EBUSY;
netif_ops[encap] = ops;
return 0;
}
int isdn_net_online(isdn_net_dev *idev)
{
return idev->dialstate == ST_ACTIVE;
isdn_net_dec_frame_cnt(idev);
mlp->stats.tx_packets++;
mlp->stats.tx_bytes += c->parm.length;
return 1;
}
/* Prototypes */
static int isdn_net_force_dial_lp(isdn_net_local *);
static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
static void do_dialout(isdn_net_local *lp);
static int isdn_net_set_encap(isdn_net_dev *p, int encap);
static int isdn_net_handle_event(isdn_net_local *lp, int pr, void *arg);
char *isdn_net_revision = "$Revision: 1.140.6.11 $";
/*
* Code for raw-networking over ISDN
*/
static void
isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
{
......@@ -232,1970 +163,437 @@ isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
dst_link_failure(skb);
}
/* Open/initialize the board. */
static int
isdn_net_open(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
int retval = 0;
if (!lp->ops)
return -ENODEV;
if (lp->ops->open)
retval = lp->ops->open(lp);
if (!retval)
return retval;
netif_start_queue(dev);
isdn_MOD_INC_USE_COUNT();
return 0;
}
/*
* unbind a net-interface (resets interface after an error)
*/
static void
isdn_net_unbind_channel(isdn_net_local * lp)
isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev)
{
isdn_net_dev *idev = lp->netdev;
ulong flags;
save_flags(flags);
cli();
if (lp->ops->unbind)
lp->ops->unbind(lp);
skb_queue_purge(&lp->super_tx_queue);
unsigned char *p = skb->nh.raw; /* hopefully, this was set correctly */
unsigned short proto = ntohs(skb->protocol);
int data_ofs;
struct ip_ports {
unsigned short source;
unsigned short dest;
} *ipp;
char addinfo[100];
if (!lp->master) { /* reset only master device */
/* Moral equivalent of dev_purge_queues():
BEWARE! This chunk of code cannot be called from hardware
interrupt handler. I hope it is true. --ANK
*/
qdisc_reset(lp->netdev->dev.qdisc);
}
idev->dialstate = ST_NULL;
if (idev->isdn_slot >= 0) {
isdn_slot_set_rx_netdev(idev->isdn_slot, NULL);
isdn_slot_set_st_netdev(idev->isdn_slot, NULL);
isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET);
data_ofs = ((p[0] & 15) * 4);
switch (proto) {
case ETH_P_IP:
switch (p[9]) {
case IPPROTO_ICMP:
strcpy(addinfo, "ICMP");
break;
case IPPROTO_TCP:
case IPPROTO_UDP:
ipp = (struct ip_ports *) (&p[data_ofs]);
sprintf(addinfo, "%s, port: %d -> %d",
p[9] == IPPROTO_TCP ? "TCP" : "UDP",
ntohs(ipp->source), ntohs(ipp->dest));
break;
default:
sprintf(addinfo, "type %d", p[9]);
}
idev->isdn_slot = -1;
printk(KERN_INFO
"OPEN: %u.%u.%u.%u -> %u.%u.%u.%u %s\n",
restore_flags(flags);
NIPQUAD(*(u32 *)(p + 12)), NIPQUAD(*(u32 *)(p + 16)),
addinfo);
break;
case ETH_P_ARP:
printk(KERN_INFO
"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
NIPQUAD(*(u32 *)(p + 14)), NIPQUAD(*(u32 *)(p + 24)));
break;
default:
printk(KERN_INFO "OPEN: unknown proto %#x\n", proto);
}
}
/*
* Assign an ISDN-channel to a net-interface
* this function is used to send supervisory data, i.e. data which was
* not received from the network layer, but e.g. frames from ipppd, CCP
* reset frames etc.
*/
static int
isdn_net_bind_channel(isdn_net_local *lp, int idx)
void
isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_dev *idev = lp->netdev;
int retval = 0;
unsigned long flags;
save_flags(flags);
cli();
idev->isdn_slot = idx;
isdn_slot_set_rx_netdev(idev->isdn_slot, lp->netdev);
isdn_slot_set_st_netdev(idev->isdn_slot, lp->netdev);
if (lp->ops->bind)
retval = lp->ops->bind(lp);
if (in_irq()) {
// we can't grab the lock from irq context,
// so we just queue the packet
skb_queue_tail(&idev->super_tx_queue, skb);
if (retval < 0)
isdn_net_unbind_channel(lp);
tasklet_schedule(&idev->tlet);
return;
}
restore_flags(flags);
return retval;
spin_lock_bh(&idev->xmit_lock);
if (!isdn_net_dev_busy(idev)) {
isdn_net_writebuf_skb(idev, skb);
} else {
skb_queue_tail(&idev->super_tx_queue, skb);
}
spin_unlock_bh(&idev->xmit_lock);
}
/*
* Perform auto-hangup for net-interfaces.
*
* auto-hangup:
* Increment idle-counter (this counter is reset on any incoming or
* outgoing packet), if counter exceeds configured limit either do a
* hangup immediately or - if configured - wait until just before the next
* charge-info.
* all frames sent from the (net) LL to a HL driver should go via this function
* it's serialized by the caller holding the idev->xmit_lock spinlock
*/
static void isdn_net_hup_timer(unsigned long data)
void isdn_net_writebuf_skb(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_local *lp = &idev->local;
isdn_net_local *mlp = idev->mlp;
int ret;
int len = skb->len; /* save len */
if (!isdn_net_online(idev)) {
/* before obtaining the lock the caller should have checked that
the lp isn't busy */
if (isdn_net_dev_busy(idev)) {
isdn_BUG();
return;
goto error;
}
dbg_net_dial("%s: huptimer %d, onhtime %d, chargetime %ld, chargeint %d\n",
l->name, l->huptimer, l->onhtime, l->chargetime, l->chargeint);
if (lp->onhtime == 0)
return;
if (idev->huptimer++ <= lp->onhtime)
goto mod_timer;
if ((lp->hupflags & (ISDN_MANCHARGE | ISDN_CHARGEHUP)) == (ISDN_MANCHARGE | ISDN_CHARGEHUP)) {
while (time_after(jiffies, idev->chargetime + idev->chargeint))
idev->chargetime += idev->chargeint;
if (time_after(jiffies, idev->chargetime + idev->chargeint - 2 * HZ)) {
if (idev->outgoing || lp->hupflags & ISDN_INHUP) {
isdn_net_hangup(idev);
return;
}
}
} else if (idev->outgoing) {
if (lp->hupflags & ISDN_CHARGEHUP) {
if (idev->charge_state != ST_CHARGE_HAVE_CINT) {
dbg_net_dial("%s: did not get CINT\n", lp->name);
isdn_net_hangup(idev);
return;
} else if (time_after(jiffies, idev->chargetime + idev->chargeint)) {
dbg_net_dial("%s: chtime = %lu, chint = %d\n",
lp->name, lp->chargetime, lp->chargeint);
isdn_net_hangup(idev);
return;
if (!isdn_net_online(idev)) {
isdn_BUG();
goto error;
}
ret = isdn_slot_write(idev->isdn_slot, skb);
if (ret != len) {
/* we should never get here */
printk(KERN_WARNING "%s: HL driver queue full\n", idev->name);
goto error;
}
} else if (lp->hupflags & ISDN_INHUP) {
isdn_net_hangup(idev);
idev->transcount += len;
isdn_net_inc_frame_cnt(idev);
return;
}
mod_timer:
mod_timer(&idev->hup_timer, idev->hup_timer.expires + HZ);
}
static void isdn_net_lp_disconnected(isdn_net_local *lp)
{
isdn_net_rm_from_bundle(lp);
error:
dev_kfree_skb(skb);
mlp->stats.tx_errors++;
}
static void isdn_net_connected(isdn_net_local *lp)
static void
isdn_net_dial_slave(isdn_net_local *mlp)
{
isdn_net_dev *idev = lp->netdev;
isdn_net_dev *idev;
idev->dialstate = ST_ACTIVE;
idev->hup_timer.expires = jiffies + HZ;
add_timer(&idev->hup_timer);
if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
if (lp->master) { /* is lp a slave? */
isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
isdn_net_add_to_bundle(nd, lp);
list_for_each_entry(idev, &mlp->slaves, slaves) {
if (!isdn_net_bound(idev)) {
isdn_net_dial(idev);
break;
}
}
printk(KERN_INFO "isdn_net: %s connected\n", idev->name);
/* If first Chargeinfo comes before B-Channel connect,
* we correct the timestamp here.
*/
idev->chargetime = jiffies;
/* reset dial-timeout */
idev->dialstarted = 0;
idev->dialwait_timer = 0;
idev->transcount = 0;
idev->cps = 0;
idev->last_jiffies = jiffies;
if (lp->ops->connected)
lp->ops->connected(lp);
else
isdn_net_device_wake_queue(lp);
}
/*
* 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.
* Based on cps-calculation, check if device is overloaded.
* If so, and if a slave exists, trigger dialing for it.
* If any slave is online, deliver packets using a simple round robin
* scheme.
*
* Return: 0 on success, !0 on failure.
*/
int
isdn_net_stat_callback(int idx, isdn_ctrl *c)
{
isdn_net_dev *p = isdn_slot_st_netdev(idx);
isdn_net_local *lp;
int cmd = c->command;
if (!p) {
HERE;
return 0;
}
lp = &p->local;
return isdn_net_handle_event(lp, cmd, c);
}
static void
isdn_net_dial_timer(unsigned long data)
int
isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
isdn_net_dev *idev;
isdn_net_local *mlp = ndev->priv;
isdn_net_handle_event(&idev->local, idev->dial_event, NULL);
}
/* Initiate dialout. Set phone-number-pointer to first number
* of interface.
*/
static void
init_dialout(isdn_net_local *lp)
{
isdn_net_dev *idev = lp->netdev;
ndev->trans_start = jiffies;
idev->dial = 0;
if (list_empty(&mlp->online))
return isdn_net_autodial(skb, ndev);
if (lp->dialtimeout > 0 &&
(idev->dialstarted == 0 ||
time_after(jiffies, idev->dialstarted + lp->dialtimeout + lp->dialwait))) {
idev->dialstarted = jiffies;
idev->dialwait_timer = 0;
idev = isdn_net_get_locked_dev(mlp);
if (!idev) {
printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
netif_stop_queue(ndev);
return 1;
}
lp->dialretry = 0;
do_dialout(lp);
}
/* we have our idev locked from now on */
/* Setup interface, dial current phone-number, switch to next number.
* If list of phone-numbers is exhausted, increment
* retry-counter.
*/
static void
do_dialout(isdn_net_local *lp)
{
isdn_net_dev *idev = lp->netdev;
int i;
unsigned long flags;
struct isdn_net_phone *phone;
struct dial_info dial = {
.l2_proto = lp->l2_proto,
.l3_proto = lp->l3_proto,
.si1 = 7,
.si2 = 0,
.msn = lp->msn,
};
if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)
return;
spin_lock_irqsave(&lp->lock, flags);
if (list_empty(&lp->phone[1])) {
spin_unlock_irqrestore(&lp->lock, flags);
return;
}
i = 0;
list_for_each_entry(phone, &lp->phone[1], list) {
if (i++ == idev->dial)
goto found;
}
/* otherwise start in front */
phone = list_entry(lp->phone[1].next, struct isdn_net_phone, list);
idev->dial = 0;
lp->dialretry++;
isdn_net_writebuf_skb(idev, skb);
spin_unlock_bh(&idev->xmit_lock);
found:
idev->dial++;
dial.phone = phone->num;
spin_unlock_irqrestore(&lp->lock, flags);
if (lp->dialretry > lp->dialmax) {
if (lp->dialtimeout == 0) {
idev->dialwait_timer = jiffies + lp->dialwait;
idev->dialstarted = 0;
}
isdn_net_hangup(idev);
return;
}
if(lp->dialtimeout > 0 &&
time_after(jiffies, idev->dialstarted + lp->dialtimeout)) {
idev->dialwait_timer = jiffies + lp->dialwait;
idev->dialstarted = 0;
isdn_net_hangup(idev);
return;
}
/*
* Switch to next number or back to start if at end of list.
/* the following stuff is here for backwards compatibility.
* in future, start-up and hangup of slaves (based on current load)
* should move to userspace and get based on an overall cps
* calculation
*/
isdn_slot_dial(idev->isdn_slot, &dial);
idev->huptimer = 0;
idev->outgoing = 1;
if (idev->chargeint)
idev->charge_state = ST_CHARGE_HAVE_CINT;
else
idev->charge_state = ST_CHARGE_NULL;
if (lp->cbdelay && (lp->flags & ISDN_NET_CBOUT)) {
idev->dial_timer.expires = jiffies + lp->cbdelay;
idev->dial_event = EV_NET_TIMER_CB;
} else {
idev->dial_timer.expires = jiffies + 10 * HZ;
idev->dial_event = EV_NET_TIMER_OUT_DCONN;
if (jiffies != idev->last_jiffies) {
idev->cps = idev->transcount * HZ / (jiffies - idev->last_jiffies);
idev->last_jiffies = jiffies;
idev->transcount = 0;
}
idev->dialstate = ST_OUT_WAIT_DCONN;
add_timer(&idev->dial_timer);
}
if (dev->net_verbose > 3)
printk(KERN_DEBUG "%s: %d bogocps\n", idev->name, idev->cps);
/* For EV_NET_DIAL, returns 1 if timer callback is needed
* For ISDN_STAT_*, returns 1 if event was for us
*/
static int
isdn_net_handle_event(isdn_net_local *lp, int pr, void *arg)
{
isdn_net_dev *idev = lp->netdev;
isdn_ctrl *c = arg;
isdn_ctrl cmd;
dbg_net_dial("%s: dialstate=%d pr=%#x\n", lp->name, lp->dialstate,pr);
switch (idev->dialstate) {
case ST_ACTIVE:
switch (pr) {
case ISDN_STAT_BSENT:
/* A packet has successfully been sent out */
isdn_net_dec_frame_cnt(lp);
lp->stats.tx_packets++;
lp->stats.tx_bytes += c->parm.length;
return 1;
case ISDN_STAT_DHUP:
if (lp->ops->disconnected)
lp->ops->disconnected(lp);
isdn_net_lp_disconnected(lp);
isdn_slot_all_eaz(idev->isdn_slot);
printk(KERN_INFO "%s: remote hangup\n", idev->name);
printk(KERN_INFO "%s: Chargesum is %d\n", idev->name,
idev->charge);
isdn_net_unbind_channel(lp);
return 1;
case ISDN_STAT_CINF:
/* Charge-info from TelCo. Calculate interval between
* charge-infos and set timestamp for last info for
* usage by isdn_net_autohup()
*/
idev->charge++;
switch (idev->charge_state) {
case ST_CHARGE_NULL:
idev->charge_state = ST_CHARGE_GOT_CINF;
break;
case ST_CHARGE_GOT_CINF:
idev->charge_state = ST_CHARGE_HAVE_CINT;
/* fall through */
case ST_CHARGE_HAVE_CINT:
idev->chargeint = jiffies - idev->chargetime - 2 * HZ;
break;
}
idev->chargetime = jiffies;
dbg_net_dial("%s: got CINF\n", lp->name);
return 1;
}
break;
case ST_OUT_WAIT_DCONN:
switch (pr) {
case EV_NET_TIMER_OUT_DCONN:
/* try again */
do_dialout(lp);
return 1;
case EV_NET_TIMER_CB:
/* Remote does callback. Hangup after cbdelay,
* then wait for incoming call */
printk(KERN_INFO "%s: hangup waiting for callback ...\n", idev->name);
isdn_net_hangup(idev);
return 1;
case ISDN_STAT_DCONN:
/* Got D-Channel-Connect, send B-Channel-request */
del_timer(&idev->dial_timer);
idev->dialstate = ST_OUT_WAIT_BCONN;
isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
idev->dial_timer.expires = jiffies + 10 * HZ;
idev->dial_event = EV_NET_TIMER_OUT_BCONN;
add_timer(&idev->dial_timer);
return 1;
case ISDN_STAT_DHUP:
del_timer(&idev->dial_timer);
isdn_slot_all_eaz(idev->isdn_slot);
printk(KERN_INFO "%s: remote hangup\n", idev->name);
isdn_net_unbind_channel(lp);
return 1;
}
break;
case ST_OUT_WAIT_BCONN:
switch (pr) {
case EV_NET_TIMER_OUT_BCONN:
/* try again */
do_dialout(lp);
return 1;
case ISDN_STAT_BCONN:
del_timer(&idev->dial_timer);
isdn_slot_set_usage(idev->isdn_slot, isdn_slot_usage(idev->isdn_slot) | ISDN_USAGE_OUTGOING);
isdn_net_connected(lp);
return 1;
case ISDN_STAT_DHUP:
del_timer(&idev->dial_timer);
isdn_slot_all_eaz(idev->isdn_slot);
printk(KERN_INFO "%s: remote hangup\n", idev->name);
isdn_net_unbind_channel(lp);
return 1;
}
break;
case ST_IN_WAIT_DCONN:
switch (pr) {
case EV_NET_TIMER_IN_DCONN:
isdn_net_hangup(idev);
return 1;
case ISDN_STAT_DCONN:
del_timer(&idev->dial_timer);
idev->dialstate = ST_IN_WAIT_BCONN;
isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
idev->dial_timer.expires = jiffies + 10 * HZ;
idev->dial_event = EV_NET_TIMER_IN_BCONN;
add_timer(&idev->dial_timer);
return 1;
case ISDN_STAT_DHUP:
del_timer(&idev->dial_timer);
isdn_slot_all_eaz(idev->isdn_slot);
printk(KERN_INFO "%s: remote hangup\n", idev->name);
isdn_net_unbind_channel(lp);
return 1;
}
break;
case ST_IN_WAIT_BCONN:
switch (pr) {
case EV_NET_TIMER_IN_BCONN:
isdn_net_hangup(idev);
break;
case ISDN_STAT_BCONN:
del_timer(&idev->dial_timer);
isdn_slot_set_rx_netdev(idev->isdn_slot, idev);
isdn_net_connected(lp);
return 1;
case ISDN_STAT_DHUP:
del_timer(&idev->dial_timer);
isdn_slot_all_eaz(idev->isdn_slot);
printk(KERN_INFO "%s: remote hangup\n", idev->name);
isdn_net_unbind_channel(lp);
return 1;
if (idev->cps > mlp->triggercps) {
if (!idev->sqfull) {
/* First time overload: set timestamp only */
idev->sqfull = 1;
idev->sqfull_stamp = jiffies;
} else {
/* subsequent overload: if slavedelay exceeded, start dialing */
if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
isdn_net_dial_slave(mlp);
}
break;
case ST_WAIT_BEFORE_CB:
switch (pr) {
case EV_NET_TIMER_CB:
/* Callback Delay */
init_dialout(lp);
return 1;
}
break;
default:
isdn_BUG();
break;
}
printk("NOT HANDLED?\n");
return 0;
}
/*
* Perform hangup for a net-interface.
*/
void
isdn_net_hangup(isdn_net_dev *idev)
{
isdn_net_local *lp = &idev->local;
isdn_ctrl cmd;
del_timer_sync(&idev->hup_timer);
if (!isdn_net_bound(idev))
return;
// FIXME ugly and recursive
if (lp->slave != NULL) {
isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
isdn_net_dev *sidev = slp->netdev;
if (isdn_net_bound(sidev)) {
printk(KERN_INFO
"isdn_net: hang up slave %s before %s\n",
sidev->name, idev->name);
isdn_net_hangup(sidev);
}
}
printk(KERN_INFO "isdn_net: local hangup %s\n", idev->name);
if (lp->ops->disconnected)
lp->ops->disconnected(lp);
isdn_net_lp_disconnected(lp);
isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
printk(KERN_INFO "%s: Chargesum is %d\n", idev->name, idev->charge);
isdn_slot_all_eaz(idev->isdn_slot);
isdn_net_unbind_channel(lp);
}
void
isdn_net_hangup_all()
{
struct list_head *l;
list_for_each(l, &isdn_net_devs) {
isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
isdn_net_hangup(p);
}
}
typedef struct {
unsigned short source;
unsigned short dest;
} ip_ports;
static void
isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
{
isdn_net_dev *idev = lp->netdev;
u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
unsigned short proto = ntohs(skb->protocol);
int data_ofs;
ip_ports *ipp;
char addinfo[100];
addinfo[0] = '\0';
/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
/* fall back to old isdn_net_log_packet method() */
char * buf = skb->data;
printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, idev->name);
p = buf;
proto = ETH_P_IP;
switch (lp->p_encap) {
case ISDN_NET_ENCAP_IPTYP:
proto = ntohs(*(unsigned short *) &buf[0]);
p = &buf[2];
break;
case ISDN_NET_ENCAP_ETHER:
proto = ntohs(*(unsigned short *) &buf[12]);
p = &buf[14];
break;
case ISDN_NET_ENCAP_CISCOHDLC:
proto = ntohs(*(unsigned short *) &buf[2]);
p = &buf[4];
break;
case ISDN_NET_ENCAP_SYNCPPP:
proto = ntohs(skb->protocol);
p = &buf[IPPP_MAX_HEADER];
break;
}
}
data_ofs = ((p[0] & 15) * 4);
switch (proto) {
case ETH_P_IP:
switch (p[9]) {
case 1:
strcpy(addinfo, " ICMP");
break;
case 2:
strcpy(addinfo, " IGMP");
break;
case 4:
strcpy(addinfo, " IPIP");
break;
case 6:
ipp = (ip_ports *) (&p[data_ofs]);
sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
ntohs(ipp->dest));
break;
case 8:
strcpy(addinfo, " EGP");
break;
case 12:
strcpy(addinfo, " PUP");
break;
case 17:
ipp = (ip_ports *) (&p[data_ofs]);
sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
ntohs(ipp->dest));
break;
case 22:
strcpy(addinfo, " IDP");
break;
}
printk(KERN_INFO
"OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
p[12], p[13], p[14], p[15],
p[16], p[17], p[18], p[19],
addinfo);
break;
case ETH_P_ARP:
printk(KERN_INFO
"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
p[14], p[15], p[16], p[17],
p[24], p[25], p[26], p[27]);
break;
}
}
/*
* this function is used to send supervisory data, i.e. data which was
* not received from the network layer, but e.g. frames from ipppd, CCP
* reset frames etc.
*/
void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb)
{
if (in_irq()) {
// we can't grab the lock from irq context,
// so we just queue the packet
skb_queue_tail(&lp->super_tx_queue, skb);
schedule_work(&lp->tqueue);
return;
}
spin_lock_bh(&lp->xmit_lock);
if (!isdn_net_lp_busy(lp)) {
isdn_net_writebuf_skb(lp, skb);
} else {
skb_queue_tail(&lp->super_tx_queue, skb);
}
spin_unlock_bh(&lp->xmit_lock);
}
/*
* called from tq_immediate
*/
static void isdn_net_softint(void *private)
{
isdn_net_local *lp = private;
struct sk_buff *skb;
spin_lock_bh(&lp->xmit_lock);
while (!isdn_net_lp_busy(lp)) {
skb = skb_dequeue(&lp->super_tx_queue);
if (!skb)
break;
isdn_net_writebuf_skb(lp, skb);
}
spin_unlock_bh(&lp->xmit_lock);
}
/*
* all frames sent from the (net) LL to a HL driver should go via this function
* it's serialized by the caller holding the lp->xmit_lock spinlock
*/
void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb)
{
isdn_net_dev *idev = lp->netdev;
int ret;
int len = skb->len; /* save len */
/* before obtaining the lock the caller should have checked that
the lp isn't busy */
if (isdn_net_lp_busy(lp)) {
isdn_BUG();
goto error;
}
if (!isdn_net_online(idev)) {
isdn_BUG();
goto error;
}
ret = isdn_slot_write(idev->isdn_slot, skb);
if (ret != len) {
/* we should never get here */
printk(KERN_WARNING "%s: HL driver queue full\n", idev->name);
goto error;
}
idev->transcount += len;
isdn_net_inc_frame_cnt(lp);
return;
error:
dev_kfree_skb(skb);
lp->stats.tx_errors++;
}
/*
* Helper function for isdn_net_start_xmit.
* When called, the connection is already established.
* Based on cps-calculation, check if device is overloaded.
* If so, and if a slave exists, trigger dialing for it.
* If any slave is online, deliver packets using a simple round robin
* scheme.
*
* Return: 0 on success, !0 on failure.
*/
static int
isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
{
isdn_net_dev *nd, *idev;
isdn_net_local *slp;
isdn_net_local *lp = ndev->priv;
int retv = 0;
if (lp->master) {
isdn_BUG();
dev_kfree_skb(skb);
return 0;
}
/* For the other encaps the header has already been built */
if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
return isdn_ppp_xmit(skb, ndev);
}
nd = ((isdn_net_local *) ndev->priv)->netdev;
lp = isdn_net_get_locked_lp(nd);
if (!lp) {
printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
return 1;
}
idev = lp->netdev;
/* we have our lp locked from now on */
/* Reset hangup-timeout */
idev->huptimer = 0; // FIXME?
isdn_net_writebuf_skb(lp, skb);
spin_unlock_bh(&lp->xmit_lock);
/* the following stuff is here for backwards compatibility.
* in future, start-up and hangup of slaves (based on current load)
* should move to userspace and get based on an overall cps
* calculation
*/
if (jiffies != idev->last_jiffies) {
idev->cps = idev->transcount * HZ / (jiffies - idev->last_jiffies);
idev->last_jiffies = jiffies;
idev->transcount = 0;
}
if (dev->net_verbose > 3)
printk(KERN_DEBUG "%s: %d bogocps\n", idev->name, idev->cps);
if (idev->cps > lp->triggercps) {
if (lp->slave) {
if (!lp->sqfull) {
/* First time overload: set timestamp only */
lp->sqfull = 1;
lp->sqfull_stamp = jiffies;
} else {
/* subsequent overload: if slavedelay exceeded, start dialing */
if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) {
slp = lp->slave->priv;
if (!isdn_net_bound(slp->netdev)) {
isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
}
}
}
}
} else {
if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) {
lp->sqfull = 0;
} else {
if (idev->sqfull && time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay + 10 * HZ)) {
idev->sqfull = 0;
}
/* this is a hack to allow auto-hangup for slaves on moderate loads */
nd->queue = &nd->local;
}
return retv;
}
static void
isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
if (!skb)
return;
if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
if (pullsize > 0) {
printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
skb_pull(skb, pullsize);
}
}
}
void isdn_net_tx_timeout(struct net_device * ndev)
{
isdn_net_local *lp = ndev->priv;
isdn_net_dev *idev = lp->netdev;
printk(KERN_WARNING "isdn_tx_timeout dev %s %d\n",
ndev->name, idev->dialstate);
netif_wake_queue(ndev);
}
static int
isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_local *lp = ndev->priv;
isdn_net_dev *idev = lp->netdev;
if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO))
goto discard;
if (idev->dialwait_timer <= 0)
if (idev->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, idev->dialstarted + lp->dialtimeout + lp->dialwait))
idev->dialwait_timer = idev->dialstarted + lp->dialtimeout + lp->dialwait;
if (idev->dialwait_timer > 0) {
if(time_before(jiffies, idev->dialwait_timer))
goto discard;
idev->dialwait_timer = 0;
}
if (isdn_net_force_dial_lp(lp) < 0)
goto discard;
/* Log packet, which triggered dialing */
if (dev->net_verbose)
isdn_net_log_skb(skb, lp);
netif_stop_queue(ndev);
return 1;
discard:
isdn_net_unreachable(ndev, skb, "dial rejected");
dev_kfree_skb(skb);
return 0;
}
/*
* Try sending a packet.
* If this interface isn't connected to a ISDN-Channel, find a free channel,
* and start dialing.
*/
static int
isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_local *lp = ndev->priv;
isdn_net_dev *idev = lp->netdev;
int retval;
if (lp->p_encap == ISDN_NET_ENCAP_X25IFACE)
return isdn_x25_start_xmit(skb, ndev);
/* auto-dialing xmit function */
isdn_net_adjust_hdr(skb, ndev);
isdn_dumppkt("S:", skb->data, skb->len, 40);
if (!isdn_net_bound(idev))
return isdn_net_autodial(skb, ndev);
/* Device is bound to an ISDN channel */
ndev->trans_start = jiffies;
if (idev->dialstate != ST_ACTIVE) {
netif_stop_queue(ndev);
return 1;
}
/* ISDN connection is established, try sending */
retval = isdn_net_xmit(ndev, skb);
if (retval)
netif_stop_queue(ndev);
return retval;
}
/*
* Shutdown a net-interface.
*/
static int
isdn_net_close(struct net_device *dev)
{
struct net_device *p;
isdn_net_local *lp = dev->priv;
if (lp->ops->close)
lp->ops->close(lp);
netif_stop_queue(dev);
for (p = lp->slave; p; p = ((isdn_net_local *) p->priv)->slave)
isdn_net_hangup(p->priv);
isdn_net_hangup(dev->priv);
isdn_MOD_DEC_USE_COUNT();
return 0;
}
/*
* Get statistics
*/
static struct net_device_stats *
isdn_net_get_stats(struct net_device *dev)
{
isdn_net_local *lp = (isdn_net_local *) dev->priv;
return &lp->stats;
}
/*
* Got a packet from ISDN-Channel.
*/
static void
isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
{
isdn_net_local *lp = (isdn_net_local *) ndev->priv;
isdn_net_dev *idev = lp->netdev;
isdn_net_local *olp = lp; /* original 'lp' */
idev->transcount += skb->len;
lp->stats.rx_packets++;
lp->stats.rx_bytes += skb->len;
if (lp->master) {
/* Bundling: If device is a slave-device, deliver to master, also
* handle master's statistics and hangup-timeout
*/
ndev = lp->master;
lp = (isdn_net_local *) ndev->priv;
lp->stats.rx_packets++;
lp->stats.rx_bytes += skb->len;
}
skb->dev = ndev;
skb->pkt_type = PACKET_HOST;
skb->mac.raw = skb->data;
isdn_dumppkt("R:", skb->data, skb->len, 40);
lp->ops->receive(lp->netdev, olp, skb);
}
/*
* A packet arrived via ISDN. Search interface-chain for a corresponding
* interface. If found, deliver packet to receiver-function and return 1,
* else return 0.
*/
int
isdn_net_rcv_skb(int idx, struct sk_buff *skb)
{
isdn_net_dev *idev = isdn_slot_rx_netdev(idx);
if (!idev)
return 0;
if (!isdn_net_online(idev))
return 0;
isdn_net_receive(&idev->dev, skb);
return 0;
}
/*
* Interface-setup. (just after registering a new interface)
*/
static int
isdn_net_init(struct net_device *ndev)
{
/* Setup the generic properties */
ndev->mtu = 1500;
ndev->tx_queue_len = 10;
ndev->open = &isdn_net_open;
ndev->hard_start_xmit = &isdn_net_start_xmit;
ndev->hard_header_len = ETH_HLEN + isdn_hard_header_len();
ndev->stop = &isdn_net_close;
ndev->get_stats = &isdn_net_get_stats;
return 0;
}
static int
isdn_net_do_callback(isdn_net_local *lp)
{
isdn_net_dev *idev = lp->netdev;
int slot;
/*
* Is the state MANUAL?
* If so, no callback can be made,
* so reject actively.
*/
if (ISDN_NET_DIALMODE(*lp) == 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, lp->l2_proto, lp->l3_proto,
idev->pre_device, idev->pre_channel, lp->msn);
if (slot < 0)
goto err;
if (isdn_net_bind_channel(lp, slot) < 0)
goto err;
/* Setup dialstate. */
idev->dial_timer.expires = jiffies + lp->cbdelay;
idev->dial_event = EV_NET_TIMER_CB;
add_timer(&idev->dial_timer);
idev->dialstate = ST_WAIT_BEFORE_CB;
/* Initiate dialing by returning 2 or 4 */
return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
err:
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;
struct list_head *l;
struct isdn_net_phone *n;
ulong flags;
char nr[32];
char *my_eaz;
int retval;
isdn_ctrl cmd;
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 *lp = &idev->local;
/* check acceptable call types for DOV */
dbg_net_icall("n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
lp->name, lp->msn, lp->flags, lp->dialstate);
my_eaz = isdn_slot_map_eaz2msn(slot, lp->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",
lp->pre_device, lp->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 (lp->flags & ISDN_NET_SECURE) {
spin_lock_irqsave(&lp->lock, flags);
list_for_each_entry(n, &lp->phone[0], list) {
if (!isdn_msncmp(nr, n->num)) {
spin_unlock_irqrestore(&lp->lock, flags);
goto found;
}
}
spin_unlock_irqrestore(&lp->lock, flags);
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(*lp) == 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;
}
/* Interface is up, now see if it's a slave. If so, see if
* it's master and parent slave is online. If not, reject the call.
*/
if (lp->master) {
isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
printk(KERN_DEBUG "ICALLslv: %s\n", idev->name);
printk(KERN_DEBUG "master=%s\n", mlp->netdev->name);
if (isdn_net_bound(mlp->netdev)) {
printk(KERN_DEBUG "master online\n");
/* Master is online, find parent-slave (master if first slave) */
while (mlp->slave) {
if ((isdn_net_local *) mlp->slave->priv == lp)
break;
mlp = (isdn_net_local *) mlp->slave->priv;
}
} else
printk(KERN_DEBUG "master offline\n");
/* Found parent, if it's offline iterate next device */
printk(KERN_DEBUG "mlpf: %d\n", isdn_net_bound(mlp->netdev));
if (!isdn_net_bound(mlp->netdev)) {
continue;
}
}
if (lp->flags & ISDN_NET_CALLBACK) {
retval = isdn_net_do_callback(lp);
restore_flags(flags);
return retval;
}
printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", idev->name,
nr, eaz);
strcpy(isdn_slot_num(idx), nr);
isdn_slot_set_usage(idx, (isdn_slot_usage(idx) & ISDN_USAGE_EXCLUSIVE) | ISDN_USAGE_NET);
isdn_net_bind_channel(lp, idx);
idev->outgoing = 0;
idev->huptimer = 0;
idev->charge_state = ST_CHARGE_NULL;
/* Got incoming Call, setup L2 and L3 protocols,
* then wait for D-Channel-connect
*/
cmd.arg = lp->l2_proto << 8;
isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL2, &cmd);
cmd.arg = lp->l3_proto << 8;
isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL3, &cmd);
idev->dial_timer.expires = jiffies + 15 * HZ;
idev->dial_event = EV_NET_TIMER_IN_DCONN;
add_timer(&idev->dial_timer);
idev->dialstate = ST_IN_WAIT_DCONN;
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;
}
/*
* Search list of net-interfaces for an interface with given name.
*/
isdn_net_dev *
isdn_net_findif(char *name)
{
struct list_head *l;
list_for_each(l, &isdn_net_devs) {
isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
if (!strcmp(p->name, name))
return p;
}
return NULL;
}
/*
* Force a net-interface to dial out.
* This is called from the userlevel-routine below or
* from isdn_net_start_xmit().
*/
static int
isdn_net_force_dial_lp(isdn_net_local *lp)
{
isdn_net_dev *idev = lp->netdev;
int slot;
unsigned long flags;
if (isdn_net_bound(idev))
return -EBUSY;
save_flags(flags);
cli();
if (idev->exclusive >= 0)
slot = idev->exclusive;
else
slot = isdn_get_free_slot(ISDN_USAGE_NET, lp->l2_proto,
lp->l3_proto, idev->pre_device,
idev->pre_channel, lp->msn);
if (slot < 0)
goto err;
if (isdn_net_bind_channel(lp, slot) < 0)
goto err;;
/* Initiate dialing */
restore_flags(flags);
init_dialout(lp);
return 0;
err:
restore_flags(flags);
return -EAGAIN;
}
/*
* This is called from certain upper protocol layers (multilink ppp
* and x25iface encapsulation module) that want to initiate dialing
* themselves.
*/
int
isdn_net_dial_req(isdn_net_local * lp)
{
/* is there a better error code? */
if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
return isdn_net_force_dial_lp(lp);
}
/*
* Force a net-interface to dial out.
* This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
*/
int
isdn_net_force_dial(char *name)
{
isdn_net_dev *p = isdn_net_findif(name);
if (!p)
return -ENODEV;
return (isdn_net_force_dial_lp(&p->local));
}
/*
* Allocate a new network-interface and initialize its data structures.
*/
int
isdn_net_new(char *name, struct net_device *master)
{
int retval;
isdn_net_dev *netdev;
/* Avoid creating an existing interface */
if (isdn_net_findif(name)) {
printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
return -EEXIST;
}
if (!(netdev = kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
return -ENOMEM;
}
memset(netdev, 0, sizeof(isdn_net_dev));
strcpy(netdev->name, name);
strcpy(netdev->dev.name, name);
netdev->dev.priv = &netdev->local;
netdev->dev.init = isdn_net_init;
if (master) {
/* Device shall be a slave */
struct net_device *p = (((isdn_net_local *) master->priv)->slave);
struct net_device *q = master;
netdev->local.master = master;
/* Put device at end of slave-chain */
while (p) {
q = p;
p = (((isdn_net_local *) p->priv)->slave);
}
((isdn_net_local *) q->priv)->slave = &(netdev->dev);
} else {
/* Device shall be a master */
/*
* Watchdog timer (currently) for master only.
*/
netdev->dev.tx_timeout = isdn_net_tx_timeout;
netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT;
retval = register_netdev(&netdev->dev);
if (retval) {
printk(KERN_WARNING "isdn_net: Could not register net-device\n");
kfree(netdev);
return retval;
}
}
netdev->local.magic = ISDN_NET_MAGIC;
netdev->queue = &netdev->local;
spin_lock_init(&netdev->queue_lock);
netdev->local.last = &netdev->local;
netdev->local.netdev = netdev;
netdev->local.next = &netdev->local;
INIT_WORK(&netdev->local.tqueue, isdn_net_softint, &netdev->local);
spin_lock_init(&netdev->local.xmit_lock);
netdev->isdn_slot = -1;
netdev->pre_device = -1;
netdev->pre_channel = -1;
netdev->exclusive = -1;
netdev->ppp_slot = -1;
netdev->pppbind = -1;
netdev->local.p_encap = -1;
skb_queue_head_init(&netdev->local.super_tx_queue);
netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
netdev->local.triggercps = 6000;
netdev->local.slavedelay = 10 * HZ;
netdev->local.hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */
netdev->local.onhtime = 10; /* Default hangup-time for saving costs
of those who forget configuring this */
netdev->local.dialmax = 1;
netdev->local.flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL; /* Hangup before Callback, manual dial */
netdev->local.cbdelay = 5 * HZ; /* Wait 5 secs before Callback */
netdev->local.dialtimeout = -1; /* Infinite Dial-Timeout */
netdev->local.dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
netdev->dialstarted = 0; /* Jiffies of last dial-start */
netdev->dialwait_timer = 0; /* Jiffies of earliest next dial-start */
init_timer(&netdev->dial_timer);
netdev->dial_timer.data = (unsigned long) netdev;
netdev->dial_timer.function = isdn_net_dial_timer;
init_timer(&netdev->hup_timer);
netdev->hup_timer.data = (unsigned long) netdev;
netdev->hup_timer.function = isdn_net_hup_timer;
spin_lock_init(&netdev->local.lock);
INIT_LIST_HEAD(&netdev->local.phone[0]);
INIT_LIST_HEAD(&netdev->local.phone[1]);
isdn_net_set_encap(netdev, ISDN_NET_ENCAP_RAWIP);
/* Put into to netdev-chain */
list_add(&netdev->global_list, &isdn_net_devs);
return 0;
}
int
isdn_net_newslave(char *parm)
{
char *p = strchr(parm, ',');
isdn_net_dev *m;
/* Slave-Name MUST not be empty */
if (!p || !p[1])
return -EINVAL;
*p = 0;
/* Master must already exist */
if (!(m = isdn_net_findif(parm)))
return -ESRCH;
/* Master must be a real interface, not a slave */
if (m->local.master)
return -ENXIO;
/* Master must not be started yet */
if (isdn_net_device_started(m))
return -EBUSY;
return isdn_net_new(p+1, &m->dev);
}
static int
isdn_net_set_encap(isdn_net_dev *p, int encap)
{
isdn_net_local *lp = &p->local;
int retval = 0;
if (lp->p_encap == encap){
/* nothing to do */
retval = 0;
goto out;
}
if (isdn_net_device_started(p)) {
retval = -EBUSY;
goto out;
}
if (lp->ops && lp->ops->cleanup)
lp->ops->cleanup(lp);
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR) {
lp->p_encap = -1;
lp->ops = NULL;
retval = -EINVAL;
goto out;
}
lp->p_encap = encap;
lp->ops = netif_ops[encap];
p->dev.hard_header = lp->ops->hard_header;
p->dev.do_ioctl = lp->ops->do_ioctl;
p->dev.flags = lp->ops->flags;
p->dev.type = lp->ops->type;
p->dev.addr_len = lp->ops->addr_len;
if (lp->ops->init)
retval = lp->ops->init(lp);
if (retval != 0) {
lp->p_encap = -1;
lp->ops = NULL;
}
out:
return retval;
}
static int
isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg)
{
isdn_net_local *lp = &idev->local;
int i, retval;
int drvidx = -1;
int chidx = -1;
char drvid[25];
strncpy(drvid, cfg->drvid, 24);
drvid[24] = 0;
if (cfg->exclusive && !strlen(drvid)) {
/* If we want to bind exclusively, need to specify drv/chan */
retval = -ENODEV;
goto out;
}
if (strlen(drvid)) {
/* A bind has been requested ... */
char *c = strchr(drvid, ',');
if (!c) {
retval = -ENODEV;
goto out;
}
/* The channel-number is appended to the driver-Id with a comma */
*c = 0;
chidx = simple_strtol(c + 1, NULL, 10);
for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
/* Lookup driver-Id in array */
if (!strcmp(dev->drvid[i], drvid)) {
drvidx = i;
break;
}
}
if (drvidx == -1 || chidx == -1) {
/* Either driver-Id or channel-number invalid */
retval = -ENODEV;
goto out;
}
}
if (cfg->exclusive == (idev->exclusive >= 0) &&
drvidx == idev->pre_device && chidx == idev->pre_channel) {
/* no change */
retval = 0;
goto out;
}
if (idev->exclusive >= 0) {
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
isdn_free_channel(idev->pre_device, idev->pre_channel, ISDN_USAGE_NET);
idev->exclusive = -1;
}
if (cfg->exclusive) {
/* If binding is exclusive, try to grab the channel */
idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET, lp->l2_proto,
lp->l3_proto, drvidx, chidx, cfg->eaz);
if (idev->exclusive < 0) {
/* Grab failed, because desired channel is in use */
retval = -EBUSY;
goto out;
}
/* All went ok, so update isdninfo */
isdn_slot_set_usage(idev->exclusive, ISDN_USAGE_EXCLUSIVE);
}
idev->pre_device = drvidx;
idev->pre_channel = chidx;
retval = 0;
out:
return retval;
}
/*
* Set interface-parameters.
* Always set all parameters, so the user-level application is responsible
* for not overwriting existing setups. It has to get the current
* setup first, if only selected parameters are to be changed.
*/
int
isdn_net_setcfg(isdn_net_ioctl_cfg *cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *lp = &idev->local;
ulong features;
int i, retval;
if (!idev) {
retval = -ENODEV;
goto out;
}
/* See if any registered driver supports the features we want */
features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
for (i = 0; i < ISDN_MAX_DRIVERS; i++)
if (dev->drv[i] &&
(dev->drv[i]->interface->features & features) == features)
break;
if (i == ISDN_MAX_DRIVERS) {
printk(KERN_WARNING "isdn_net: No driver with selected features\n");
retval = -ENODEV;
goto out;
}
retval = isdn_net_set_encap(idev, cfg->p_encap);
if (retval)
goto out;
retval = isdn_net_bind(idev, cfg);
if (retval)
goto out;
strncpy(lp->msn, cfg->eaz, ISDN_MSNLEN-1);
lp->msn[ISDN_MSNLEN-1] = 0;
lp->onhtime = cfg->onhtime;
idev->charge = cfg->charge;
lp->l2_proto = cfg->l2_proto;
lp->l3_proto = cfg->l3_proto;
lp->cbdelay = cfg->cbdelay * HZ / 5;
lp->dialmax = cfg->dialmax;
lp->triggercps = cfg->triggercps;
lp->slavedelay = cfg->slavedelay * HZ;
idev->pppbind = cfg->pppbind;
lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
lp->dialwait = cfg->dialwait * HZ;
if (cfg->secure)
lp->flags |= ISDN_NET_SECURE;
else
lp->flags &= ~ISDN_NET_SECURE;
if (cfg->cbhup)
lp->flags |= ISDN_NET_CBHUP;
else
lp->flags &= ~ISDN_NET_CBHUP;
switch (cfg->callback) {
case 0:
lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
break;
case 1:
lp->flags |= ISDN_NET_CALLBACK;
lp->flags &= ~ISDN_NET_CBOUT;
break;
case 2:
lp->flags |= ISDN_NET_CBOUT;
lp->flags &= ~ISDN_NET_CALLBACK;
break;
}
lp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */
if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
retval = -EINVAL;
goto out;
}
lp->flags |= cfg->dialmode; /* turn on selected bits */
if (lp->flags & ISDN_NET_DM_OFF)
isdn_net_hangup(idev);
if (cfg->chargehup)
lp->hupflags |= ISDN_CHARGEHUP;
else
lp->hupflags &= ~ISDN_CHARGEHUP;
if (cfg->ihup)
lp->hupflags |= ISDN_INHUP;
else
lp->hupflags &= ~ISDN_INHUP;
if (cfg->chargeint > 10) {
idev->chargeint = cfg->chargeint * HZ;
idev->charge_state = ST_CHARGE_HAVE_CINT;
lp->hupflags |= ISDN_MANCHARGE;
}
retval = 0;
out:
return retval;
}
/*
* Perform get-interface-parameters.ioctl
*/
int
isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *lp = &idev->local;
if (!idev)
return -ENODEV;
strcpy(cfg->eaz, lp->msn);
cfg->exclusive = idev->exclusive >= 0;
if (idev->pre_device >= 0) {
sprintf(cfg->drvid, "%s,%d", dev->drvid[idev->pre_device],
idev->pre_channel);
} else
cfg->drvid[0] = '\0';
cfg->onhtime = lp->onhtime;
cfg->charge = idev->charge;
cfg->l2_proto = lp->l2_proto;
cfg->l3_proto = lp->l3_proto;
cfg->p_encap = lp->p_encap;
cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
cfg->callback = 0;
if (lp->flags & ISDN_NET_CALLBACK)
cfg->callback = 1;
if (lp->flags & ISDN_NET_CBOUT)
cfg->callback = 2;
cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
cfg->chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
cfg->ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0;
cfg->cbdelay = lp->cbdelay * 5 / HZ;
cfg->dialmax = lp->dialmax;
cfg->triggercps = lp->triggercps;
cfg->slavedelay = lp->slavedelay / HZ;
cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
(idev->chargeint / HZ) : 0;
cfg->pppbind = idev->pppbind;
cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
cfg->dialwait = lp->dialwait / HZ;
if (lp->slave)
strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->netdev->name);
else
cfg->slave[0] = '\0';
if (lp->master)
strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->netdev->name);
else
cfg->master[0] = '\0';
list_del(&mlp->online);
list_add_tail(&mlp->online, &idev->online);
}
return 0;
}
/*
* Add a phone-number to an interface.
*/
int
isdn_net_addphone(isdn_net_ioctl_phone * phone)
isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_dev *p = isdn_net_findif(phone->name);
unsigned long flags;
struct isdn_net_phone *n;
isdn_net_local *mlp = ndev->priv;
isdn_net_dev *idev = list_entry(mlp->slaves.next, isdn_net_dev, slaves);
/* are we dialing already? */
if (isdn_net_bound(idev))
goto stop_queue;
if (!p)
return -ENODEV;
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
goto discard;
if (isdn_net_dial(idev) < 0)
goto discard;
/* Log packet, which triggered dialing */
if (dev->net_verbose)
isdn_net_log_skb(skb, idev);
n = kmalloc(sizeof(*n), GFP_KERNEL);
if (!n)
return -ENOMEM;
stop_queue:
netif_stop_queue(ndev);
return 1;
strcpy(n->num, phone->phone);
spin_lock_irqsave(&p->local.lock, flags);
list_add_tail(&n->list, &p->local.phone[phone->outgoing & 1]);
spin_unlock_irqrestore(&p->local.lock, flags);
discard:
isdn_net_unreachable(ndev, skb, "dial rejected");
dev_kfree_skb(skb);
return 0;
}
/*
* Copy a string of all phone-numbers of an interface to user space.
* This might sleep and must be called with the isdn semaphore down.
* Got a packet from ISDN-Channel.
*/
int
isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
static void
isdn_net_receive(isdn_net_dev *idev, struct sk_buff *skb)
{
isdn_net_dev *p = isdn_net_findif(phone->name);
unsigned long flags;
int inout = phone->outgoing & 1;
int count = 0;
char *buf = (char *)__get_free_page(GFP_KERNEL);
struct isdn_net_phone *n;
if (!p)
return -ENODEV;
if (!buf)
return -ENOMEM;
inout &= 1;
spin_lock_irqsave(&p->local.lock, flags);
list_for_each_entry(n, &p->local.phone[inout], list) {
strcpy(&buf[count], n->num);
count += strlen(n->num);
buf[count++] = ' ';
if (count > PAGE_SIZE - ISDN_MSNLEN - 1)
break;
}
spin_unlock_irqrestore(&p->local.lock, flags);
if (!count)
count++;
isdn_net_local *mlp = idev->mlp;
buf[count-1] = 0;
idev->transcount += skb->len;
if (copy_to_user(phones, buf, count))
count = -EFAULT;
mlp->stats.rx_packets++;
mlp->stats.rx_bytes += skb->len;
skb->dev = &mlp->dev;
skb->pkt_type = PACKET_HOST;
skb->mac.raw = skb->data;
isdn_dumppkt("R:", skb->data, skb->len, 40);
free_page((unsigned long)buf);
return count;
mlp->ops->receive(mlp, idev, skb);
}
/*
* Copy a string containing the peer's phone number of a connected interface
* to user space.
* A packet arrived via ISDN. Search interface-chain for a corresponding
* interface. If found, deliver packet to receiver-function and return 1,
* else return 0.
*/
int
isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
isdn_net_rcv_skb(int idx, struct sk_buff *skb)
{
isdn_net_dev *p = isdn_net_findif(phone->name);
int idx;
isdn_net_dev *idev = isdn_slot_idev(idx);
if (!p) return -ENODEV;
/*
* Theoretical race: while this executes, the remote number might
* become invalid (hang up) or change (new connection), resulting
* in (partially) wrong number copied to user. This race
* currently ignored.
*/
idx = p->isdn_slot;
if (idx<0) return -ENOTCONN;
/* for pre-bound channels, we need this extra check */
if (strncmp(isdn_slot_num(idx),"???",3) == 0 ) return -ENOTCONN;
strncpy(phone->phone,isdn_slot_num(idx),ISDN_MSNLEN);
phone->outgoing=USG_OUTGOING(isdn_slot_usage(idx));
if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
if (!idev) {
HERE;
return 0;
}
/*
* Delete a phone-number from an interface.
*/
int
isdn_net_delphone(isdn_net_ioctl_phone * phone)
{
isdn_net_dev *p = isdn_net_findif(phone->name);
int inout = phone->outgoing & 1;
struct isdn_net_phone *n;
unsigned long flags;
int retval;
if (!p)
return -ENODEV;
retval = -EINVAL;
spin_lock_irqsave(&p->local.lock, flags);
list_for_each_entry(n, &p->local.phone[inout], list) {
if (!strcmp(n->num, phone->phone)) {
list_del(&n->list);
kfree(n);
retval = 0;
break;
}
}
spin_unlock_irqrestore(&p->local.lock, flags);
return retval;
if (!isdn_net_online(idev))
return 0;
isdn_net_receive(idev, skb);
return 0;
}
/*
* Delete all phone-numbers of an interface.
* 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.
*/
static int
isdn_net_rmallphone(isdn_net_dev * p)
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;
unsigned long flags;
int i;
ulong flags;
char nr[32];
char *my_eaz;
spin_lock_irqsave(&p->local.lock, flags);
for (i = 0; i < 2; i++) {
while (!list_empty(&p->local.phone[i])) {
n = list_entry(p->local.phone[i].next, struct isdn_net_phone, list);
list_del(&n->list);
kfree(n);
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;
}
spin_lock_irqsave(&p->local.lock, flags);
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;
}
}
/*
* Force a hangup of a network-interface.
*/
int
isdn_net_force_hangup(char *name)
{
isdn_net_dev *idev = isdn_net_findif(name);
struct net_device *q;
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;
if (!idev)
return -ENODEV;
/* 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);
if (idev->isdn_slot < 0)
return -ENOTCONN;
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 */
}
q = idev->local.slave;
/* If this interface has slaves, do a hangup for them also. */
while (q) {
isdn_net_hangup(((isdn_net_local *) q->priv)->netdev);
q = (((isdn_net_local *) q->priv)->slave);
switch (isdn_msncmp(eaz, my_eaz)) {
case 1:
continue;
case 2:
match_more = 1;
continue;
}
isdn_net_hangup(idev);
return 0;
}
/*
* Helper-function for isdn_net_rm: Do the real work.
*/
static int
isdn_net_realrm(isdn_net_dev *p)
{
unsigned long flags;
if (isdn_net_bound(idev))
continue;
save_flags(flags);
cli();
if (isdn_net_device_started(p)) {
restore_flags(flags);
return -EBUSY;
}
isdn_net_set_encap(p, -1);
/* Free all phone-entries */
isdn_net_rmallphone(p);
/* If interface is bound exclusive, free channel-usage */
if (p->exclusive >= 0)
isdn_unexclusive_channel(p->pre_device, p->pre_channel);
if (p->local.master) {
/* It's a slave-device, so update master's slave-pointer if necessary */
if (((isdn_net_local *) (p->local.master->priv))->slave == &p->dev)
((isdn_net_local *) (p->local.master->priv))->slave = p->local.slave;
} else {
/* Unregister only if it's a master-device */
unregister_netdev(&p->dev);
}
/* Unlink device from chain */
list_del(&p->global_list);
if (p->local.slave) {
/* If this interface has a slave, remove it also */
char *slavename = ((isdn_net_local *) (p->local.slave->priv))->netdev->name;
struct list_head *l;
if (!USG_NONE(isdn_slot_usage(idx)))
continue;
list_for_each(l, &isdn_net_devs) {
isdn_net_dev *n = list_entry(l, isdn_net_dev, global_list);
if (!strcmp(n->name, slavename)) {
isdn_net_realrm(n);
break;
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;
}
}
restore_flags(flags);
kfree(p);
return 0;
}
continue;
}
found:
dbg_net_icall("n_fi: match3\n");
/* matching interface found */
/*
* Remove a single network-interface.
/*
* 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.
*/
int
isdn_net_rm(char *name)
{
struct list_head *l;
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);
/* Search name in netdev-chain */
list_for_each(l, &isdn_net_devs) {
isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
if (!strcmp(p->name, name))
return isdn_net_realrm(p);
isdn_net_accept(idev, idx, nr);
restore_flags(flags);
return 1;
}
return -ENODEV;
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;
}
/*
* Remove all network-interfaces
* This is called from certain upper protocol layers (multilink ppp
* and x25iface encapsulation module) that want to initiate dialing
* themselves.
*/
int
isdn_net_rmall(void)
isdn_net_dial_req(isdn_net_dev *idev)
{
unsigned long flags;
int ret;
/* Walk through netdev-chain */
save_flags(flags);
cli();
while (!list_empty(&isdn_net_devs)) {
isdn_net_dev *p = list_entry(isdn_net_devs.next, isdn_net_dev, global_list);
isdn_net_local *mlp = idev->mlp;
/* is there a better error code? */
if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
return -EBUSY;
/* Remove master-devices only, slaves get removed with their master */
if (!p->local.master) {
if ((ret = isdn_net_realrm(p))) {
restore_flags(flags);
return ret;
}
}
}
restore_flags(flags);
return 0;
return isdn_net_dial(idev);
}
// ISDN_NET_ENCAP_IPTYP
......@@ -2212,16 +610,17 @@ isdn_iptyp_header(struct sk_buff *skb, struct net_device *dev,
}
static void
isdn_iptyp_receive(isdn_net_dev *p, isdn_net_local *olp,
isdn_iptyp_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb)
{
isdn_net_reset_huptimer(p, olp->netdev);
idev->huptimer = 0;
get_u16(skb->data, &skb->protocol);
skb_pull(skb, 2);
netif_rx(skb);
}
static struct isdn_netif_ops iptyp_ops = {
.hard_start_xmit = isdn_net_start_xmit,
.hard_header = isdn_iptyp_header,
.flags = IFF_NOARP | IFF_POINTOPOINT,
.type = ARPHRD_PPP,
......@@ -2243,16 +642,17 @@ isdn_uihdlc_header(struct sk_buff *skb, struct net_device *dev,
}
static void
isdn_uihdlc_receive(isdn_net_dev *p, isdn_net_local *olp,
isdn_uihdlc_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb)
{
isdn_net_reset_huptimer(p, olp->netdev);
idev->huptimer = 0;
skb_pull(skb, 2);
skb->protocol = htons(ETH_P_IP);
netif_rx(skb);
}
static struct isdn_netif_ops uihdlc_ops = {
.hard_start_xmit = isdn_net_start_xmit,
.hard_header = isdn_uihdlc_header,
.flags = IFF_NOARP | IFF_POINTOPOINT,
.type = ARPHRD_HDLC,
......@@ -2265,15 +665,16 @@ static struct isdn_netif_ops uihdlc_ops = {
// ======================================================================
static void
isdn_rawip_receive(isdn_net_dev *p, isdn_net_local *olp,
isdn_rawip_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb)
{
isdn_net_reset_huptimer(p, olp->netdev);
idev->huptimer = 0;
skb->protocol = htons(ETH_P_IP);
netif_rx(skb);
}
static struct isdn_netif_ops rawip_ops = {
.hard_start_xmit = isdn_net_start_xmit,
.flags = IFF_NOARP | IFF_POINTOPOINT,
.type = ARPHRD_PPP,
.receive = isdn_rawip_receive,
......@@ -2283,73 +684,19 @@ static struct isdn_netif_ops rawip_ops = {
// Ethernet over ISDN
// ======================================================================
/* This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
* instead of dev->hard_header_len off. This is done because the
* lowlevel-driver has already pulled off its stuff when we get
* here and this routine only gets called with p_encap == ETHER.
* Determine the packet's protocol ID. The rule here is that we
* assume 802.3 if the type field is short enough to be a length.
* This is normal practice and works for any 'now in use' protocol.
* FIXME
*/
static unsigned short
isdn_eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
struct ethhdr *eth;
unsigned char *rawp;
skb->mac.raw = skb->data;
skb_pull(skb, ETH_HLEN);
eth = skb->mac.ethernet;
if (*eth->h_dest & 1) {
if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
}
/*
* This ALLMULTI check should be redundant by 1.4
* so don't forget to remove it.
*/
else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) {
if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
skb->pkt_type = PACKET_OTHERHOST;
}
if (ntohs(eth->h_proto) >= 1536)
return eth->h_proto;
rawp = skb->data;
/*
* This is a magic hack to spot IPX packets. Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
* won't work for fault tolerant netware but does for the rest.
*/
if (*(unsigned short *) rawp == 0xFFFF)
return htons(ETH_P_802_3);
/*
* Real 802.2 LLC
*/
return htons(ETH_P_802_2);
}
static void
isdn_ether_receive(isdn_net_dev *p, isdn_net_local *olp,
isdn_ether_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb)
{
isdn_net_reset_huptimer(p, olp->netdev);
skb->protocol = isdn_eth_type_trans(skb, skb->dev);
idev->huptimer = 0;
skb->protocol = eth_type_trans(skb, skb->dev);
netif_rx(skb);
}
static int
isdn_ether_open(isdn_net_local *lp)
{
struct net_device *dev = &lp->netdev->dev;
struct net_device *dev = &lp->dev;
struct in_device *in_dev;
int i;
......@@ -2369,7 +716,7 @@ isdn_ether_open(isdn_net_local *lp)
static int
isdn_ether_init(isdn_net_local *lp)
{
struct net_device *dev = &lp->netdev->dev;
struct net_device *dev = &lp->dev;
ether_setup(dev);
dev->tx_queue_len = 10;
......@@ -2379,6 +726,7 @@ isdn_ether_init(isdn_net_local *lp)
}
static struct isdn_netif_ops ether_ops = {
.hard_start_xmit = isdn_net_start_xmit,
.hard_header = eth_header,
.receive = isdn_ether_receive,
.init = isdn_ether_init,
......@@ -2388,7 +736,7 @@ static struct isdn_netif_ops ether_ops = {
// ======================================================================
void
isdn_net_init_module(void)
isdn_net_init(void)
{
register_isdn_netif(ISDN_NET_ENCAP_ETHER, &ether_ops);
register_isdn_netif(ISDN_NET_ENCAP_RAWIP, &rawip_ops);
......@@ -2402,4 +750,7 @@ isdn_net_init_module(void)
#ifdef CONFIG_ISDN_PPP
register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP, &isdn_ppp_ops);
#endif
isdn_net_lib_init();
}
......@@ -32,46 +32,49 @@
#define CISCO_SLARP_REPLY 1
#define CISCO_SLARP_KEEPALIVE 2
extern void isdn_net_init_module(void);
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);
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_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 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_new(char *, struct net_device *);
extern int isdn_net_newslave(char *);
extern int isdn_net_rm(char *);
extern int isdn_net_rmall(void);
extern int isdn_net_stat_callback(int, isdn_ctrl *);
extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
extern int isdn_net_addphone(isdn_net_ioctl_phone *);
extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
extern int isdn_net_delphone(isdn_net_ioctl_phone *);
extern int isdn_net_find_icall(int, int, int, setup_parm *);
extern void isdn_net_hangup(isdn_net_dev *);
extern void isdn_net_hangup_all(void);
extern int isdn_net_force_hangup(char *);
extern int isdn_net_force_dial(char *);
extern isdn_net_dev *isdn_net_findif(char *);
extern int isdn_net_hangup(isdn_net_dev *);
extern int isdn_net_rcv_skb(int, struct sk_buff *);
extern int isdn_net_dial_req(isdn_net_local *);
extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb);
extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
extern int isdn_net_online(isdn_net_dev *idev);
extern int isdn_net_dial_req(isdn_net_dev *);
extern void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb);
extern void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb);
extern int isdn_net_online(isdn_net_dev *);
static inline void
isdn_net_reset_huptimer(isdn_net_dev *idev, isdn_net_dev *idev2)
{
idev->huptimer = 0;
idev2->huptimer = 0;
}
enum {
ST_CHARGE_NULL,
ST_CHARGE_GOT_CINF, /* got a first charge info */
ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
};
#define ISDN_NET_MAX_QUEUE_LENGTH 2
/*
* is this particular channel busy?
*/
static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
static inline int
isdn_net_dev_busy(isdn_net_dev *idev)
{
if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
if (atomic_read(&idev->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
return 0;
else
return 1;
......@@ -81,86 +84,69 @@ static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
* For the given net device, this will get a non-busy channel out of the
* corresponding bundle. The returned channel is locked.
*/
static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd)
static inline isdn_net_dev *
isdn_net_get_locked_dev(isdn_net_local *mlp)
{
unsigned long flags;
isdn_net_local *lp;
spin_lock_irqsave(&nd->queue_lock, flags);
lp = nd->queue; /* get lp on top of queue */
spin_lock_bh(&nd->queue->xmit_lock);
while (isdn_net_lp_busy(nd->queue)) {
spin_unlock_bh(&nd->queue->xmit_lock);
nd->queue = nd->queue->next;
if (nd->queue == lp) { /* not found -- should never happen */
lp = NULL;
goto errout;
isdn_net_dev *idev;
spin_lock_irqsave(&mlp->online_lock, flags);
list_for_each_entry(idev, &mlp->online, online) {
spin_lock_bh(&idev->xmit_lock);
if (!isdn_net_dev_busy(idev)) {
/* point the head to next online channel */
list_del(&mlp->online);
list_add(&mlp->online, &idev->online);
goto found;
}
spin_lock_bh(&nd->queue->xmit_lock);
spin_unlock_bh(&idev->xmit_lock);
}
lp = nd->queue;
nd->queue = nd->queue->next;
errout:
spin_unlock_irqrestore(&nd->queue_lock, flags);
return lp;
idev = NULL;
found:
spin_unlock_irqrestore(&mlp->online_lock, flags);
return idev;
}
/*
* add a channel to a bundle
*/
static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp)
static inline void
isdn_net_add_to_bundle(isdn_net_local *mlp, isdn_net_dev *idev)
{
isdn_net_local *lp;
unsigned long flags;
spin_lock_irqsave(&nd->queue_lock, flags);
lp = nd->queue;
nlp->last = lp->last;
lp->last->next = nlp;
lp->last = nlp;
nlp->next = lp;
nd->queue = nlp;
spin_unlock_irqrestore(&nd->queue_lock, flags);
spin_lock_irqsave(&mlp->online_lock, flags);
list_add(&idev->online, &mlp->online);
spin_unlock_irqrestore(&mlp->online_lock, flags);
}
/*
* remove a channel from the bundle it belongs to
*/
static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
static inline void
isdn_net_rm_from_bundle(isdn_net_dev *idev)
{
isdn_net_local *master_lp = lp;
isdn_net_local *mlp = idev->mlp;
unsigned long flags;
if (lp->master)
master_lp = (isdn_net_local *) lp->master->priv;
spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
lp->last->next = lp->next;
lp->next->last = lp->last;
if (master_lp->netdev->queue == lp) {
master_lp->netdev->queue = lp->next;
if (lp->next == lp) { /* last in queue */
master_lp->netdev->queue = &master_lp->netdev->local;
}
}
lp->next = lp->last = lp; /* (re)set own pointers */
spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
spin_lock_irqsave(&mlp->online_lock, flags);
// list_del(&idev->online); FIXME
spin_unlock_irqrestore(&mlp->online_lock, flags);
}
/*
* wake up the network -> net_device queue.
* For slaves, wake the corresponding master interface.
*/
static inline void isdn_net_device_wake_queue(isdn_net_local *lp)
static inline void
isdn_net_dev_wake_queue(isdn_net_dev *idev)
{
if (lp->master)
netif_wake_queue(lp->master);
else
netif_wake_queue(&lp->netdev->dev);
netif_wake_queue(&idev->mlp->dev);
}
static inline int isdn_net_bound(isdn_net_dev *idev)
static inline int
isdn_net_bound(isdn_net_dev *idev)
{
return idev->isdn_slot >= 0;
}
......
/*
* Linux ISDN subsystem, Network interface configuration
*
* Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de)
* 1995,96 by Thinking Objects Software GmbH Wuerzburg
* 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
* 1999-2002 by Kai Germaschewski <kai@germaschewski.name>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/capability.h>
#include <linux/rtnetlink.h>
#include "isdn_common.h"
#include "isdn_net.h"
#include "isdn_ppp.h"
#include "isdn_fsm.h"
#define ISDN_NET_TX_TIMEOUT (20*HZ)
/* All of this configuration code is globally serialized */
static DECLARE_MUTEX(sem);
LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */ // FIXME static
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 */
/* ====================================================================== */
static struct isdn_netif_ops *isdn_netif_ops[ISDN_NET_ENCAP_NR];
int
register_isdn_netif(int encap, struct isdn_netif_ops *ops)
{
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR)
return -EINVAL;
if (isdn_netif_ops[encap])
return -EBUSY;
isdn_netif_ops[encap] = ops;
return 0;
}
/* ====================================================================== */
/* Helpers */
/* ====================================================================== */
/* Search list of net-interfaces for an interface with given name. */
static isdn_net_dev *
isdn_net_findif(char *name)
{
isdn_net_dev *idev;
list_for_each_entry(idev, &isdn_net_devs, global_list) {
if (!strcmp(idev->name, name))
return idev;
}
return NULL;
}
/* Set up a certain encapsulation */
static int
isdn_net_set_encap(isdn_net_local *lp, int encap)
{
int retval = 0;
if (lp->p_encap == encap){
/* nothing to do */
retval = 0;
goto out;
}
if (netif_running(&lp->dev)) {
retval = -EBUSY;
goto out;
}
if (lp->ops && lp->ops->cleanup)
lp->ops->cleanup(lp);
if (encap < 0 || encap >= ISDN_NET_ENCAP_NR) {
lp->p_encap = -1;
lp->ops = NULL;
retval = -EINVAL;
goto out;
}
lp->p_encap = encap;
lp->ops = isdn_netif_ops[encap];
lp->dev.hard_start_xmit = lp->ops->hard_start_xmit;
lp->dev.hard_header = lp->ops->hard_header;
lp->dev.do_ioctl = lp->ops->do_ioctl;
lp->dev.flags = lp->ops->flags;
lp->dev.type = lp->ops->type;
lp->dev.addr_len = lp->ops->addr_len;
if (lp->ops->init)
retval = lp->ops->init(lp);
if (retval != 0) {
lp->p_encap = -1;
lp->ops = NULL;
}
out:
return retval;
}
static int
isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg)
{
isdn_net_local *mlp = idev->mlp;
int i, retval;
int drvidx = -1;
int chidx = -1;
char drvid[25];
strncpy(drvid, cfg->drvid, 24);
drvid[24] = 0;
if (cfg->exclusive && !strlen(drvid)) {
/* If we want to bind exclusively, need to specify drv/chan */
retval = -ENODEV;
goto out;
}
if (strlen(drvid)) {
/* A bind has been requested ... */
char *c = strchr(drvid, ',');
if (!c) {
retval = -ENODEV;
goto out;
}
/* The channel-number is appended to the driver-Id with a comma */
*c = 0;
chidx = simple_strtol(c + 1, NULL, 10);
for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
/* Lookup driver-Id in array */
if (!strcmp(dev->drvid[i], drvid)) {
drvidx = i;
break;
}
}
if (drvidx == -1 || chidx == -1) {
/* Either driver-Id or channel-number invalid */
retval = -ENODEV;
goto out;
}
}
if (cfg->exclusive == (idev->exclusive >= 0) &&
drvidx == idev->pre_device && chidx == idev->pre_channel) {
/* no change */
retval = 0;
goto out;
}
if (idev->exclusive >= 0) {
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
isdn_free_channel(idev->pre_device, idev->pre_channel, ISDN_USAGE_NET);
idev->exclusive = -1;
}
if (cfg->exclusive) {
/* If binding is exclusive, try to grab the channel */
idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto,
mlp->l3_proto, drvidx, chidx, cfg->eaz);
if (idev->exclusive < 0) {
/* Grab failed, because desired channel is in use */
retval = -EBUSY;
goto out;
}
/* All went ok, so update isdninfo */
isdn_slot_set_usage(idev->exclusive, ISDN_USAGE_EXCLUSIVE);
}
idev->pre_device = drvidx;
idev->pre_channel = chidx;
retval = 0;
out:
return retval;
}
/*
* Delete all phone-numbers of an interface.
*/
static void
isdn_net_rmallphone(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
struct isdn_net_phone *n;
int i;
for (i = 0; i < 2; i++) {
while (!list_empty(&mlp->phone[i])) {
n = list_entry(mlp->phone[i].next, struct isdn_net_phone, list);
list_del(&n->list);
kfree(n);
}
}
}
/* ====================================================================== */
/* /dev/isdnctrl net ioctl interface */
/* ====================================================================== */
/*
* Allocate a new network-interface and initialize its data structures
*/
static int
isdn_net_addif(char *name, isdn_net_local *mlp)
{
int retval;
struct net_device *dev = NULL;
isdn_net_dev *idev;
/* Avoid creating an existing interface */
if (isdn_net_findif(name))
return -EEXIST;
idev = kmalloc(sizeof(*idev), GFP_KERNEL);
if (!idev)
return -ENOMEM;
memset(idev, 0, sizeof(*idev));
strcpy(idev->name, name);
tasklet_init(&idev->tlet, isdn_net_tasklet, (unsigned long) idev);
spin_lock_init(&idev->xmit_lock);
skb_queue_head_init(&idev->super_tx_queue);
idev->isdn_slot = -1;
idev->pre_device = -1;
idev->pre_channel = -1;
idev->exclusive = -1;
idev->ppp_slot = -1;
idev->pppbind = -1;
init_timer(&idev->dial_timer);
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);
if (!mlp)
return -ENOMEM;
memset(mlp, 0, sizeof(*mlp));
mlp->magic = ISDN_NET_MAGIC;
INIT_LIST_HEAD(&mlp->slaves);
INIT_LIST_HEAD(&mlp->online);
mlp->p_encap = -1;
isdn_net_set_encap(mlp, ISDN_NET_ENCAP_RAWIP);
mlp->l2_proto = ISDN_PROTO_L2_X75I;
mlp->l3_proto = ISDN_PROTO_L3_TRANS;
mlp->triggercps = 6000;
mlp->slavedelay = 10 * HZ;
mlp->hupflags = ISDN_INHUP;
mlp->onhtime = 10;
mlp->dialmax = 1;
mlp->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;
mlp->cbdelay = 5 * HZ; /* Wait 5 secs before call-back */
mlp->dialtimeout = 60 * HZ;/* Wait 1 min for connection */
mlp->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
INIT_LIST_HEAD(&mlp->phone[0]);
INIT_LIST_HEAD(&mlp->phone[1]);
dev = &mlp->dev;
}
idev->mlp = mlp;
list_add_tail(&idev->slaves, &mlp->slaves);
if (dev) {
strcpy(dev->name, name);
dev->priv = mlp;
dev->init = isdn_init_netif;
SET_MODULE_OWNER(dev);
retval = register_netdev(dev);
if (retval) {
kfree(mlp);
kfree(idev);
return retval;
}
}
list_add(&idev->global_list, &isdn_net_devs);
return 0;
}
/*
* Add a new slave interface to an existing one
*/
static int
isdn_net_addslave(char *parm)
{
char *p = strchr(parm, ',');
isdn_net_dev *idev;
isdn_net_local *mlp;
int retval;
/* get slave name */
if (!p || !p[1])
return -EINVAL;
*p++ = 0;
/* find master */
idev = isdn_net_findif(parm);
if (!idev)
return -ESRCH;
mlp = idev->mlp;
rtnl_lock();
if (netif_running(&mlp->dev))
return -EBUSY;
retval = isdn_net_addif(p, mlp);
rtnl_unlock();
return retval;
}
/*
* Delete a single network-interface
*/
static int
isdn_net_dev_delete(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
int retval;
rtnl_lock();
if (netif_running(&mlp->dev)) {
retval = -EBUSY;
goto unlock;
}
isdn_net_set_encap(mlp, -1);
isdn_net_rmallphone(idev);
if (idev->exclusive >= 0)
isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
list_del(&idev->slaves);
rtnl_unlock();
if (list_empty(&mlp->slaves)) {
unregister_netdev(&mlp->dev);
kfree(mlp);
}
list_del(&idev->global_list);
kfree(idev);
return 0;
unlock:
rtnl_unlock();
return retval;
}
/*
* Delete a single network-interface
*/
static int
isdn_net_delif(char *name)
{
/* FIXME: For compatibility, if a master isdn_net_dev is rm'ed,
* kill all slaves, too */
isdn_net_dev *idev = isdn_net_findif(name);
if (!idev)
return -ENODEV;
return isdn_net_dev_delete(idev);
}
/*
* Set interface-parameters.
* Always set all parameters, so the user-level application is responsible
* for not overwriting existing setups. It has to get the current
* setup first, if only selected parameters are to be changed.
*/
static int
isdn_net_setcfg(isdn_net_ioctl_cfg *cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *mlp;
ulong features;
int i, retval;
if (!idev)
return -ENODEV;
mlp = idev->mlp;
rtnl_lock();
if (netif_running(&mlp->dev)) {
retval = -EBUSY;
goto out;
}
/* See if any registered driver supports the features we want */
features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
for (i = 0; i < ISDN_MAX_DRIVERS; i++)
if (dev->drv[i] &&
(dev->drv[i]->interface->features & features) == features)
break;
if (i == ISDN_MAX_DRIVERS) {
printk(KERN_WARNING "isdn_net: No driver with selected features\n");
retval = -ENODEV;
goto out;
}
retval = isdn_net_set_encap(mlp, cfg->p_encap);
if (retval)
goto out;
retval = isdn_net_bind(idev, cfg);
if (retval)
goto out;
strncpy(mlp->msn, cfg->eaz, ISDN_MSNLEN-1);
mlp->msn[ISDN_MSNLEN-1] = 0;
mlp->onhtime = cfg->onhtime;
idev->charge = cfg->charge;
mlp->l2_proto = cfg->l2_proto;
mlp->l3_proto = cfg->l3_proto;
mlp->cbdelay = cfg->cbdelay * HZ / 5;
mlp->dialmax = cfg->dialmax;
mlp->triggercps = cfg->triggercps;
mlp->slavedelay = cfg->slavedelay * HZ;
idev->pppbind = cfg->pppbind;
mlp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
mlp->dialwait = cfg->dialwait * HZ;
if (cfg->secure)
mlp->flags |= ISDN_NET_SECURE;
else
mlp->flags &= ~ISDN_NET_SECURE;
if (cfg->cbhup)
mlp->flags |= ISDN_NET_CBHUP;
else
mlp->flags &= ~ISDN_NET_CBHUP;
switch (cfg->callback) {
case 0:
mlp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
break;
case 1:
mlp->flags |= ISDN_NET_CALLBACK;
mlp->flags &= ~ISDN_NET_CBOUT;
break;
case 2:
mlp->flags |= ISDN_NET_CBOUT;
mlp->flags &= ~ISDN_NET_CALLBACK;
break;
}
mlp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */
if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
retval = -EINVAL;
goto out;
}
mlp->flags |= cfg->dialmode; /* turn on selected bits */
if (mlp->flags & ISDN_NET_DM_OFF)
isdn_net_hangup(idev);
if (cfg->chargehup)
mlp->hupflags |= ISDN_CHARGEHUP;
else
mlp->hupflags &= ~ISDN_CHARGEHUP;
if (cfg->ihup)
mlp->hupflags |= ISDN_INHUP;
else
mlp->hupflags &= ~ISDN_INHUP;
if (cfg->chargeint > 10) {
idev->chargeint = cfg->chargeint * HZ;
idev->charge_state = ST_CHARGE_HAVE_CINT;
mlp->hupflags |= ISDN_MANCHARGE;
}
retval = 0;
out:
rtnl_unlock();
return retval;
}
/*
* Perform get-interface-parameters.ioctl
*/
static int
isdn_net_getcfg(isdn_net_ioctl_cfg *cfg)
{
isdn_net_dev *idev = isdn_net_findif(cfg->name);
isdn_net_local *mlp;
if (!idev)
return -ENODEV;
mlp = idev->mlp;
strcpy(cfg->eaz, mlp->msn);
cfg->exclusive = idev->exclusive >= 0;
if (idev->pre_device >= 0) {
sprintf(cfg->drvid, "%s,%d", dev->drvid[idev->pre_device],
idev->pre_channel);
} else {
cfg->drvid[0] = '\0';
}
cfg->onhtime = mlp->onhtime;
cfg->charge = idev->charge;
cfg->l2_proto = mlp->l2_proto;
cfg->l3_proto = mlp->l3_proto;
cfg->p_encap = mlp->p_encap;
cfg->secure = (mlp->flags & ISDN_NET_SECURE) ? 1 : 0;
cfg->callback = 0;
if (mlp->flags & ISDN_NET_CALLBACK)
cfg->callback = 1;
if (mlp->flags & ISDN_NET_CBOUT)
cfg->callback = 2;
cfg->cbhup = (mlp->flags & ISDN_NET_CBHUP) ? 1 : 0;
cfg->dialmode = mlp->flags & ISDN_NET_DIALMODE_MASK;
cfg->chargehup = (mlp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
cfg->ihup = (mlp->hupflags & ISDN_INHUP) ? 1 : 0;
cfg->cbdelay = mlp->cbdelay * 5 / HZ;
cfg->dialmax = mlp->dialmax;
cfg->triggercps = mlp->triggercps;
cfg->slavedelay = mlp->slavedelay / HZ;
cfg->chargeint = (mlp->hupflags & ISDN_CHARGEHUP) ?
(idev->chargeint / HZ) : 0;
cfg->pppbind = idev->pppbind;
cfg->dialtimeout = mlp->dialtimeout >= 0 ? mlp->dialtimeout / HZ : -1;
cfg->dialwait = mlp->dialwait / HZ;
if (idev->slaves.next != &mlp->slaves)
strcpy(cfg->slave, list_entry(idev->slaves.next, isdn_net_dev, slaves)->name);
else
cfg->slave[0] = '\0';
if (strcmp(mlp->dev.name, idev->name))
strcpy(cfg->master, mlp->dev.name);
else
cfg->master[0] = '\0';
return 0;
}
/*
* Add a phone-number to an interface.
*/
static int
isdn_net_addphone(isdn_net_ioctl_phone *phone)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
struct isdn_net_phone *n;
int retval = 0;
if (!idev)
return -ENODEV;
rtnl_lock();
if (netif_running(&idev->mlp->dev)) {
retval = -EBUSY;
goto out;
}
n = kmalloc(sizeof(*n), GFP_KERNEL);
if (!n) {
retval = -ENOMEM;
goto out;
}
strcpy(n->num, phone->phone);
list_add_tail(&n->list, &idev->mlp->phone[phone->outgoing & 1]);
out:
rtnl_unlock();
return retval;
}
/*
* Delete a phone-number from an interface.
*/
static int
isdn_net_delphone(isdn_net_ioctl_phone *phone)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
struct isdn_net_phone *n;
int retval;
if (!idev)
return -ENODEV;
rtnl_lock();
if (netif_running(&idev->mlp->dev)) {
retval = -EBUSY;
goto out;
}
retval = -EINVAL;
list_for_each_entry(n, &idev->mlp->phone[phone->outgoing & 1], list) {
if (!strcmp(n->num, phone->phone)) {
list_del(&n->list);
kfree(n);
retval = 0;
break;
}
}
out:
rtnl_unlock();
return retval;
}
/*
* Copy a string of all phone-numbers of an interface to user space.
*/
static int
isdn_net_getphone(isdn_net_ioctl_phone * phone, char *phones)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
int count = 0;
char *buf = (char *)__get_free_page(GFP_KERNEL);
struct isdn_net_phone *n;
if (!buf)
return -ENOMEM;
if (!idev) {
count = -ENODEV;
goto free;
}
list_for_each_entry(n, &idev->mlp->phone[phone->outgoing & 1], list) {
strcpy(&buf[count], n->num);
count += strlen(n->num);
buf[count++] = ' ';
if (count > PAGE_SIZE - ISDN_MSNLEN - 1)
break;
}
if (!count) /* list was empty? */
count++;
buf[count-1] = 0;
if (copy_to_user(phones, buf, count))
count = -EFAULT;
free:
free_page((unsigned long)buf);
return count;
}
/*
* Force a net-interface to dial out.
*/
static int
isdn_net_dial_out(char *name)
{
isdn_net_dev *idev = isdn_net_findif(name);
if (!idev)
return -ENODEV;
return isdn_net_dial(idev);
}
/*
* Force a hangup of a network-interface.
*/
static int
isdn_net_force_hangup(char *name) // FIXME rename?
{
isdn_net_dev *idev = isdn_net_findif(name);
if (!idev)
return -ENODEV;
if (idev->isdn_slot < 0)
return -ENOTCONN;
isdn_net_hangup(idev);
return 0;
}
/*
* Copy a string containing the peer's phone number of a connected interface
* to user space.
*/
static int
isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
{
isdn_net_dev *idev = isdn_net_findif(phone->name);
int idx;
if (!idev)
return -ENODEV;
/* FIXME
* Theoretical race: while this executes, the remote number might
* become invalid (hang up) or change (new connection), resulting
* in (partially) wrong number copied to user. This race
* currently ignored.
*/
idx = idev->isdn_slot;
if (idx < 0)
return -ENOTCONN;
/* for pre-bound channels, we need this extra check */
if (strncmp(isdn_slot_num(idx), "???", 3) == 0 )
return -ENOTCONN;
strncpy(phone->phone, isdn_slot_num(idx), ISDN_MSNLEN);
phone->outgoing = USG_OUTGOING(isdn_slot_usage(idx));
if (copy_to_user(peer, phone, sizeof(*peer)))
return -EFAULT;
return 0;
}
/*
* ioctl on /dev/isdnctrl, used to configure ISDN net interfaces
*/
int
isdn_net_ioctl(struct inode *ino, struct file *file, uint cmd, ulong arg)
{
/* Save stack space */
union {
char name[10];
char bname[20];
isdn_net_ioctl_phone phone;
isdn_net_ioctl_cfg cfg;
} iocpar;
int retval;
#define name iocpar.name
#define bname iocpar.bname
#define phone iocpar.phone
#define cfg iocpar.cfg
name[sizeof(name)-1] = 0;
bname[sizeof(bname)-1] = 0;
down(&sem);
switch (cmd) {
case IIOCNETAIF: /* add an interface */
if (copy_from_user(name, (char *) arg, sizeof(name) - 1)) {
retval = -EFAULT;
break;
}
retval = isdn_net_addif(name, NULL);
break;
case IIOCNETASL: /* add slave to an interface */
if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1)) {
retval = -EFAULT;
break;
}
retval = isdn_net_addslave(bname);
break;
case IIOCNETDIF: /* delete an interface */
if (copy_from_user(name, (char *) arg, sizeof(name) - 1)) {
retval = -EFAULT;
break;
}
retval = isdn_net_delif(name);
break;
case IIOCNETSCF: /* set config */
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))) {
retval = -EFAULT;
break;
}
retval = isdn_net_setcfg(&cfg);
break;
case IIOCNETGCF: /* get config */
if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))) {
retval = -EFAULT;
break;
}
retval = isdn_net_getcfg(&cfg);
if (retval)
break;
if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
retval = -EFAULT;
break;
case IIOCNETANM: /* add a phone number */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
break;
}
retval = isdn_net_addphone(&phone);
break;
case IIOCNETGNM: /* get list of phone numbers */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
break;
}
retval = isdn_net_getphone(&phone, (char *) arg);
break;
case IIOCNETDNM: /* delete a phone number */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
break;
}
retval = isdn_net_delphone(&phone);
break;
case IIOCNETDIL: /* trigger dial-out */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_net_dial_out(name);
break;
case IIOCNETHUP: /* hangup */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_net_force_hangup(name);
break;
case IIOCNETGPN: /* Get peer phone number of a connected interface */
if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
retval = -EFAULT;
}
retval = isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
break;
#ifdef CONFIG_ISDN_PPP
case IIOCNETALN: /* Add link */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_ppp_dial_slave(name);
break;
case IIOCNETDLN: /* Delete link */
if (copy_from_user(name, (char *) arg, sizeof(name))) {
retval = -EFAULT;
break;
}
retval = isdn_ppp_hangup_slave(name);
break;
#endif
default:
retval = -ENOTTY;
}
up(&sem);
return retval;
#undef name
#undef bname
#undef iocts
#undef phone
#undef cfg
}
/*
* Hang up all network-interfaces
*/
void
isdn_net_hangup_all(void)
{
isdn_net_dev *idev;
down(&sem);
list_for_each_entry(idev, &isdn_net_devs, global_list)
isdn_net_hangup(idev);
up(&sem);
}
/*
* Remove all network-interfaces
*/
void
isdn_net_exit(void)
{
isdn_net_dev *idev;
int retval;
down(&sem);
while (!list_empty(&isdn_net_devs)) {
idev = list_entry(isdn_net_devs.next, isdn_net_dev, global_list);
retval = isdn_net_dev_delete(idev);
/* can only fail if an interface is still running.
* In this case, an elevated module use count should
* have prevented this function from being called in
* the first place */
if (retval)
isdn_BUG();
}
up(&sem);
// FIXME
isdn_net_lib_exit();
}
/* ====================================================================== */
/* interface to network layer */
/* ====================================================================== */
/*
* Open/initialize the board.
*/
static int
isdn_net_open(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
int retval = 0;
if (!lp->ops)
return -ENODEV;
if (lp->ops->open)
retval = lp->ops->open(lp);
if (!retval)
return retval;
netif_start_queue(dev);
return 0;
}
/*
* Shutdown a net-interface.
*/
// FIXME share?
static int
isdn_net_close(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
struct list_head *l, *n;
isdn_net_dev *sdev;
if (lp->ops->close)
lp->ops->close(lp);
netif_stop_queue(dev);
list_for_each_safe(l, n, &lp->online) {
sdev = list_entry(l, isdn_net_dev, online);
isdn_net_hangup(sdev);
}
return 0;
}
/*
* Get statistics
*/
static struct net_device_stats *
isdn_net_get_stats(struct net_device *dev)
{
isdn_net_local *lp = dev->priv;
return &lp->stats;
}
/*
* Transmit timeout
*/
static void
isdn_net_tx_timeout(struct net_device *dev)
{
printk(KERN_WARNING "isdn_tx_timeout dev %s\n", dev->name);
netif_wake_queue(dev);
}
/*
* Interface-setup. (just after registering a new interface)
*/
static int
isdn_init_netif(struct net_device *ndev)
{
/* Setup the generic properties */
ndev->mtu = 1500;
ndev->tx_queue_len = 10;
ndev->open = &isdn_net_open;
ndev->hard_header_len = ETH_HLEN + isdn_hard_header_len();
ndev->stop = &isdn_net_close;
ndev->get_stats = &isdn_net_get_stats;
ndev->tx_timeout = isdn_net_tx_timeout;
ndev->watchdog_timeo = ISDN_NET_TX_TIMEOUT;
return 0;
}
/* ====================================================================== */
static void
isdn_net_tasklet(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
struct sk_buff *skb;
spin_lock_bh(&idev->xmit_lock);
while (!isdn_net_dev_busy(idev)) {
skb = skb_dequeue(&idev->super_tx_queue);
if (!skb)
break;
isdn_net_writebuf_skb(idev, skb);
}
spin_unlock_bh(&idev->xmit_lock);
}
/* ====================================================================== */
/* call control state machine */
/* ====================================================================== */
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->fi.state == ST_ACTIVE;
}
static void
isdn_net_dial_timer(unsigned long data)
{
isdn_net_dev *idev = (isdn_net_dev *) data;
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
*/
void
isdn_net_unbind_channel(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
ulong flags;
save_flags(flags);
cli();
if (idev->isdn_slot < 0) {
isdn_BUG();
return;
}
if (mlp->ops->unbind)
mlp->ops->unbind(idev);
skb_queue_purge(&idev->super_tx_queue);
fsm_change_state(&idev->fi, ST_NULL);
isdn_slot_set_idev(idev->isdn_slot, NULL);
isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET);
idev->isdn_slot = -1;
restore_flags(flags);
}
int
isdn_net_dial(isdn_net_dev *idev)
{
int slot;
isdn_net_local *mlp = idev->mlp;
if (isdn_net_bound(idev))
return -EBUSY;
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)
goto err;
isdn_slot_set_usage(slot, isdn_slot_usage(slot) | ISDN_USAGE_OUTGOING);
if (isdn_net_bind_channel(idev, slot) < 0)
goto err;
/* Initiate dialing */
dialout_first(&idev->fi, 0, NULL); // FIXME
return 0;
err:
return -EAGAIN;
}
void
isdn_net_accept(isdn_net_dev *idev, int slot, char *nr)
{
isdn_net_local *mlp = idev->mlp;
isdn_ctrl cmd;
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);
idev->outgoing = 0;
idev->charge_state = ST_CHARGE_NULL;
/* Got incoming Call, setup L2 and L3 protocols,
* then wait for D-Channel-connect
*/
cmd.arg = mlp->l2_proto << 8;
isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL2, &cmd);
cmd.arg = mlp->l3_proto << 8;
isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL3, &cmd);
idev->dial_timer.expires = jiffies + mlp->dialtimeout;
idev->dial_event = EV_TIMER_INCOMING;
add_timer(&idev->dial_timer);
fsm_change_state(&idev->fi, ST_IN_WAIT_DCONN);
}
int
isdn_net_do_callback(isdn_net_dev *idev)
{
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)
goto err;
/* Setup dialstate. */
idev->dial_timer.expires = jiffies + mlp->cbdelay;
idev->dial_event = EV_TIMER_CB_IN;
add_timer(&idev->dial_timer);
fsm_change_state(&idev->fi, ST_WAIT_BEFORE_CB);
/* Initiate dialing by returning 2 or 4 */
return (mlp->flags & ISDN_NET_CBHUP) ? 2 : 4;
err:
return 0;
}
/* ---------------------------------------------------------------------- */
/* callbacks in the state machine */
/* ---------------------------------------------------------------------- */
/* Find the idev->dial'th outgoing number. */
static struct isdn_net_phone *
get_outgoing_phone(isdn_net_dev *idev)
{
isdn_net_local *mlp = idev->mlp;
struct isdn_net_phone *phone;
int i = 0;
list_for_each_entry(phone, &mlp->phone[1], list) {
if (i++ == idev->dial)
return phone;
}
return NULL;
}
/* Initiate dialout. */
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) {
isdn_net_unbind_channel(idev);
return;
}
if (list_empty(&mlp->phone[1])) {
isdn_net_unbind_channel(idev);
return;
}
idev->dial = 0;
idev->dialretry = 0;
dialout_next(fi, pr, arg);
}
/* Try dialing the next number. */
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,
.l3_proto = mlp->l3_proto,
.si1 = 7,
.si2 = 0,
.msn = mlp->msn,
.phone = get_outgoing_phone(idev)->num,
};
/* next time, try next number */
idev->dial++;
idev->outgoing = 1;
if (idev->chargeint)
idev->charge_state = ST_CHARGE_HAVE_CINT;
else
idev->charge_state = ST_CHARGE_NULL;
/* 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_TIMER_CB_OUT;
} else {
idev->dial_timer.expires = jiffies + mlp->dialtimeout;
idev->dial_event = EV_TIMER_DIAL;
}
fsm_change_state(&idev->fi, ST_OUT_WAIT_DCONN);
add_timer(&idev->dial_timer);
/* Dial */
isdn_slot_dial(idev->isdn_slot, &dial);
}
/* If we didn't connect within dialtimeout, we give up for now
* and wait for dialwait jiffies before trying again.
*/
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;
fsm_change_state(&idev->fi, ST_OUT_DIAL_WAIT);
isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
/* get next phone number */
if (!get_outgoing_phone(idev)) {
/* otherwise start over at first entry */
idev->dial = 0;
idev->dialretry++;
}
if (idev->dialretry >= mlp->dialmax) {
isdn_net_hangup(idev);
return;
}
idev->dial_event = EV_TIMER_DIAL_WAIT;
mod_timer(&idev->dial_timer, jiffies + mlp->dialwait);
}
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);
}
static void
out_dconn(struct fsm_inst *fi, int pr, void *arg)
{
isdn_net_dev *idev = fi->userdata;
isdn_ctrl cmd;
fsm_change_state(&idev->fi, ST_OUT_WAIT_BCONN);
isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
}
static void
in_dconn(struct fsm_inst *fi, int pr, void *arg)
{
isdn_net_dev *idev = fi->userdata;
isdn_ctrl cmd;
fsm_change_state(&idev->fi, ST_IN_WAIT_BCONN);
isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
}
static void
bconn(struct fsm_inst *fi, int pr, void *arg)
{
isdn_net_dev *idev = fi->userdata;
isdn_net_local *mlp = idev->mlp;
fsm_change_state(&idev->fi, ST_ACTIVE);
if (mlp->onhtime) {
idev->huptimer = 0;
idev->dial_event = EV_TIMER_HUP;
mod_timer(&idev->dial_timer, jiffies + HZ);
} else {
del_timer(&idev->dial_timer);
}
isdn_net_add_to_bundle(mlp, idev);
printk(KERN_INFO "isdn_net: %s connected\n", idev->name);
/* If first Chargeinfo comes before B-Channel connect,
* we correct the timestamp here.
*/
idev->chargetime = jiffies;
idev->transcount = 0;
idev->cps = 0;
idev->last_jiffies = jiffies;
if (mlp->ops->connected)
mlp->ops->connected(idev);
else
isdn_net_dev_wake_queue(idev);
}
/* Check if it's time for idle hang-up */
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",
idev->name, idev->huptimer, mlp->onhtime, idev->chargetime, idev->chargeint);
if (idev->huptimer++ <= mlp->onhtime)
goto mod_timer;
if (mlp->hupflags & ISDN_CHARGEHUP &&
idev->charge_state == ST_CHARGE_HAVE_CINT) {
if (!time_after(jiffies, idev->chargetime
+ idev->chargeint - 2 * HZ))
goto mod_timer;
}
if (idev->outgoing || mlp->hupflags & ISDN_INHUP) {
isdn_net_hangup(idev);
return;
}
mod_timer:
mod_timer(&idev->dial_timer, idev->dial_timer.expires + HZ);
}
/* Charge-info from TelCo. */
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:
idev->charge_state = ST_CHARGE_GOT_CINF;
break;
case ST_CHARGE_GOT_CINF:
idev->charge_state = ST_CHARGE_HAVE_CINT;
/* fall through */
case ST_CHARGE_HAVE_CINT:
idev->chargeint = jiffies - idev->chargetime;
break;
}
idev->chargetime = jiffies;
dbg_net_dial("%s: got CINF\n", idev->name);
}
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);
if (mlp->ops->disconnected)
mlp->ops->disconnected(idev);
isdn_net_rm_from_bundle(idev);
printk(KERN_INFO "%s: disconnected\n", idev->name);
printk(KERN_INFO "%s: Chargesum is %d\n", idev->name,
idev->charge);
isdn_slot_all_eaz(idev->isdn_slot);
isdn_net_unbind_channel(idev);
}
/* Perform hangup for a net-interface. */
int
isdn_net_hangup(isdn_net_dev *idev)
{
isdn_ctrl cmd;
del_timer(&idev->dial_timer);
if (!isdn_net_bound(idev)) {
isdn_BUG();
return 1;
}
printk(KERN_INFO "%s: local hangup\n", idev->name);
isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
return 1;
}
/*
* 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)
{
isdn_net_dev *idev = isdn_slot_idev(idx);
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 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 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;
}
}
int
isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
{
fsm_event(&idev->fi, pr, arg);
}
static void
hang_up(struct fsm_inst *fi, int pr, void *arg)
{
isdn_net_dev *idev = fi->userdata;
isdn_net_hangup(idev);
}
static void
got_bsent(struct fsm_inst *fi, int pr, void *arg)
{
isdn_net_dev *idev = fi->userdata;
isdn_ctrl *c = arg;
isdn_net_bsent(idev, c);
}
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, ...)
{
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);
}
void
isdn_net_lib_init(void)
{
fsm_new(&isdn_net_fsm);
}
void
isdn_net_lib_exit(void)
{
fsm_free(&isdn_net_fsm);
}
......@@ -23,7 +23,7 @@
/* Prototypes */
static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
static int isdn_ppp_closewait(int slot);
static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
static void isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb, int proto);
static int isdn_ppp_if_get_unit(char *namebuf);
static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
......@@ -59,10 +59,10 @@ static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
static ippp_bundle * isdn_ppp_bundle_arr = NULL;
static int isdn_ppp_mp_bundle_array_init(void);
static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );
static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
static int isdn_ppp_mp_init(isdn_net_local *lp, ippp_bundle *add_to);
static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb);
static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
static void isdn_ppp_mp_cleanup(isdn_net_local *lp );
static int isdn_ppp_bundle(struct ippp_struct *, int unit);
#endif /* CONFIG_ISDN_MPP */
......@@ -100,9 +100,8 @@ isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot
* in this case we bind another lp to the master device
*/
static void
isdn_ppp_free(isdn_net_local * lp)
isdn_ppp_free(isdn_net_dev *idev)
{
isdn_net_dev *idev = lp->netdev;
unsigned long flags;
struct ippp_struct *is;
......@@ -116,9 +115,9 @@ isdn_ppp_free(isdn_net_local * lp)
cli();
#ifdef CONFIG_ISDN_MPP
spin_lock(&lp->netdev->pb->lock);
spin_lock(&idev->pb->lock);
#endif
isdn_net_rm_from_bundle(lp);
isdn_net_rm_from_bundle(idev);
#ifdef CONFIG_ISDN_MPP
if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */
isdn_ppp_mp_cleanup(lp);
......@@ -139,7 +138,7 @@ isdn_ppp_free(isdn_net_local * lp)
is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGNED' state */
if (is->debug & 0x1)
printk(KERN_DEBUG "isdn_ppp_free %d %p %p\n", idev->ppp_slot, lp, is->idev);
printk(KERN_DEBUG "isdn_ppp_free %d %p\n", idev->ppp_slot, is->idev);
is->idev = NULL; /* link is down .. set lp to NULL */
idev->ppp_slot = -1; /* is this OK ?? */
......@@ -152,9 +151,8 @@ isdn_ppp_free(isdn_net_local * lp)
* bind isdn_net_local <=> ippp-device
*/
int
isdn_ppp_bind(isdn_net_local * lp)
isdn_ppp_bind(isdn_net_dev *idev)
{
isdn_net_dev *idev = lp->netdev;
int i;
int unit = 0;
long flags;
......@@ -226,10 +224,8 @@ isdn_ppp_bind(isdn_net_local * lp)
*/
static void
isdn_ppp_wakeup_daemon(isdn_net_local *lp)
isdn_ppp_wakeup_daemon(isdn_net_dev *idev)
{
isdn_net_dev *idev = lp->netdev;
if (idev->ppp_slot < 0 || idev->ppp_slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
__FUNCTION__, idev->ppp_slot);
......@@ -314,8 +310,6 @@ isdn_ppp_open(struct inode *ino, struct file *file)
is->maxcid = 16; /* VJ: maxcid */
is->tk = current;
init_waitqueue_head(&is->wq);
is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
is->last = is->rq;
is->minor = minor;
#ifdef CONFIG_ISDN_PPP_VJ
/*
......@@ -337,7 +331,6 @@ static int
isdn_ppp_release(struct inode *ino, struct file *file)
{
uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
int i;
struct ippp_struct *is;
lock_kernel();
......@@ -356,14 +349,7 @@ isdn_ppp_release(struct inode *ino, struct file *file)
is->state &= ~IPPP_CONNECT;
isdn_net_hangup(is->idev);
}
for (i = 0; i < NUM_RCV_BUFFS; i++) {
if (is->rq[i].buf) {
kfree(is->rq[i].buf);
is->rq[i].buf = NULL;
}
}
is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
is->last = is->rq;
skb_queue_purge(&is->rq);
#ifdef CONFIG_ISDN_PPP_VJ
/* TODO: if this was the previous master: link the slcomp to the new master */
......@@ -487,7 +473,7 @@ isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned
if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
if (idev) {
/* OK .. we are ready to send buffers */
netif_wake_queue(&idev->dev);
isdn_net_dev_wake_queue(idev);
}
}
is->pppcfg = val;
......@@ -562,14 +548,16 @@ isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned
return isdn_ppp_set_compressor(is, &data);
case PPPIOCGCALLINFO:
{
isdn_net_local *mlp;
struct isdn_net_phone *phone;
struct pppcallinfo pci;
int i;
memset((char *) &pci,0,sizeof(struct pppcallinfo));
if(idev) {
strncpy(pci.local_num,idev->local.msn,63);
mlp = idev->mlp;
strncpy(pci.local_num, mlp->msn, 63);
i = 0;
list_for_each_entry(phone, &idev->local.phone[1], list) {
list_for_each_entry(phone, &mlp->phone[1], list) {
if (i++ == idev->dial) {
strncpy(pci.remote_num,phone->num,63);
break;
......@@ -580,7 +568,7 @@ isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned
pci.calltype = CALLTYPE_OUTGOING;
else
pci.calltype = CALLTYPE_INCOMING;
if(idev->local.flags & ISDN_NET_CALLBACK)
if(mlp->flags & ISDN_NET_CALLBACK)
pci.calltype |= CALLTYPE_CALLBACK;
}
return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
......@@ -595,12 +583,8 @@ static unsigned int
isdn_ppp_poll(struct file *file, poll_table * wait)
{
unsigned int mask;
struct ippp_buf_queue *bf;
struct ippp_buf_queue *bl;
unsigned long flags;
struct ippp_struct *is;
lock_kernel();
is = file->private_data;
if (is->debug & 0x2)
......@@ -622,21 +606,15 @@ isdn_ppp_poll(struct file *file, poll_table * wait)
/* we're always ready to send .. */
mask = POLLOUT | POLLWRNORM;
save_flags(flags);
cli();
bl = is->last;
bf = is->first;
/*
* if IPPP_NOBLOCK is set we return even if we have nothing to read
*/
if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
if (!skb_queue_empty(&is->rq) || is->state & IPPP_NOBLOCK) {
is->state &= ~IPPP_NOBLOCK;
mask |= POLLIN | POLLRDNORM;
}
restore_flags(flags);
out:
unlock_kernel();
return mask;
}
......@@ -647,10 +625,8 @@ isdn_ppp_poll(struct file *file, poll_table * wait)
static int
isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
{
struct ippp_buf_queue *bf,
*bl;
unsigned long flags;
unsigned char *nbuf;
struct sk_buff *skb;
unsigned char *p;
struct ippp_struct *is;
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
......@@ -663,35 +639,22 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
printk(KERN_DEBUG "ippp: device not activated.\n");
return 0;
}
nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
if (!nbuf) {
printk(KERN_WARNING "ippp: Can't alloc buf\n");
if (skb_queue_len(&is->rq) > IPPP_MAX_RQ_LEN) {
printk(KERN_WARNING "ippp: Queue is full\n");
return 0;
}
nbuf[0] = PPP_ALLSTATIONS;
nbuf[1] = PPP_UI;
nbuf[2] = proto >> 8;
nbuf[3] = proto & 0xff;
memcpy(nbuf + 4, buf, len);
save_flags(flags);
cli();
bf = is->first;
bl = is->last;
if (bf == bl) {
printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
bf = bf->next;
kfree(bf->buf);
is->first = bf;
skb = dev_alloc_skb(len + 4);
if (!skb) {
printk(KERN_WARNING "ippp: Can't alloc buf\n");
return 0;
}
bl->buf = (char *) nbuf;
bl->len = len + 4;
is->last = bl->next;
restore_flags(flags);
p = skb_put(skb, 4);
p += put_u8(p, PPP_ALLSTATIONS);
p += put_u8(p, PPP_UI);
p += put_u16(p, proto);
memcpy(skb_put(skb, len), buf, len);
skb_queue_tail(&is->rq, skb);
wake_up_interruptible(&is->wq);
return len;
......@@ -706,54 +669,36 @@ static ssize_t
isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off)
{
struct ippp_struct *is;
struct ippp_buf_queue *b;
unsigned long flags;
unsigned char *save_buf;
struct sk_buff *skb;
int retval;
if (off != &file->f_pos)
return -ESPIPE;
lock_kernel();
is = file->private_data;
if (!(is->state & IPPP_OPEN)) {
retval = 0;
goto out;
}
retval = verify_area(VERIFY_WRITE, (void *) buf, count);
if (retval)
goto out;
save_flags(flags);
cli();
b = is->first->next;
save_buf = b->buf;
if (!save_buf) {
restore_flags(flags);
skb = skb_dequeue(&is->rq);
if (!skb) {
retval = -EAGAIN;
goto out;
}
if (b->len < count)
count = b->len;
b->buf = NULL;
is->first = b;
restore_flags(flags);
if (copy_to_user(buf, save_buf, count)) {
kfree(save_buf);
if (skb->len > count) {
retval = -EMSGSIZE;
goto out_free;
}
if (copy_to_user(buf, skb->data, skb->len)) {
retval = -EFAULT;
goto out;
goto out_free;
}
kfree(save_buf);
retval = count;
retval = skb->len;
out_free:
dev_kfree_skb(skb);
out:
unlock_kernel();
return retval;
}
......@@ -832,9 +777,9 @@ isdn_ppp_write(struct file *file, const char *buf, size_t count, loff_t *off)
isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,idev->ppp_slot);
}
isdn_ppp_send_ccp(idev,&idev->local,skb); /* keeps CCP/compression states in sync */
isdn_ppp_send_ccp(idev,idev->mlp,skb); /* keeps CCP/compression states in sync */
isdn_net_write_super(&idev->local, skb);
isdn_net_write_super(idev, skb);
}
}
retval = count;
......@@ -881,15 +826,7 @@ isdn_ppp_init(void)
}
memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
ippp_table[i]->state = 0;
ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
ippp_table[i]->last = ippp_table[i]->rq;
for (j = 0; j < NUM_RCV_BUFFS; j++) {
ippp_table[i]->rq[j].buf = NULL;
ippp_table[i]->rq[j].last = ippp_table[i]->rq +
(NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
}
skb_queue_head_init(&ippp_table[i]->rq);
}
return 0;
}
......@@ -963,10 +900,9 @@ static int isdn_ppp_strip_proto(struct sk_buff *skb)
/*
* handler for incoming packets on a syncPPP interface
*/
static void isdn_ppp_receive(isdn_net_dev *net_dev, isdn_net_local *lp,
static void isdn_ppp_receive(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb)
{
isdn_net_dev *idev = lp->netdev;
struct ippp_struct *is;
int slot;
int proto;
......@@ -976,7 +912,7 @@ static void isdn_ppp_receive(isdn_net_dev *net_dev, isdn_net_local *lp,
* huptimer on LCP packets.
*/
if (PPP_PROTOCOL(skb->data) != PPP_LCP)
isdn_net_reset_huptimer(net_dev,lp->netdev);
idev->huptimer = 0;
slot = idev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
......@@ -1012,12 +948,12 @@ static void isdn_ppp_receive(isdn_net_dev *net_dev, isdn_net_local *lp,
if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP
if (proto == PPP_MP) {
isdn_ppp_mp_receive(net_dev, lp, skb);
isdn_ppp_mp_receive(lp, idev, skb);
return;
}
}
#endif
isdn_ppp_push_higher(net_dev, lp, skb, proto);
isdn_ppp_push_higher(lp, idev, skb, proto);
}
/*
......@@ -1026,10 +962,10 @@ static void isdn_ppp_receive(isdn_net_dev *net_dev, isdn_net_local *lp,
* note: net_dev has to be master net_dev
*/
static void
isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
struct sk_buff *skb, int proto)
{
isdn_net_dev *idev = lp->netdev;
struct net_device *dev = &net_dev->dev;
struct net_device *dev = &lp->dev;
struct ippp_struct *is, *mis;
int slot;
......@@ -1041,14 +977,6 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
}
is = ippp_table[slot];
if (lp->master) { // FIXME?
slot = ((isdn_net_local *) (lp->master->priv))->netdev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n",
slot);
goto drop_packet;
}
}
mis = ippp_table[slot];
if (is->debug & 0x10) {
......@@ -1079,12 +1007,12 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
case PPP_VJC_UNCOMP:
if (is->debug & 0x20)
printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
if (net_dev->ppp_slot < 0) {
if (idev->ppp_slot < 0) {
printk(KERN_ERR "%s: net_dev->ppp_slot(%d) out of range\n",
__FUNCTION__ , net_dev->ppp_slot);
__FUNCTION__ , idev->ppp_slot);
goto drop_packet;
}
if (slhc_remember(ippp_table[net_dev->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
if (slhc_remember(ippp_table[idev->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
goto drop_packet;
}
......@@ -1105,12 +1033,12 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
}
skb_put(skb, skb_old->len + 128);
memcpy(skb->data, skb_old->data, skb_old->len);
if (net_dev->ppp_slot < 0) {
if (idev->ppp_slot < 0) {
printk(KERN_ERR "%s: net_dev->ppp_slot(%d) out of range\n",
__FUNCTION__ , net_dev->ppp_slot);
__FUNCTION__ , idev->ppp_slot);
goto drop_packet;
}
pkt_len = slhc_uncompress(ippp_table[net_dev->ppp_slot]->slcomp,
pkt_len = slhc_uncompress(ippp_table[idev->ppp_slot]->slcomp,
skb->data, skb_old->len);
kfree_skb(skb_old);
if (pkt_len < 0)
......@@ -1123,7 +1051,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
#endif
case PPP_CCP:
case PPP_CCPFRAG:
isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
isdn_ppp_receive_ccp(idev,lp,skb,proto);
/* Dont pop up ResetReq/Ack stuff to the daemon any
longer - the job is done already */
if(skb->data[0] == CCP_RESETREQ ||
......@@ -1146,7 +1074,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
return;
drop_packet:
net_dev->local.stats.rx_dropped++;
lp->stats.rx_dropped++;
kfree_skb(skb);
}
......@@ -1184,20 +1112,21 @@ static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
* skb isn't allowed!!
*/
int
isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
static int
isdn_ppp_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
isdn_net_local *lp,*mlp;
isdn_net_dev *idev;
isdn_net_dev *nd;
isdn_net_local *mlp = ndev->priv;
isdn_net_dev *idev = list_entry(mlp->online.next, isdn_net_dev, online);
unsigned int proto = PPP_IP; /* 0x21 */
struct ippp_struct *ipt,*ipts;
int slot;
mlp = (isdn_net_local *) (netdev->priv);
nd = mlp->netdev; /* get master lp */
ndev->trans_start = jiffies;
slot = nd->ppp_slot;
if (list_empty(&mlp->online))
return isdn_net_autodial(skb, ndev);
slot = idev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
slot);
......@@ -1208,7 +1137,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */
if (ipts->debug & 0x1)
printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name);
printk(KERN_INFO "%s: IP frame delayed.\n", ndev->name);
netif_stop_queue(ndev);
return 1;
}
......@@ -1226,13 +1156,13 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
return 0;
}
lp = isdn_net_get_locked_lp(nd);
if (!lp) {
printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
idev = isdn_net_get_locked_dev(mlp);
if (!idev) {
printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
netif_stop_queue(ndev);
return 1;
}
/* we have our lp locked from now on */
idev = lp->netdev;
slot = idev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
......@@ -1385,10 +1315,10 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,idev->ppp_slot);
}
isdn_net_writebuf_skb(lp, skb);
isdn_net_writebuf_skb(idev, skb);
unlock:
spin_unlock_bh(&lp->xmit_lock);
spin_unlock_bh(&idev->xmit_lock);
return 0;
}
......@@ -1459,7 +1389,7 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
is->mp_seqno = 0;
if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
return -ENOMEM;
lp->next = lp->last = lp; /* nobody else in a queue */
lp->netdev->pb->frags = NULL;
lp->netdev->pb->frames = 0;
lp->netdev->pb->seq = LONG_MAX;
......@@ -1479,12 +1409,12 @@ static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );
static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );
static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *dev,
struct sk_buff *skb)
{
isdn_net_dev *idev = lp->netdev;
struct ippp_struct *is;
isdn_net_local * lpq;
isdn_net_dev *qdev;
ippp_bundle * mp;
isdn_mppp_stats * stats;
struct sk_buff * newfrag, * frag, * start, *nextf;
......@@ -1492,8 +1422,8 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
unsigned long flags;
int slot;
spin_lock_irqsave(&net_dev->pb->lock, flags);
mp = net_dev->pb;
spin_lock_irqsave(&lp->netdev->pb->lock, flags);
mp = lp->netdev->pb;
stats = &mp->stats;
slot = idev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
......@@ -1531,8 +1461,8 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
/* find the minimum received sequence number over all links */
is->last_link_seqno = minseq = newseq;
for (lpq = net_dev->queue;;) {
slot = lpq->netdev->ppp_slot;
list_for_each_entry(qdev, &lp->online, online) {
slot = qdev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",
__FUNCTION__ ,slot);
......@@ -1541,8 +1471,6 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
if (MP_LT(lls, minseq))
minseq = lls;
}
if ((lpq = lpq->next) == net_dev->queue)
break;
}
if (MP_LT(minseq, mp->seq))
minseq = mp->seq; /* can't go beyond already processed
......@@ -1631,7 +1559,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) {
minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
/* Reassemble the packet then dispatch it */
isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);
isdn_ppp_mp_reassembly(lp->netdev, lp, start, nextf);
start = NULL;
frag = NULL;
......@@ -1808,7 +1736,7 @@ void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
}
}
proto = isdn_ppp_strip_proto(skb);
isdn_ppp_push_higher(net_dev, lp, skb, proto);
isdn_ppp_push_higher(lp, idev, skb, proto);
}
static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
......@@ -1844,7 +1772,7 @@ isdn_ppp_bundle(struct ippp_struct *is, int unit)
spin_lock_irqsave(&p->pb->lock, flags);
nidev = is->idev;
idev = p->queue->netdev;
idev = list_entry(p->mlp->online.next, isdn_net_dev, online);
if( nidev->ppp_slot < 0 || nidev->ppp_slot >= ISDN_MAX_CHANNELS ||
idev ->ppp_slot < 0 || idev ->ppp_slot >= ISDN_MAX_CHANNELS ) {
printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
......@@ -1854,7 +1782,7 @@ isdn_ppp_bundle(struct ippp_struct *is, int unit)
goto out;
}
isdn_net_add_to_bundle(p, &nidev->local);
isdn_net_add_to_bundle(p->mlp, nidev);
ippp_table[nidev->ppp_slot]->unit = ippp_table[idev->ppp_slot]->unit;
......@@ -1863,7 +1791,7 @@ isdn_ppp_bundle(struct ippp_struct *is, int unit)
(SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
ippp_table[nidev->ppp_slot]->mpppcfg |= ippp_table[idev->ppp_slot]->mpppcfg &
(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
rc = isdn_ppp_mp_init(&nidev->local, p->pb);
rc = isdn_ppp_mp_init(nidev->mlp, p->pb);
out:
spin_unlock_irqrestore(&p->pb->lock, flags);
return rc;
......@@ -1936,7 +1864,7 @@ isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if(copy_to_user(r, PPP_VERSION, len)) error = -EFAULT;
break;
case SIOCGPPPSTATS:
error = isdn_ppp_dev_ioctl_stats(lp->netdev->ppp_slot, ifr, dev);
error = isdn_ppp_dev_ioctl_stats(0, ifr, dev);
break;
default:
error = -EINVAL;
......@@ -1977,28 +1905,25 @@ isdn_ppp_dial_slave(char *name)
{
#ifdef CONFIG_ISDN_MPP
isdn_net_dev *idev;
isdn_net_local *lp;
struct net_device *sdev;
isdn_net_dev *sdev;
idev = isdn_net_findif(name);
if (!idev)
return 1;
lp = &idev->local;
if (!isdn_net_bound(idev))
return 5;
sdev = lp->slave;
sdev = idev->slave;
while (sdev) {
isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
if (!isdn_net_bound(mlp->netdev))
if (!isdn_net_bound(sdev))
break;
sdev = mlp->slave;
sdev = sdev->slave;
}
if (!sdev)
return 2;
isdn_net_dial_req((isdn_net_local *) sdev->priv);
isdn_net_dial_req(sdev);
return 0;
#else
return -1;
......@@ -2009,35 +1934,20 @@ int
isdn_ppp_hangup_slave(char *name)
{
#ifdef CONFIG_ISDN_MPP
isdn_net_dev *idev;
isdn_net_local *lp, *mlp = NULL;
struct net_device *sdev;
isdn_net_dev *idev, *sdev;
idev = isdn_net_findif(name);
if (!idev)
return 1;
lp = &idev->local;
if (!isdn_net_bound(idev))
return 5;
sdev = lp->slave;
while (sdev) {
mlp = (isdn_net_local *) sdev->priv;
if (mlp->slave) { /* find last connected link in chain */
isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv;
if (!isdn_net_bound(nlp->netdev))
break;
} else if (isdn_net_bound(mlp->netdev))
break;
sdev = mlp->slave;
}
if (!sdev)
sdev = idev->slave;
if (!sdev || !isdn_net_bound(sdev))
return 2;
isdn_net_hangup(mlp->netdev);
isdn_net_hangup(sdev);
return 0;
#else
return -1;
......@@ -2139,7 +2049,7 @@ static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
printk(KERN_DEBUG "Sending CCP Frame:\n");
isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
isdn_net_write_super(&idev->local, skb);
isdn_net_write_super(idev, skb);
}
/* Allocate the reset state vector */
......@@ -2537,10 +2447,9 @@ static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
* we received a CCP frame ..
* not a clean solution, but we MUST handle a few cases in the kernel
*/
static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
static void isdn_ppp_receive_ccp(isdn_net_dev *idev, isdn_net_local *lp,
struct sk_buff *skb,int proto)
{
isdn_net_dev *idev = lp->netdev;
struct ippp_struct *is;
struct ippp_struct *mis;
int len;
......@@ -2557,15 +2466,6 @@ static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
is = ippp_table[idev->ppp_slot];
isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
if(lp->master) {
int slot = ((isdn_net_local *) (lp->master->priv))->netdev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: slot(%d) out of range\n",
__FUNCTION__ , slot);
return;
}
mis = ippp_table[slot];
} else
mis = is;
switch(skb->data[0]) {
......@@ -2715,9 +2615,8 @@ static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
* and I tried to modify this file according to that. --abp
*/
static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
static void isdn_ppp_send_ccp(isdn_net_dev *idev, isdn_net_local *lp, struct sk_buff *skb)
{
isdn_net_dev *idev = lp->netdev;
struct ippp_struct *mis,*is;
int proto, slot = idev->ppp_slot;
unsigned char *data;
......@@ -2745,15 +2644,6 @@ static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct
printk(KERN_DEBUG "Received CCP frame from daemon:\n");
isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
if (lp->master) {
slot = ((isdn_net_local *) (lp->master->priv))->netdev->ppp_slot;
if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: slot(%d) out of range\n",
__FUNCTION__ , slot);
return;
}
mis = ippp_table[slot];
} else
mis = is;
if (mis != is)
printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
......@@ -2926,6 +2816,7 @@ isdn_ppp_header(struct sk_buff *skb, struct net_device *dev,
}
struct isdn_netif_ops isdn_ppp_ops = {
.hard_start_xmit = isdn_ppp_start_xmit,
.hard_header = isdn_ppp_header,
.do_ioctl = isdn_ppp_dev_ioctl,
.flags = IFF_NOARP | IFF_POINTOPOINT,
......
......@@ -20,20 +20,6 @@ extern void isdn_ppp_cleanup(void);
extern int isdn_ppp_dial_slave(char *);
extern int isdn_ppp_hangup_slave(char *);
#ifdef CONFIG_ISDN_PPP
int isdn_ppp_xmit(struct sk_buff *, struct net_device *);
#else
static inline int
isdn_ppp_xmit(struct sk_buff *, struct net_device *)
{
return 0;
}
#endif
#define IPPP_OPEN 0x01
#define IPPP_CONNECT 0x02
#define IPPP_CLOSEWAIT 0x04
......
......@@ -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
......@@ -282,6 +285,8 @@ struct isdn_net_dev_s;
struct isdn_net_local_s;
struct isdn_netif_ops {
int (*hard_start_xmit) (struct sk_buff *skb,
struct net_device *dev);
int (*hard_header) (struct sk_buff *skb,
struct net_device *dev,
unsigned short type,
......@@ -294,26 +299,24 @@ struct isdn_netif_ops {
unsigned short flags; /* interface flags (a la BSD) */
unsigned short type; /* interface hardware type */
unsigned char addr_len;/* hardware address length */
void (*receive)(struct isdn_net_dev_s *p,
struct isdn_net_local_s *olp,
struct sk_buff *skb);
void (*connected)(struct isdn_net_local_s *lp);
void (*disconnected)(struct isdn_net_local_s *lp);
int (*bind)(struct isdn_net_local_s *lp);
void (*unbind)(struct isdn_net_local_s *lp);
int (*init)(struct isdn_net_local_s *lp);
void (*cleanup)(struct isdn_net_local_s *lp);
int (*open)(struct isdn_net_local_s *lp);
void (*close)(struct isdn_net_local_s *lp);
void (*receive)(struct isdn_net_local_s *,
struct isdn_net_dev_s *,
struct sk_buff *);
void (*connected)(struct isdn_net_dev_s *);
void (*disconnected)(struct isdn_net_dev_s *);
int (*bind)(struct isdn_net_dev_s *);
void (*unbind)(struct isdn_net_dev_s *);
int (*init)(struct isdn_net_local_s *);
void (*cleanup)(struct isdn_net_local_s *);
int (*open)(struct isdn_net_local_s *);
void (*close)(struct isdn_net_local_s *);
};
/* Local interface-data */
typedef struct isdn_net_local_s {
ulong magic;
spinlock_t lock;
struct net_device_stats stats; /* Ethernet Statistics */
int flags; /* Connection-flags */
int dialretry; /* Counter for Dialout-retries */
int dialmax; /* Max. Number of Dial-retries */
int dialtimeout; /* How long shall we try on dialing */
int dialwait; /* wait after failed attempt */
......@@ -329,26 +332,17 @@ typedef struct isdn_net_local_s {
u_char l2_proto; /* Layer-2-protocol */
u_char l3_proto; /* Layer-3-protocol */
int sqfull; /* Flag: netdev-queue overloaded */
ulong sqfull_stamp; /* Start-Time of overload */
ulong slavedelay; /* Dynamic bundling delaytime */
int triggercps; /* BogoCPS needed for trigger slave */
struct list_head phone[2]; /* List of remote-phonenumbers */
/* phone[0] = Incoming Numbers */
/* phone[1] = Outgoing Numbers */
struct net_device *master; /* Ptr to Master device for slaves */
struct net_device *slave; /* Ptr to Slave device for masters */
struct isdn_net_local_s *next; /* Ptr to next link in bundle */
struct isdn_net_local_s *last; /* Ptr to last link in bundle */
struct isdn_net_dev_s *netdev; /* Ptr to netdev */
struct sk_buff_head super_tx_queue; /* List of supervisory frames to */
/* be transmitted asap */
atomic_t frame_cnt; /* number of frames currently */
/* queued in HL driver */
/* Ptr to orig. hard_header_cache */
spinlock_t xmit_lock; /* used to protect the xmit path of */
/* a particular channel (including */
/* the frame_cnt */
struct list_head slaves; /* list of all bundled channels */
struct list_head online; /* circular list of all bundled
channels, which are currently
online */
spinlock_t online_lock; /* lock to protect queue */
#ifdef CONFIG_ISDN_X25
struct concap_device_ops *dops; /* callbacks used by encapsulator */
......@@ -361,33 +355,34 @@ typedef struct isdn_net_local_s {
ulong cisco_last_slarp_in; /* jiffie of last keepalive packet we received */
char cisco_line_state; /* state of line according to keepalive packets */
char cisco_debserint; /* debugging flag of cisco hdlc with slarp */
struct timer_list cisco_timer;
struct work_struct tqueue;
struct isdn_netif_ops *ops;
struct net_device dev; /* interface to upper levels */
} isdn_net_local;
/* the interface itself */
typedef struct isdn_net_dev_s {
isdn_net_local local;
int isdn_slot; /* Index to isdn device/channel */
int pre_device; /* Preselected isdn-device */
int pre_channel; /* Preselected isdn-channel */
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 */
unsigned long dialstarted; /* first dialing-attempt */
unsigned long dialwait_timer;/* earliest next dialing-attempt */
int dialretry; /* Counter for Dialout-retries */
int cps; /* current speed of this interface */
int transcount; /* byte-counter for cps-calculation */
int last_jiffies; /* when transcount was reset */
int sqfull; /* Flag: netdev-queue overloaded */
ulong sqfull_stamp; /* Start-Time of overload */
struct timer_list hup_timer; /* auto hangup timer */
int huptimer; /* Timeout-counter for auto-hangup */
int charge; /* Counter for charging units */
int charge_state; /* ChargeInfo state machine */
......@@ -397,13 +392,22 @@ typedef struct isdn_net_dev_s {
int pppbind; /* ippp device for bindings */
int ppp_slot; /* PPPD device slot number */
isdn_net_local *queue; /* circular list of all bundled
channels, which are currently
online */
spinlock_t queue_lock; /* lock to protect queue */
spinlock_t xmit_lock; /* used to protect the xmit path of */
/* a particular channel (including */
/* the frame_cnt */
struct sk_buff_head super_tx_queue; /* List of supervisory frames to */
/* be transmitted asap */
atomic_t frame_cnt; /* number of frames currently */
/* queued in HL driver */
struct tasklet_struct tlet;
isdn_net_local *mlp; /* Ptr to master device for all devs*/
struct list_head slaves; /* Members of local->slaves */
struct list_head online; /* Members of local->online */
char name[10]; /* Name of device */
struct list_head global_list; /* global list of all isdn_net_devs */
struct net_device dev; /* interface to upper levels */
#ifdef CONFIG_ISDN_PPP
ippp_bundle * pb; /* pointer to the common bundle structure
* with the per-bundle data */
......
......@@ -159,14 +159,7 @@ typedef struct {
isdn_mppp_stats stats;
} ippp_bundle;
#define NUM_RCV_BUFFS 64
struct ippp_buf_queue {
struct ippp_buf_queue *next;
struct ippp_buf_queue *last;
char *buf; /* NULL here indicates end of queue */
int len;
};
#define IPPP_MAX_RQ_LEN 8
/* The data structure for one CCP reset transaction */
enum ippp_ccp_reset_states {
......@@ -201,9 +194,7 @@ struct ippp_ccp_reset {
struct ippp_struct {
struct ippp_struct *next_link;
int state;
struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */
struct ippp_buf_queue *first; /* pointer to (current) first packet */
struct ippp_buf_queue *last; /* pointer to (current) last used packet in queue */
struct sk_buff_head rq;
wait_queue_head_t wq;
struct task_struct *tk;
unsigned int mpppcfg;
......
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