diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2002-10-06 19:18:52 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-10-06 19:18:52 -0700 |
| commit | 6e7aa9f00e155b5ffb39ca6342c9ea96a1cc5051 (patch) | |
| tree | d50d8d2115c3c6d96f3c50d1c76462f006248312 | |
| parent | c27b8aa3ddd2be97dda447b324a5f0bc545c717c (diff) | |
| parent | 9c06758cdc72e6c445fa6488a660cb8a3c7d008c (diff) | |
Merge http://linux-isdn.bkbits.net/linux-2.5.isdn
into home.transmeta.com:/home/torvalds/v2.5/linux
31 files changed, 2503 insertions, 2572 deletions
@@ -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 # --------------------------------------------------------------------------- diff --git a/drivers/isdn/eicon/Divas_mod.c b/drivers/isdn/eicon/Divas_mod.c index 3bfa23b57591..c1443a676dfd 100644 --- a/drivers/isdn/eicon/Divas_mod.c +++ b/drivers/isdn/eicon/Divas_mod.c @@ -10,7 +10,7 @@ #undef N_DATA #include <linux/kernel.h> - +#include <linux/tqueue.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/ioport.h> @@ -25,6 +25,8 @@ MODULE_DESCRIPTION("ISDN4Linux: Driver for Eicon Diva Server cards"); MODULE_AUTHOR("Armin Schindler"); MODULE_LICENSE("GPL"); +void DivasInitDpc(void); + #ifdef MODULE #include "idi.h" void DIVA_DIDD_Write(DESCRIPTOR *, int); @@ -47,6 +49,8 @@ divas_init(void) return -ENODEV; #endif + DivasInitDpc(); + if (pci_present()) { if (DivasCardsDiscover() < 0) diff --git a/drivers/isdn/eicon/adapter.h b/drivers/isdn/eicon/adapter.h index 761aebcd5f5b..b861d3557904 100644 --- a/drivers/isdn/eicon/adapter.h +++ b/drivers/isdn/eicon/adapter.h @@ -176,8 +176,7 @@ extern void DivasLogAdd(void *buffer, int length); /*------------------------------------------------------------------*/ int DivasDpcSchedule(void); -void DivasDoDpc(void *); -void DivasDoRequestDpc(void *pData); +void DivasDoDpc(unsigned long); int DivasScheduleRequestDpc(void); /* table of IDI request functions */ diff --git a/drivers/isdn/eicon/common.c b/drivers/isdn/eicon/common.c index 77d3a50b922a..b9ec4041d3b3 100644 --- a/drivers/isdn/eicon/common.c +++ b/drivers/isdn/eicon/common.c @@ -801,7 +801,7 @@ void DivaDoCardDpc(card_t *card) } -void DivasDoDpc(void *pData) +void DivasDoDpc(unsigned long dummy) { card_t *card = DivasCards; int i = DivasCardNext; @@ -814,11 +814,6 @@ void DivasDoDpc(void *pData) } } -void DivasDoRequestDpc(void *pData) -{ - DivasDoDpc(pData); -} - /* * DivasGetNum * Returns the number of active adapters diff --git a/drivers/isdn/eicon/eicon.h b/drivers/isdn/eicon/eicon.h index c925f21b2f70..f2c6a043a056 100644 --- a/drivers/isdn/eicon/eicon.h +++ b/drivers/isdn/eicon/eicon.h @@ -1,4 +1,4 @@ -/* $Id: eicon.h,v 1.23.6.5 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon.h,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $ * * ISDN low-level module for Eicon active ISDN-Cards. * @@ -321,9 +321,9 @@ typedef struct eicon_card { struct sk_buff_head sackq; /* Data-Ack-Message queue */ struct sk_buff_head statq; /* Status-Message queue */ int statq_entries; - struct work_struct snd_tq; /* Task struct for xmit bh */ - struct work_struct rcv_tq; /* Task struct for rcv bh */ - struct work_struct ack_tq; /* Task struct for ack bh */ + struct tasklet_struct snd_tq; /* Task struct for xmit bh */ + struct tasklet_struct rcv_tq; /* Task struct for rcv bh */ + struct tasklet_struct ack_tq; /* Task struct for ack bh */ eicon_chan* IdTable[256]; /* Table to find entity */ __u16 ref_in; __u16 ref_out; @@ -349,17 +349,17 @@ extern char *eicon_ctype_name[]; extern __inline__ void eicon_schedule_tx(eicon_card *card) { - schedule_work(&card->snd_tq); + tasklet_schedule(&card->snd_tq); } extern __inline__ void eicon_schedule_rx(eicon_card *card) { - schedule_work(&card->rcv_tq); + tasklet_schedule(&card->rcv_tq); } extern __inline__ void eicon_schedule_ack(eicon_card *card) { - schedule_work(&card->ack_tq); + tasklet_schedule(&card->ack_tq); } extern int eicon_addcard(int, int, int, char *, int); diff --git a/drivers/isdn/eicon/eicon_dsp.h b/drivers/isdn/eicon/eicon_dsp.h index 2ccfeb0fb815..299edcaab27b 100644 --- a/drivers/isdn/eicon/eicon_dsp.h +++ b/drivers/isdn/eicon/eicon_dsp.h @@ -1,4 +1,4 @@ -/* $Id: eicon_dsp.h,v 1.7.6.1 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon_dsp.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $ * * ISDN lowlevel-module for Eicon active cards. * DSP definitions diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c index cfa93399deab..fc341e1b396c 100644 --- a/drivers/isdn/eicon/eicon_idi.c +++ b/drivers/isdn/eicon/eicon_idi.c @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.c,v 1.41.6.4 2001/11/06 20:58:29 kai Exp $ +/* $Id: eicon_idi.c,v 1.1.4.1.2.4 2002/10/01 11:29:13 armin Exp $ * * ISDN lowlevel-module for Eicon active cards. * IDI interface @@ -24,7 +24,7 @@ #undef EICON_FULL_SERVICE_OKTETT -char *eicon_idi_revision = "$Revision: 1.41.6.4 $"; +char *eicon_idi_revision = "$Revision: 1.1.4.1.2.4 $"; eicon_manifbuf *manbuf; diff --git a/drivers/isdn/eicon/eicon_idi.h b/drivers/isdn/eicon/eicon_idi.h index c0cab657b4d9..d39484be78ce 100644 --- a/drivers/isdn/eicon/eicon_idi.h +++ b/drivers/isdn/eicon/eicon_idi.h @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.h,v 1.11.6.1 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon_idi.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $ * * ISDN lowlevel-module for the Eicon active cards. * IDI-Interface diff --git a/drivers/isdn/eicon/eicon_io.c b/drivers/isdn/eicon/eicon_io.c index ff61c5f10c5a..806e3771b503 100644 --- a/drivers/isdn/eicon/eicon_io.c +++ b/drivers/isdn/eicon/eicon_io.c @@ -1,4 +1,4 @@ -/* $Id: eicon_io.c,v 1.13.6.2 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon_io.c,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $ * * ISDN low-level module for Eicon active ISDN-Cards. * Code for communicating with hardware. diff --git a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c index a925d1f3e962..1b0ff98d150b 100644 --- a/drivers/isdn/eicon/eicon_isa.c +++ b/drivers/isdn/eicon/eicon_isa.c @@ -1,4 +1,4 @@ -/* $Id: eicon_isa.c,v 1.16.6.2 2001/11/06 20:58:29 kai Exp $ +/* $Id: eicon_isa.c,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $ * * ISDN low-level module for Eicon active ISDN-Cards. * Hardware-specific code for old ISA cards. @@ -20,7 +20,7 @@ #define release_shmem release_region #define request_shmem request_region -char *eicon_isa_revision = "$Revision: 1.16.6.2 $"; +char *eicon_isa_revision = "$Revision: 1.1.4.1.2.3 $"; #undef EICON_MCA_DEBUG diff --git a/drivers/isdn/eicon/eicon_isa.h b/drivers/isdn/eicon/eicon_isa.h index 7502aabecb8c..47aa5650601e 100644 --- a/drivers/isdn/eicon/eicon_isa.h +++ b/drivers/isdn/eicon/eicon_isa.h @@ -1,4 +1,4 @@ -/* $Id: eicon_isa.h,v 1.10.6.1 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon_isa.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $ * * ISDN low-level module for Eicon active ISDN-Cards. * diff --git a/drivers/isdn/eicon/eicon_mod.c b/drivers/isdn/eicon/eicon_mod.c index f51cffef1d6a..02d594281783 100644 --- a/drivers/isdn/eicon/eicon_mod.c +++ b/drivers/isdn/eicon/eicon_mod.c @@ -1,4 +1,4 @@ -/* $Id: eicon_mod.c,v 1.37.6.6 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon_mod.c,v 1.1.4.1.2.4 2002/10/01 11:29:13 armin Exp $ * * ISDN lowlevel-module for Eicon active cards. * @@ -44,7 +44,7 @@ static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains start of card-list */ -static char *eicon_revision = "$Revision: 1.37.6.6 $"; +static char *eicon_revision = "$Revision: 1.1.4.1.2.4 $"; extern char *eicon_pci_revision; extern char *eicon_isa_revision; @@ -142,8 +142,10 @@ eicon_findnpcicard(int driverid) #endif /* CONFIG_PCI */ static void -eicon_rcv_dispatch(struct eicon_card *card) +eicon_rcv_dispatch(unsigned long context) { + struct eicon_card *card = (struct eicon_card *)context; + switch (card->bus) { case EICON_BUS_ISA: case EICON_BUS_MCA: @@ -152,13 +154,15 @@ eicon_rcv_dispatch(struct eicon_card *card) break; default: eicon_log(card, 1, - "eicon_ack_dispatch: Illegal bustype %d\n", card->bus); + "eicon_rcv_dispatch: Illegal bustype %d\n", card->bus); } } static void -eicon_ack_dispatch(struct eicon_card *card) +eicon_ack_dispatch(unsigned long context) { + struct eicon_card *card = (struct eicon_card *)context; + switch (card->bus) { case EICON_BUS_ISA: case EICON_BUS_MCA: @@ -172,8 +176,10 @@ eicon_ack_dispatch(struct eicon_card *card) } static void -eicon_transmit(struct eicon_card *card) +eicon_transmit(unsigned long context) { + struct eicon_card *card = (struct eicon_card *)context; + switch (card->bus) { case EICON_BUS_ISA: case EICON_BUS_MCA: @@ -868,12 +874,9 @@ eicon_alloccard(int Type, int membase, int irq, char *id, int card_id) skb_queue_head_init(&card->sackq); skb_queue_head_init(&card->statq); card->statq_entries = 0; - card->snd_tq.routine = (void *) (void *) eicon_transmit; - card->snd_tq.data = card; - card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch; - card->rcv_tq.data = card; - card->ack_tq.routine = (void *) (void *) eicon_ack_dispatch; - card->ack_tq.data = card; + tasklet_init(&card->snd_tq, eicon_transmit, (unsigned long)card); + tasklet_init(&card->rcv_tq, eicon_rcv_dispatch, (unsigned long)card); + tasklet_init(&card->ack_tq, eicon_ack_dispatch, (unsigned long)card); card->interface.maxbufsize = 4000; card->interface.command = if_command; card->interface.writebuf_skb = if_sendbuf; diff --git a/drivers/isdn/eicon/eicon_pci.c b/drivers/isdn/eicon/eicon_pci.c index 69c0a0b790a6..6246162dcda4 100644 --- a/drivers/isdn/eicon/eicon_pci.c +++ b/drivers/isdn/eicon/eicon_pci.c @@ -1,4 +1,4 @@ -/* $Id: eicon_pci.c,v 1.15.6.3 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon_pci.c,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $ * * ISDN low-level module for Eicon active ISDN-Cards. * Hardware-specific code for PCI cards. @@ -24,7 +24,7 @@ #include "adapter.h" #include "uxio.h" -char *eicon_pci_revision = "$Revision: 1.15.6.3 $"; +char *eicon_pci_revision = "$Revision: 1.1.4.1.2.3 $"; #if CONFIG_PCI /* intire stuff is only for PCI */ #ifdef CONFIG_ISDN_DRV_EICON_PCI diff --git a/drivers/isdn/eicon/eicon_pci.h b/drivers/isdn/eicon/eicon_pci.h index 7e615c6f3e0b..7f58e9b636b3 100644 --- a/drivers/isdn/eicon/eicon_pci.h +++ b/drivers/isdn/eicon/eicon_pci.h @@ -1,4 +1,4 @@ -/* $Id: eicon_pci.h,v 1.6.6.1 2001/09/23 22:24:37 kai Exp $ +/* $Id: eicon_pci.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $ * * ISDN low-level module for Eicon active ISDN-Cards (PCI part). * diff --git a/drivers/isdn/eicon/linio.c b/drivers/isdn/eicon/linio.c index b20057bb279d..1756c671045c 100644 --- a/drivers/isdn/eicon/linio.c +++ b/drivers/isdn/eicon/linio.c @@ -10,6 +10,10 @@ #define N_DATA +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/smp_lock.h> #include <asm/io.h> #include <asm/system.h> #include <linux/slab.h> @@ -24,7 +28,7 @@ int log_on=0; int Divasdevflag = 0; -//spinlock_t diva_lock = SPIN_LOCK_UNLOCKED; +spinlock_t diva_lock = SPIN_LOCK_UNLOCKED; static ux_diva_card_t card_pool[MAX_CARDS]; @@ -673,20 +677,14 @@ long UxCardLock(ux_diva_card_t *card) { unsigned long flags; - //spin_lock_irqsave(&diva_lock, flags); + spin_lock_irqsave(&diva_lock, flags); - save_flags(flags); - cli(); return flags; - } void UxCardUnlock(ux_diva_card_t *card, long ipl) { - //spin_unlock_irqrestore(&diva_lock, ipl); - - restore_flags(ipl); - + spin_unlock_irqrestore(&diva_lock, ipl); } dword UxTimeGet(void) diff --git a/drivers/isdn/eicon/linsys.c b/drivers/isdn/eicon/linsys.c index 30680c6c9086..afe772fbb5d5 100644 --- a/drivers/isdn/eicon/linsys.c +++ b/drivers/isdn/eicon/linsys.c @@ -24,6 +24,8 @@ struct pt_regs; #include "uxio.h" +static struct tasklet_struct DivasTask; + int Divas4BRIInitPCI(card_t *card, dia_card_t *cfg) { /* Use UxPciConfigWrite routines to initialise PCI config space */ @@ -79,24 +81,23 @@ int DivasBRIInitPCI(card_t *card, dia_card_t *cfg) int DivasDpcSchedule(void) { - static struct work_struct DivasTask; - - INIT_WORK(&DivasTask, DivasDoDpc, NULL); - schedule_work(&DivasTask); + tasklet_schedule(&DivasTask); return 0; } int DivasScheduleRequestDpc(void) { - static struct work_struct DivasTask; - - INIT_WORK(&DivasTask, DivasDoRequestDpc, NULL); - schedule_work(&DivasTask); + tasklet_schedule(&DivasTask); return 0; } +void DivasInitDpc(void) +{ + tasklet_init(&DivasTask, DivasDoDpc, 0); +} + void DivasLogAdd(void *buffer, int length) { static diff --git a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile index 65055e42faf2..407501c0d6b1 100644 --- a/drivers/isdn/i4l/Makefile +++ b/drivers/isdn/i4l/Makefile @@ -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. diff --git a/drivers/isdn/i4l/isdn_ciscohdlck.c b/drivers/isdn/i4l/isdn_ciscohdlck.c index 6a72d3bc1cf1..0f08f3daea88 100644 --- a/drivers/isdn/i4l/isdn_ciscohdlck.c +++ b/drivers/isdn/i4l/isdn_ciscohdlck.c @@ -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,62 +42,59 @@ 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: @@ -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); + isdn_net_write_super(idev, skb); - lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ; - - 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, diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index d8709313dc56..5a2b4b527a29 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -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) +isdn_slot_set_idev(int sl, isdn_net_dev *idev) { BUG_ON(sl < 0); - slot[sl].rx_netdev = nd; + slot[sl].idev = idev; } isdn_net_dev * -isdn_slot_rx_netdev(int sl) +isdn_slot_idev(int sl) { BUG_ON(sl < 0); - return slot[sl].rx_netdev; -} - -void -isdn_slot_set_st_netdev(int sl, isdn_net_dev *nd) -{ - BUG_ON(sl < 0); - - slot[sl].st_netdev = nd; -} - -isdn_net_dev * -isdn_slot_st_netdev(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")) diff --git a/drivers/isdn/i4l/isdn_common.h b/drivers/isdn/i4l/isdn_common.h index 9e4b37b0c7ce..d0ea6f37088c 100644 --- a/drivers/isdn/i4l/isdn_common.h +++ b/drivers/isdn/i4l/isdn_common.h @@ -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); diff --git a/drivers/isdn/i4l/isdn_concap.c b/drivers/isdn/i4l/isdn_concap.c index e325ce283cea..c7db6ce74843 100644 --- a/drivers/isdn/i4l/isdn_concap.c +++ b/drivers/isdn/i4l/isdn_concap.c @@ -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, diff --git a/drivers/isdn/i4l/isdn_concap.h b/drivers/isdn/i4l/isdn_concap.h index 94c7c83374cb..8150d6f595e0 100644 --- a/drivers/isdn/i4l/isdn_concap.h +++ b/drivers/isdn/i4l/isdn_concap.h @@ -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 diff --git a/drivers/isdn/i4l/isdn_fsm.c b/drivers/isdn/i4l/isdn_fsm.c new file mode 100644 index 000000000000..52de59763fe8 --- /dev/null +++ b/drivers/isdn/i4l/isdn_fsm.c @@ -0,0 +1,169 @@ +/* $Id: fsm.c,v 1.14.6.4 2001/09/23 22:24:47 kai Exp $ + * + * Finite state machine + * + * Author Karsten Keil + * Copyright by Karsten Keil <keil@isdn4linux.de> + * by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Jan den Ouden + * Fritz Elfert + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/string.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].fn; + } + 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]); + + return fn(fi, event, arg); +} + +void +fsm_change_state(struct fsm_inst *fi, int newstate) +{ + fi->state = newstate; + if (fi->debug) + fi->printdebug(fi, "ChangeState %s", + fi->fsm->st_str[newstate]); +} + +#if 0 +static void +FsmExpireTimer(struct FsmTimer *ft) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); +#endif + FsmEvent(ft->fi, ft->event, ft->arg); +} + +void +FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) +{ + ft->fi = fi; + ft->tl.function = (void *) FsmExpireTimer; + ft->tl.data = (long) ft; +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft); +#endif + init_timer(&ft->tl); +} + +void +FsmDelTimer(struct FsmTimer *ft, int where) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where); +#endif + del_timer(&ft->tl); +} + +int +FsmAddTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) { + printk(KERN_WARNING "FsmAddTimer: timer already active!\n"); + ft->fi->printdebug(ft->fi, "FsmAddTimer already active!"); + return -1; + } + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); + return 0; +} + +void +FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); +} +#endif diff --git a/drivers/isdn/i4l/isdn_fsm.h b/drivers/isdn/i4l/isdn_fsm.h new file mode 100644 index 000000000000..4a48a401da45 --- /dev/null +++ b/drivers/isdn/i4l/isdn_fsm.h @@ -0,0 +1,61 @@ +/* $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 __ISDN_FSM_H__ +#define __ISDN_FSM_H__ + +#include <linux/kernel.h> +#include <linux/timer.h> + +struct fsm_inst; + +typedef int (*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; + fsm_fn fn; +}; + +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 diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index a64b0e3d8b6f..43da8a9e46e8 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -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,611 +163,48 @@ 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_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); - - 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); - } - idev->isdn_slot = -1; - - restore_flags(flags); -} - -/* - * Assign an ISDN-channel to a net-interface - */ -static int -isdn_net_bind_channel(isdn_net_local *lp, int idx) +isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev) { - 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 (retval < 0) - isdn_net_unbind_channel(lp); - - restore_flags(flags); - return retval; -} - -/* - * 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. - */ - -static void isdn_net_hup_timer(unsigned long data) -{ - isdn_net_dev *idev = (isdn_net_dev *) data; - isdn_net_local *lp = &idev->local; - - if (!isdn_net_online(idev)) { - isdn_BUG(); - return; - } - - 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; - } - } - } else if (lp->hupflags & ISDN_INHUP) { - isdn_net_hangup(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); -} - -static void isdn_net_connected(isdn_net_local *lp) -{ - isdn_net_dev *idev = lp->netdev; - - 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); - } - } - 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. - */ -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) -{ - isdn_net_dev *idev = (isdn_net_dev *) data; - - 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; - - idev->dial = 0; - - if (lp->dialtimeout > 0 && - (idev->dialstarted == 0 || - time_after(jiffies, idev->dialstarted + lp->dialtimeout + lp->dialwait))) { - idev->dialstarted = jiffies; - idev->dialwait_timer = 0; - } - lp->dialretry = 0; - do_dialout(lp); -} - -/* 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++; - - 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. - */ - 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; - } - idev->dialstate = ST_OUT_WAIT_DCONN; - add_timer(&idev->dial_timer); -} - -/* 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; - } - 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 char *p = skb->nh.raw; /* hopefully, this was set correctly */ unsigned short proto = ntohs(skb->protocol); int data_ofs; - ip_ports *ipp; + struct ip_ports { + unsigned short source; + unsigned short dest; + } *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); + case ETH_P_IP: + switch (p[9]) { + case IPPROTO_ICMP: + strcpy(addinfo, "ICMP"); 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]); + 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]); + } + printk(KERN_INFO + "OPEN: %u.%u.%u.%u -> %u.%u.%u.%u %s\n", + + 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); } } @@ -845,56 +213,40 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) * 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) +void +isdn_net_write_super(isdn_net_dev *idev, 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); + skb_queue_tail(&idev->super_tx_queue, skb); + + tasklet_schedule(&idev->tlet); return; } - spin_lock_bh(&lp->xmit_lock); - if (!isdn_net_lp_busy(lp)) { - isdn_net_writebuf_skb(lp, skb); + spin_lock_bh(&idev->xmit_lock); + if (!isdn_net_dev_busy(idev)) { + isdn_net_writebuf_skb(idev, skb); } else { - skb_queue_tail(&lp->super_tx_queue, skb); + skb_queue_tail(&idev->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); + spin_unlock_bh(&idev->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 + * it's serialized by the caller holding the idev->xmit_lock spinlock */ -void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb) +void isdn_net_writebuf_skb(isdn_net_dev *idev, struct sk_buff *skb) { - isdn_net_dev *idev = lp->netdev; + isdn_net_local *mlp = idev->mlp; 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)) { + if (isdn_net_dev_busy(idev)) { isdn_BUG(); goto error; } @@ -911,18 +263,28 @@ void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb) } idev->transcount += len; - isdn_net_inc_frame_cnt(lp); + isdn_net_inc_frame_cnt(idev); return; error: dev_kfree_skb(skb); - lp->stats.tx_errors++; + mlp->stats.tx_errors++; } +static void +isdn_net_dial_slave(isdn_net_local *mlp) +{ + isdn_net_dev *idev; + + list_for_each_entry(idev, &mlp->slaves, slaves) { + if (!isdn_net_bound(idev)) { + isdn_net_dial(idev); + break; + } + } +} /* - * 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 @@ -931,37 +293,27 @@ void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb) * Return: 0 on success, !0 on failure. */ -static int -isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb) +int +isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) { - isdn_net_dev *nd, *idev; - isdn_net_local *slp; - isdn_net_local *lp = ndev->priv; - int retv = 0; + isdn_net_dev *idev; + isdn_net_local *mlp = ndev->priv; - if (lp->master) { - isdn_BUG(); - dev_kfree_skb(skb); - return 0; - } + ndev->trans_start = jiffies; - /* 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) { + if (list_empty(&mlp->online)) + return isdn_net_autodial(skb, ndev); + + 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; } - idev = lp->netdev; - /* we have our lp locked from now on */ + /* we have our idev locked from now on */ - /* Reset hangup-timeout */ - idev->huptimer = 0; // FIXME? - isdn_net_writebuf_skb(lp, skb); - spin_unlock_bh(&lp->xmit_lock); + isdn_net_writebuf_skb(idev, skb); + spin_unlock_bh(&idev->xmit_lock); /* the following stuff is here for backwards compatibility. * in future, start-up and hangup of slaves (based on current load) @@ -976,88 +328,50 @@ isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb) 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); - } - } + 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); } } } else { - if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) { - lp->sqfull = 0; + 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); - } + list_del(&mlp->online); + list_add_tail(&mlp->online, &idev->online); } -} - - -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); + return 0; } -static int +int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev) { - isdn_net_local *lp = ndev->priv; - isdn_net_dev *idev = lp->netdev; + isdn_net_local *mlp = ndev->priv; + isdn_net_dev *idev = list_entry(mlp->slaves.next, isdn_net_dev, slaves); - 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; + /* are we dialing already? */ + if (isdn_net_bound(idev)) + goto stop_queue; - idev->dialwait_timer = 0; - } + if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO) + goto discard; - if (isdn_net_force_dial_lp(lp) < 0) + if (isdn_net_dial(idev) < 0) goto discard; /* Log packet, which triggered dialing */ if (dev->net_verbose) - isdn_net_log_skb(skb, lp); + isdn_net_log_skb(skb, idev); + stop_queue: netif_stop_queue(ndev); return 1; @@ -1069,103 +383,23 @@ isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev) /* - * 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_receive(isdn_net_dev *idev, 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' */ + isdn_net_local *mlp = idev->mlp; 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; + 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); - lp->ops->receive(lp->netdev, olp, skb); + mlp->ops->receive(mlp, idev, skb); } /* @@ -1176,73 +410,16 @@ isdn_net_receive(struct net_device *ndev, struct sk_buff *skb) int isdn_net_rcv_skb(int idx, struct sk_buff *skb) { - isdn_net_dev *idev = isdn_slot_rx_netdev(idx); + isdn_net_dev *idev = isdn_slot_idev(idx); - if (!idev) + if (!idev) { + HERE; 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: + isdn_net_receive(idev, skb); return 0; } @@ -1268,13 +445,12 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) char *eaz; unsigned char si1, si2; int match_more = 0; + int retval; struct list_head *l; struct isdn_net_phone *n; ulong flags; char nr[32]; char *my_eaz; - int retval; - isdn_ctrl cmd; int slot = isdn_dc2minor(di, ch); /* Search name in netdev-chain */ @@ -1312,13 +488,13 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) 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; + isdn_net_local *mlp = idev->mlp; /* 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); + dbg_net_icall("n_fi: if='%s', l.msn=%s, l.flags=%#x, l.dstate=%d\n", + idev->name, mlp->msn, mlp->flags, idev->fi.state); - my_eaz = isdn_slot_map_eaz2msn(slot, lp->msn); + 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') @@ -1345,7 +521,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) continue; dbg_net_icall("n_fi: match1, pdev=%d pch=%d\n", - lp->pre_device, lp->pre_channel); + idev->pre_device, idev->pre_channel); if (isdn_slot_usage(idx) & ISDN_USAGE_EXCLUSIVE && (idev->pre_channel != ch || idev->pre_device != di)) { @@ -1353,15 +529,12 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) 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 (mlp->flags & ISDN_NET_SECURE) { + list_for_each_entry(n, &mlp->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: @@ -1373,7 +546,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) * If so, no dialin is allowed, * so reject actively. * */ - if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) { + if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) { restore_flags(flags); printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n", idev->name); @@ -1389,58 +562,15 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) 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); + if (mlp->flags & ISDN_NET_CALLBACK) { + retval = isdn_net_do_callback(idev); + restore_flags(flags); + return retval; + } + printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", + idev->name, nr, eaz); - isdn_net_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; - + isdn_net_accept(idev, idx, nr); restore_flags(flags); return 1; } @@ -1451,751 +581,19 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) } /* - * 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) +isdn_net_dial_req(isdn_net_dev *idev) { + isdn_net_local *mlp = idev->mlp; /* 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)) + if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO) 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'; - - return 0; -} - -/* - * Add a phone-number to an interface. - */ -int -isdn_net_addphone(isdn_net_ioctl_phone * phone) -{ - isdn_net_dev *p = isdn_net_findif(phone->name); - unsigned long flags; - struct isdn_net_phone *n; - - if (!p) - return -ENODEV; - - n = kmalloc(sizeof(*n), GFP_KERNEL); - if (!n) - return -ENOMEM; - - 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); - 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. - */ -int -isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones) -{ - 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++; - - buf[count-1] = 0; - - if (copy_to_user(phones, buf, count)) - count = -EFAULT; - - free_page((unsigned long)buf); - return count; -} - -/* - * Copy a string containing the peer's phone number of a connected interface - * to user space. - */ -int -isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer) -{ - isdn_net_dev *p = isdn_net_findif(phone->name); - int 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; - 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; -} - -/* - * Delete all phone-numbers of an interface. - */ -static int -isdn_net_rmallphone(isdn_net_dev * p) -{ - struct isdn_net_phone *n; - unsigned long flags; - int i; - - 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); - } - } - spin_lock_irqsave(&p->local.lock, flags); - 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; - - if (!idev) - return -ENODEV; - - if (idev->isdn_slot < 0) - return -ENOTCONN; - - 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); - } - 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; - - 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; - - 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; - } - } - } - restore_flags(flags); - kfree(p); - - return 0; -} - -/* - * Remove a single network-interface. - */ -int -isdn_net_rm(char *name) -{ - struct list_head *l; - - /* 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); - } - return -ENODEV; -} - -/* - * Remove all network-interfaces - */ -int -isdn_net_rmall(void) -{ - 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); - - /* 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, ðer_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(); } + diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h index 5b80bd7dbd97..cf7917a8922a 100644 --- a/drivers/isdn/i4l/isdn_net.h +++ b/drivers/isdn/i4l/isdn_net.h @@ -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; } diff --git a/drivers/isdn/i4l/isdn_net_lib.c b/drivers/isdn/i4l/isdn_net_lib.c new file mode 100644 index 000000000000..fa7eb8b08b4b --- /dev/null +++ b/drivers/isdn/i4l/isdn_net_lib.c @@ -0,0 +1,1665 @@ +/* + * 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_DHUP, + 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_DHUP", + "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, + EV_CMD_DIAL, +}; + +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", + "EV_CMD_DIAL", +}; + +/* ====================================================================== */ +/* 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 */ +/* ====================================================================== */ + +// 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 */ + fsm_event(&idev->fi, EV_CMD_DIAL, NULL); + 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; +} + +static int dialout_next(struct fsm_inst *fi, int pr, void *arg); + +/* Initiate dialout. */ + +static int +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 -EPERM; + } + if (list_empty(&mlp->phone[1])) { + isdn_net_unbind_channel(idev); + return -EINVAL; + } + + idev->dial = 0; + idev->dialretry = 0; + return dialout_next(fi, pr, arg); +} + +/* Try dialing the next number. */ + +static int +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); + return 0; +} + +/* If we didn't connect within dialtimeout, we give up for now + * and wait for dialwait jiffies before trying again. + */ +static int +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 0; + } + idev->dial_event = EV_TIMER_DIAL_WAIT; + mod_timer(&idev->dial_timer, jiffies + mlp->dialwait); + return 0; +} + +static int +connect_fail(struct fsm_inst *fi, int pr, void *arg) +{ + isdn_net_dev *idev = fi->userdata; + + del_timer(&idev->dial_timer); + isdn_slot_all_eaz(idev->isdn_slot); + printk(KERN_INFO "%s: connection failed\n", idev->name); + isdn_net_unbind_channel(idev); + return 0; +} + +static int +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); + return 0; +} + +static int +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); + return 0; +} + +static int +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 "%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); + + return 0; +} + +static int +bhup(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); + + printk(KERN_INFO "%s: disconnected\n", idev->name); + fsm_change_state(fi, ST_WAIT_DHUP); + isdn_net_rm_from_bundle(idev); + return 0; +} + +static int +dhup(struct fsm_inst *fi, int pr, void *arg) +{ + isdn_net_dev *idev = fi->userdata; + + printk(KERN_INFO "%s: Chargesum is %d\n", idev->name, idev->charge); + isdn_slot_all_eaz(idev->isdn_slot); + isdn_net_unbind_channel(idev); + return 0; +} + +/* Check if it's time for idle hang-up */ + +static int +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 0; + } + mod_timer: + mod_timer(&idev->dial_timer, idev->dial_timer.expires + HZ); + return 0; +} + +/* Charge-info from TelCo. */ + +static int +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); + return 0; +} + +/* 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 int +hang_up(struct fsm_inst *fi, int pr, void *arg) +{ + isdn_net_dev *idev = fi->userdata; + + isdn_net_hangup(idev); + return 0; +} + +static int +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); + return 0; +} + +static struct fsm_node isdn_net_fn_tbl[] = { + { ST_NULL, EV_CMD_DIAL, dialout_first }, + + { 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, bhup }, + { ST_ACTIVE, EV_STAT_CINF, got_cinf }, + { ST_ACTIVE, EV_STAT_BSENT, got_bsent }, + + { ST_WAIT_DHUP, EV_STAT_DHUP, dhup }, + + { 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); +} diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 846b11a11d8f..3c59a373459b 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -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, - struct sk_buff *skb); -static void isdn_ppp_mp_cleanup( 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 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,36 +639,23 @@ 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; + 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); - is->last = bl->next; - restore_flags(flags); - - wake_up_interruptible(&is->wq); + 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, - struct sk_buff *skb) +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,16 +2466,7 @@ 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; + mis = is; switch(skb->data[0]) { case CCP_CONFREQ: @@ -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,16 +2644,7 @@ 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; + 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, diff --git a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h index 81743ea38e9b..af6f50fd11db 100644 --- a/drivers/isdn/i4l/isdn_ppp.h +++ b/drivers/isdn/i4l/isdn_ppp.h @@ -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 diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 7de3324606ee..d8c7b6e2eea3 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -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 timer_list cisco_timer; + + 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 */ + struct list_head global_list; /* global list of all isdn_net_devs */ #ifdef CONFIG_ISDN_PPP ippp_bundle * pb; /* pointer to the common bundle structure * with the per-bundle data */ diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index dcd0ad4aa68b..68353a39b98c 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -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; |
