diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-07-01 23:10:41 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-07-01 23:10:41 +0000 |
commit | d57519a10bc4e9b97cd8834c226523efa9ecd5f3 (patch) | |
tree | 02fe2bc988bc40865f18c4f5b338cac4fe903553 | |
parent | 8d13bb0655915eba0d1aea2ed9a130078b208205 (diff) |
lmc driver; ported by chris@dqc.org
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 3 | ||||
-rw-r--r-- | sys/conf/GENERIC | 3 | ||||
-rw-r--r-- | sys/conf/files | 5 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 10 | ||||
-rw-r--r-- | sys/dev/pci/if_lmc.c | 1393 | ||||
-rw-r--r-- | sys/dev/pci/if_lmc_common.c | 408 | ||||
-rw-r--r-- | sys/dev/pci/if_lmc_media.c | 797 | ||||
-rw-r--r-- | sys/dev/pci/if_lmc_obsd.c | 427 | ||||
-rw-r--r-- | sys/dev/pci/if_lmc_types.h | 88 | ||||
-rw-r--r-- | sys/dev/pci/if_lmcioctl.h | 243 | ||||
-rw-r--r-- | sys/dev/pci/if_lmcvar.h | 609 | ||||
-rw-r--r-- | sys/net/if_sppp.h | 193 | ||||
-rw-r--r-- | sys/net/if_spppsubr.c | 4223 |
13 files changed, 8398 insertions, 4 deletions
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 5edb2136777..b2e6d464b1a 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.110 1999/06/22 14:12:42 deraadt Exp $ +# $OpenBSD: GENERIC,v 1.111 1999/07/01 23:10:39 deraadt Exp $ # $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $ # # GENERIC -- everything that's currently supported @@ -178,6 +178,7 @@ ex0 at isa? port 0x320 irq 5 # Intel EtherExpress PRO/10 ep0 at eisa? slot ? ep* at eisa? slot ? # 3C579 ethernet fea* at eisa? slot ? # DEC DEFEA FDDI +lmc* at pci? dev ? function ? # Lan Media Corp SSI/T3/HSSI le* at pci? dev ? function ? # PCnet-PCI based ethernet le* at isapnp? de* at pci? dev ? function ? # DC21X4X-based ethernet diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC index 34e27f2e347..c081a943aa0 100644 --- a/sys/conf/GENERIC +++ b/sys/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.50 1999/06/08 01:30:13 provos Exp $ +# $OpenBSD: GENERIC,v 1.51 1999/07/01 23:10:39 deraadt Exp $ # # Machine-independent option; used by all architectures for their # GENERIC kernel @@ -75,6 +75,7 @@ pseudo-device loop 2 # network loopback pseudo-device bpfilter 8 # packet filter pseudo-device sl 2 # CSLIP pseudo-device ppp 2 # PPP +pseudo-device sppp # Sync PPP/HDLC pseudo-device tun 2 # network tunneling over tty pseudo-device enc 1 # IPSEC needs the encapsulation interface #pseudo-device strip 1 # Starmode Radio IP interface diff --git a/sys/conf/files b/sys/conf/files index d957df4bf17..5afdf8b393c 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.118 1999/06/21 23:21:44 d Exp $ +# $OpenBSD: files,v 1.119 1999/07/01 23:10:39 deraadt Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -33,6 +33,7 @@ define atm define ether define fddi define crypto +define sppp # "Chipset" attributes. These are the machine-independent portions # of device drivers. @@ -157,6 +158,7 @@ pseudo-device strip: ifnet pseudo-device random pseudo-device enc: ifnet pseudo-device bridge: ifnet, ether +pseudo-device sppp: ifnet pseudo-device sequencer @@ -388,6 +390,7 @@ file net/if_arcsubr.c arc needs-flag file net/if_atmsubr.c atm needs-flag file net/if_ethersubr.c ether | fddi needs-flag file net/if_fddisubr.c fddi +file net/if_spppsubr.c sppp file net/if_loop.c loop needs-count file net/if_media.c ifmedia file net/if_sl.c sl needs-count diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index ab0064d5db6..6b005ca9645 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.38 1999/04/28 23:20:59 alex Exp $ +# $OpenBSD: files.pci,v 1.39 1999/07/01 23:10:39 deraadt Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -93,6 +93,14 @@ file dev/pci/ppb.c ppb attach cy at pci with cy_pci file dev/pci/cy_pci.c cy_pci +# Lan Media Corporation T1/HSSI/DS3 adapter +device lmc: ifnet, sppp +attach lmc at pci +file dev/pci/if_lmc.c lmc +file dev/pci/if_lmc_common.c lmc +file dev/pci/if_lmc_media.c lmc +file dev/pci/if_lmc_obsd.c lmc + # Intel EtherExpress PRO 10/100B device fxp: ether, ifnet, mii, ifmedia attach fxp at pci diff --git a/sys/dev/pci/if_lmc.c b/sys/dev/pci/if_lmc.c new file mode 100644 index 00000000000..f9f10794ca9 --- /dev/null +++ b/sys/dev/pci/if_lmc.c @@ -0,0 +1,1393 @@ +/* $NetBSD: if_lmc.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */ + +/*- + * Copyright (c) 1997-1999 LAN Media Corporation (LMC) + * All rights reserved. www.lanmedia.com + * + * This code is written by Michael Graff <graff@vix.com> for LMC. + * The code is derived from permitted modifications to software created + * by Matt Thomas (matt@3am-software.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All marketing or advertising materials mentioning features or + * use of this software must display the following acknowledgement: + * This product includes software developed by LAN Media Corporation + * and its contributors. + * 4. Neither the name of LAN Media Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +/*#include <sys/proc.h> only for declaration of wakeup() used by vm.h */ +#if defined(__FreeBSD__) +#include <machine/clock.h> +#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/device.h> +#endif + +#if defined(__OpenBSD__) +#include <dev/pci/pcidevs.h> +#endif + +#if defined(__NetBSD__) +#include <dev/pci/pcidevs.h> +#include "rnd.h" +#if NRND > 0 +#include <sys/rnd.h> +#endif +#endif + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/netisr.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <net/if_sppp.h> +#endif + +#if defined(__bsdi__) +#if INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#endif + +#include <net/netisr.h> +#include <net/if.h> +#include <net/netisr.h> +#include <net/if_types.h> +#include <net/if_p2p.h> +#include <net/if_c_hdlc.h> +#endif + +#if defined(__FreeBSD__) +#include <vm/pmap.h> +#include <pci.h> +#if NPCI > 0 +#include <pci/pcivar.h> +#include <pci/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "pci/" +#endif +#endif /* __FreeBSD__ */ + +#if defined(__bsdi__) +#include <i386/pci/ic/dc21040.h> +#include <i386/isa/isa.h> +#include <i386/isa/icu.h> +#include <i386/isa/dma.h> +#include <i386/isa/isavar.h> +#include <i386/pci/pci.h> + +#define INCLUDE_PATH_PREFIX "i386/pci/" +#endif /* __bsdi__ */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <machine/bus.h> +#if defined(__alpha__) && defined(__NetBSD__) +#include <machine/intr.h> +#endif +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/ic/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "dev/pci/" +#endif /* __NetBSD__ */ + +#if defined(__OpenBSD__) +#define d_length1 u.bd_length1 +#define d_length2 u.bd_length2 +#define d_flag u.bd_flag +#endif + +/* + * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use + * a C preprocessor that allows this hack, but BSDI does not. Grr. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#include INCLUDE_PATH_PREFIX "if_lmc_types.h" +#include INCLUDE_PATH_PREFIX "if_lmcioctl.h" +#include INCLUDE_PATH_PREFIX "if_lmcvar.h" +#else /* BSDI */ +#include "i386/pci/if_lmctypes.h" +#include "i386/pci/if_lmcioctl.h" +#include "i386/pci/if_lmcvar.h" +#endif + +/* + * This module supports + * the DEC 21140A pass 2.2 PCI Fast Ethernet Controller. + */ +static ifnet_ret_t lmc_ifstart_one(struct ifnet *ifp); +static ifnet_ret_t lmc_ifstart(struct ifnet *ifp); +static struct mbuf *lmc_txput(lmc_softc_t * const sc, struct mbuf *m); +static void lmc_rx_intr(lmc_softc_t * const sc); + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +static void lmc_watchdog(struct ifnet *ifp); +#endif +#if defined(__bsdi__) +static int lmc_watchdog(int); +#endif +static void lmc_ifup(lmc_softc_t * const sc); +static void lmc_ifdown(lmc_softc_t * const sc); + + +/* + * Code the read the SROM and MII bit streams (I2C) + */ +static inline void +lmc_delay_300ns(lmc_softc_t * const sc) +{ + int idx; + for (idx = (300 / 33) + 1; idx > 0; idx--) + (void)LMC_CSR_READ(sc, csr_busmode); +} + + +#define EMIT \ +do { \ + LMC_CSR_WRITE(sc, csr_srom_mii, csr); \ + lmc_delay_300ns(sc); \ +} while (0) + +static inline void +lmc_srom_idle(lmc_softc_t * const sc) +{ + unsigned bit, csr; + + csr = SROMSEL ; EMIT; + csr = SROMSEL | SROMRD; EMIT; + csr ^= SROMCS; EMIT; + csr ^= SROMCLKON; EMIT; + + /* + * Write 25 cycles of 0 which will force the SROM to be idle. + */ + for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) { + csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ + csr ^= SROMCLKON; EMIT; /* clock high; data valid */ + } + csr ^= SROMCLKOFF; EMIT; + csr ^= SROMCS; EMIT; + csr = 0; EMIT; +} + + +static void +lmc_srom_read(lmc_softc_t * const sc) +{ + unsigned idx; + const unsigned bitwidth = SROM_BITWIDTH; + const unsigned cmdmask = (SROMCMD_RD << bitwidth); + const unsigned msb = 1 << (bitwidth + 3 - 1); + unsigned lastidx = (1 << bitwidth) - 1; + + lmc_srom_idle(sc); + + for (idx = 0; idx <= lastidx; idx++) { + unsigned lastbit, data, bits, bit, csr; + csr = SROMSEL ; EMIT; + csr = SROMSEL | SROMRD; EMIT; + csr ^= SROMCSON; EMIT; + csr ^= SROMCLKON; EMIT; + + lastbit = 0; + for (bits = idx|cmdmask, bit = bitwidth + 3 + ; bit > 0 + ; bit--, bits <<= 1) { + const unsigned thisbit = bits & msb; + csr ^= SROMCLKOFF; EMIT; /* clock L data invalid */ + if (thisbit != lastbit) { + csr ^= SROMDOUT; EMIT;/* clock L invert data */ + } else { + EMIT; + } + csr ^= SROMCLKON; EMIT; /* clock H data valid */ + lastbit = thisbit; + } + csr ^= SROMCLKOFF; EMIT; + + for (data = 0, bits = 0; bits < 16; bits++) { + data <<= 1; + csr ^= SROMCLKON; EMIT; /* clock H data valid */ + data |= LMC_CSR_READ(sc, csr_srom_mii) & SROMDIN ? 1 : 0; + csr ^= SROMCLKOFF; EMIT; /* clock L data invalid */ + } + sc->lmc_rombuf[idx*2] = data & 0xFF; + sc->lmc_rombuf[idx*2+1] = data >> 8; + csr = SROMSEL | SROMRD; EMIT; + csr = 0; EMIT; + } + lmc_srom_idle(sc); +} + +#define MII_EMIT do { LMC_CSR_WRITE(sc, csr_srom_mii, csr); lmc_delay_300ns(sc); } while (0) + +static inline void +lmc_mii_writebits(lmc_softc_t * const sc, unsigned data, unsigned bits) +{ + unsigned msb = 1 << (bits - 1); + unsigned csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + unsigned lastbit = (csr & MII_DOUT) ? msb : 0; + + csr |= MII_WR; MII_EMIT; /* clock low; assert write */ + + for (; bits > 0; bits--, data <<= 1) { + const unsigned thisbit = data & msb; + if (thisbit != lastbit) { + csr ^= MII_DOUT; MII_EMIT; /* clock low; invert data */ + } + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + lastbit = thisbit; + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ + } +} + +static void +lmc_mii_turnaround(lmc_softc_t * const sc, u_int32_t cmd) +{ + u_int32_t csr; + + csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + if (cmd == MII_WRCMD) { + csr |= MII_DOUT; MII_EMIT; /* clock low; change data */ + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ + csr ^= MII_DOUT; MII_EMIT; /* clock low; change data */ + } else { + csr |= MII_RD; MII_EMIT; /* clock low; switch to read */ + } + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ +} + +static u_int32_t +lmc_mii_readbits(lmc_softc_t * const sc) +{ + u_int32_t data; + u_int32_t csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + int idx; + + for (idx = 0, data = 0; idx < 16; idx++) { + data <<= 1; /* this is NOOP on the first pass through */ + csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ + if (LMC_CSR_READ(sc, csr_srom_mii) & MII_DIN) + data |= 1; + csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ + } + csr ^= MII_RD; MII_EMIT; /* clock low; turn off read */ + + return data; +} + +u_int32_t +lmc_mii_readreg(lmc_softc_t * const sc, u_int32_t devaddr, u_int32_t regno) +{ + u_int32_t csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + u_int32_t data; + + csr &= ~(MII_RD|MII_CLK); MII_EMIT; + lmc_mii_writebits(sc, MII_PREAMBLE, 32); + lmc_mii_writebits(sc, MII_RDCMD, 8); + lmc_mii_writebits(sc, devaddr, 5); + lmc_mii_writebits(sc, regno, 5); + lmc_mii_turnaround(sc, MII_RDCMD); + + data = lmc_mii_readbits(sc); + return (data); +} + +void +lmc_mii_writereg(lmc_softc_t * const sc, u_int32_t devaddr, + u_int32_t regno, u_int32_t data) +{ + u_int32_t csr; + + csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); + csr &= ~(MII_RD|MII_CLK); MII_EMIT; + lmc_mii_writebits(sc, MII_PREAMBLE, 32); + lmc_mii_writebits(sc, MII_WRCMD, 8); + lmc_mii_writebits(sc, devaddr, 5); + lmc_mii_writebits(sc, regno, 5); + lmc_mii_turnaround(sc, MII_WRCMD); + lmc_mii_writebits(sc, data, 16); +} + +int +lmc_read_macaddr(lmc_softc_t * const sc) +{ + lmc_srom_read(sc); + + bcopy(sc->lmc_rombuf + 20, sc->lmc_enaddr, 6); + + return 0; +} + +/* + * Check to make certain there is a signal from the modem, and flicker + * lights as needed. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +static void +lmc_watchdog(struct ifnet *ifp) +#endif +#if defined(__bsdi__) +static int +lmc_watchdog(int unit) +#endif +{ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp); +#endif +#if defined(__bsdi__) + lmc_softc_t * const sc = LMC_UNIT_TO_SOFTC(unit); + struct ifnet *ifp = &sc->lmc_if; +#endif + int state; + u_int32_t ostatus; + u_int32_t link_status; + u_int32_t ticks; + + state = 0; + + link_status = sc->lmc_media->get_link_status(sc); + ostatus = ((sc->lmc_flags & LMC_MODEMOK) == LMC_MODEMOK); + + /* + * hardware level link lost, but the interface is marked as up. + * Mark it as down. + */ + if (link_status == 0 && ostatus) { + printf(LMC_PRINTF_FMT ": physical link down\n", + LMC_PRINTF_ARGS); + sc->lmc_flags &= ~LMC_MODEMOK; + lmc_led_off(sc, LMC_MII16_LED1); + } + + /* + * hardware link is up, but the interface is marked as down. + * Bring it back up again. + */ + if (link_status != 0 && !ostatus) { + printf(LMC_PRINTF_FMT ": physical link up\n", + LMC_PRINTF_ARGS); + if (sc->lmc_flags & LMC_IFUP) + lmc_ifup(sc); + sc->lmc_flags |= LMC_MODEMOK; + lmc_led_on(sc, LMC_MII16_LED1); + return; + } + + /* + * remember the timer value + */ + ticks = LMC_CSR_READ(sc, csr_gp_timer); + LMC_CSR_WRITE(sc, csr_gp_timer, 0xffffffffUL); + sc->ictl.ticks = 0x0000ffff - (ticks & 0x0000ffff); + + ifp->if_timer = 1; +} + +/* + * Mark the interface as "up" and enable TX/RX and TX/RX interrupts. + * This also does a full software reset. + */ +static void +lmc_ifup(lmc_softc_t * const sc) +{ + sc->lmc_if.if_timer = 0; + + lmc_dec_reset(sc); + lmc_reset(sc); + + sc->lmc_media->set_link_status(sc, 1); + sc->lmc_media->set_status(sc, NULL); + + sc->lmc_flags |= LMC_IFUP; + + lmc_led_on(sc, LMC_MII16_LED1); + + /* + * select what interrupts we want to get + */ + sc->lmc_intrmask |= (TULIP_STS_NORMALINTR + | TULIP_STS_RXINTR + | TULIP_STS_TXINTR + | TULIP_STS_ABNRMLINTR + | TULIP_STS_SYSERROR + | TULIP_STS_TXSTOPPED + | TULIP_STS_TXUNDERFLOW + | TULIP_STS_RXSTOPPED + ); + LMC_CSR_WRITE(sc, csr_intr, sc->lmc_intrmask); + + sc->lmc_cmdmode |= TULIP_CMD_TXRUN; + sc->lmc_cmdmode |= TULIP_CMD_RXRUN; + LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode); + + sc->lmc_if.if_timer = 1; +} + +/* + * Mark the interface as "down" and disable TX/RX and TX/RX interrupts. + * This is done by performing a full reset on the interface. + */ +static void +lmc_ifdown(lmc_softc_t * const sc) +{ + sc->lmc_if.if_timer = 0; + sc->lmc_flags &= ~LMC_IFUP; + + sc->lmc_media->set_link_status(sc, 0); + lmc_led_off(sc, LMC_MII16_LED1); + + lmc_dec_reset(sc); + lmc_reset(sc); + sc->lmc_media->set_status(sc, NULL); +} + +static void +lmc_rx_intr(lmc_softc_t * const sc) +{ + lmc_ringinfo_t * const ri = &sc->lmc_rxinfo; + struct ifnet * const ifp = &sc->lmc_if; + int fillok = 1; + + sc->lmc_rxtick++; + + for (;;) { + tulip_desc_t *eop = ri->ri_nextin; + int total_len = 0, last_offset = 0; + struct mbuf *ms = NULL, *me = NULL; + int accept = 0; + + if (fillok && sc->lmc_rxq.ifq_len < LMC_RXQ_TARGET) + goto queue_mbuf; + + /* + * If the TULIP has no descriptors, there can't be any receive + * descriptors to process. + */ + if (eop == ri->ri_nextout) + break; + + /* + * 90% of the packets will fit in one descriptor. So we + * optimize for that case. + */ + if ((((volatile tulip_desc_t *) eop)->d_status & (TULIP_DSTS_OWNER|TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) == (TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) { + IF_DEQUEUE(&sc->lmc_rxq, ms); + me = ms; + } else { + /* + * If still owned by the TULIP, don't touch it. + */ + if (((volatile tulip_desc_t *)eop)->d_status & TULIP_DSTS_OWNER) + break; + + /* + * It is possible (though improbable unless the + * BIG_PACKET support is enabled or MCLBYTES < 1518) + * for a received packet to cross more than one + * receive descriptor. + */ + while ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_RxLASTDESC) == 0) { + if (++eop == ri->ri_last) + eop = ri->ri_first; + if (eop == ri->ri_nextout || ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_OWNER))) { + return; + } + total_len++; + } + + /* + * Dequeue the first buffer for the start of the + * packet. Hopefully this will be the only one we + * need to dequeue. However, if the packet consumed + * multiple descriptors, then we need to dequeue + * those buffers and chain to the starting mbuf. + * All buffers but the last buffer have the same + * length so we can set that now. (we add to + * last_offset instead of multiplying since we + * normally won't go into the loop and thereby + * saving a ourselves from doing a multiplication + * by 0 in the normal case). + */ + IF_DEQUEUE(&sc->lmc_rxq, ms); + for (me = ms; total_len > 0; total_len--) { + me->m_len = LMC_RX_BUFLEN; + last_offset += LMC_RX_BUFLEN; + IF_DEQUEUE(&sc->lmc_rxq, me->m_next); + me = me->m_next; + } + } + + /* + * Now get the size of received packet (minus the CRC). + */ + total_len = ((eop->d_status >> 16) & 0x7FFF); + if (sc->ictl.crc_length == 16) + total_len -= 2; + else + total_len -= 4; + + if ((sc->lmc_flags & LMC_RXIGNORE) == 0 + && ((eop->d_status & LMC_DSTS_ERRSUM) == 0 +#ifdef BIG_PACKET + || (total_len <= sc->lmc_if.if_mtu + PPP_HEADER_LEN + && (eop->d_status & TULIP_DSTS_RxOVERFLOW) == 0) +#endif + )) { + me->m_len = total_len - last_offset; +#if NBPFILTER > 0 + if (sc->lmc_bpf != NULL) { + if (me == ms) + LMC_BPF_TAP(sc, mtod(ms, caddr_t), total_len); + else + LMC_BPF_MTAP(sc, ms); + } +#endif + sc->lmc_flags |= LMC_RXACT; + accept = 1; + } else { + ifp->if_ierrors++; + if (eop->d_status & TULIP_DSTS_RxOVERFLOW) { + sc->lmc_dot3stats.dot3StatsInternalMacReceiveErrors++; + } + } + + ifp->if_ipackets++; + if (++eop == ri->ri_last) + eop = ri->ri_first; + ri->ri_nextin = eop; + + queue_mbuf: + /* + * Either we are priming the TULIP with mbufs (m == NULL) + * or we are about to accept an mbuf for the upper layers + * so we need to allocate an mbuf to replace it. If we + * can't replace it, send up it anyways. This may cause + * us to drop packets in the future but that's better than + * being caught in livelock. + * + * Note that if this packet crossed multiple descriptors + * we don't even try to reallocate all the mbufs here. + * Instead we rely on the test of the beginning of + * the loop to refill for the extra consumed mbufs. + */ + if (accept || ms == NULL) { + struct mbuf *m0; + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 != NULL) { + MCLGET(m0, M_DONTWAIT); + if ((m0->m_flags & M_EXT) == 0) { + m_freem(m0); + m0 = NULL; + } + } + if (accept) { + ms->m_pkthdr.len = total_len; + ms->m_pkthdr.rcvif = ifp; +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + sppp_input(ifp, ms); +#endif +#if defined(__bsdi__) + sc->lmc_p2pcom.p2p_input(&sc->lmc_p2pcom, ms); +#endif + } + ms = m0; + } + if (ms == NULL) { + /* + * Couldn't allocate a new buffer. Don't bother + * trying to replenish the receive queue. + */ + fillok = 0; + sc->lmc_flags |= LMC_RXBUFSLOW; + continue; + } + /* + * Now give the buffer(s) to the TULIP and save in our + * receive queue. + */ + do { + ri->ri_nextout->d_length1 = LMC_RX_BUFLEN; + ri->ri_nextout->d_addr1 = LMC_KVATOPHYS(sc, mtod(ms, caddr_t)); + ri->ri_nextout->d_status = TULIP_DSTS_OWNER; + if (++ri->ri_nextout == ri->ri_last) + ri->ri_nextout = ri->ri_first; + me = ms->m_next; + ms->m_next = NULL; + IF_ENQUEUE(&sc->lmc_rxq, ms); + } while ((ms = me) != NULL); + + if (sc->lmc_rxq.ifq_len >= LMC_RXQ_TARGET) + sc->lmc_flags &= ~LMC_RXBUFSLOW; + } +} + +static int +lmc_tx_intr(lmc_softc_t * const sc) +{ + lmc_ringinfo_t * const ri = &sc->lmc_txinfo; + struct mbuf *m; + int xmits = 0; + int descs = 0; + + sc->lmc_txtick++; + + while (ri->ri_free < ri->ri_max) { +#ifdef __OpenBSD__ + u_int32_t duh_flag; +#else + u_int32_t d_flag; +#endif + + if (((volatile tulip_desc_t *) ri->ri_nextin)->d_status & TULIP_DSTS_OWNER) + break; + +#ifdef __OpenBSD__ + duh_flag = ri->ri_nextin->d_flag; + if (duh_flag & TULIP_DFLAG_TxLASTSEG) { +#else + d_flag = ri->ri_nextin->d_flag; + if (d_flag & TULIP_DFLAG_TxLASTSEG) { +#endif + const u_int32_t d_status = ri->ri_nextin->d_status; + IF_DEQUEUE(&sc->lmc_txq, m); + if (m != NULL) { +#if NBPFILTER > 0 + if (sc->lmc_bpf != NULL) + LMC_BPF_MTAP(sc, m); +#endif + m_freem(m); +#if defined(LMC_DEBUG) + } else { + printf(LMC_PRINTF_FMT ": tx_intr: failed to dequeue mbuf?!?\n", LMC_PRINTF_ARGS); +#endif + } + xmits++; + if (d_status & LMC_DSTS_ERRSUM) { + sc->lmc_if.if_oerrors++; + if (d_status & TULIP_DSTS_TxUNDERFLOW) + sc->lmc_dot3stats.dot3StatsInternalTransmitUnderflows++; + } else { + if (d_status & TULIP_DSTS_TxDEFERRED) + sc->lmc_dot3stats.dot3StatsDeferredTransmissions++; + } + } + + if (++ri->ri_nextin == ri->ri_last) + ri->ri_nextin = ri->ri_first; + + ri->ri_free++; + descs++; + sc->lmc_if.if_flags &= ~IFF_OACTIVE; + } + /* + * If nothing left to transmit, disable the timer. + * Else if progress, reset the timer back to 2 ticks. + */ + sc->lmc_if.if_opackets += xmits; + + return descs; +} + +static void +lmc_print_abnormal_interrupt (lmc_softc_t * const sc, u_int32_t csr) +{ + printf(LMC_PRINTF_FMT ": Abnormal interrupt\n", LMC_PRINTF_ARGS); +} + +static void +lmc_intr_handler(lmc_softc_t * const sc, int *progress_p) +{ + u_int32_t csr; + + while ((csr = LMC_CSR_READ(sc, csr_status)) & sc->lmc_intrmask) { + +#if defined(__NetBSD__) +#if NRND > 0 + rnd_add_uint32(&sc->lmc_rndsource, csr); +#endif +#endif + + *progress_p = 1; + LMC_CSR_WRITE(sc, csr_status, csr); + + if (csr & TULIP_STS_SYSERROR) { + sc->lmc_last_system_error = (csr & TULIP_STS_ERRORMASK) >> TULIP_STS_ERR_SHIFT; + if (sc->lmc_flags & LMC_NOMESSAGES) { + sc->lmc_flags |= LMC_SYSTEMERROR; + } else { + printf(LMC_PRINTF_FMT ": system error: %s\n", + LMC_PRINTF_ARGS, + lmc_system_errors[sc->lmc_last_system_error]); + } + sc->lmc_flags |= LMC_NEEDRESET; + sc->lmc_system_errors++; + break; + } + if (csr & (TULIP_STS_RXINTR | TULIP_STS_RXNOBUF)) { + u_int32_t misses = LMC_CSR_READ(sc, csr_missed_frames); + if (csr & TULIP_STS_RXNOBUF) + sc->lmc_dot3stats.dot3StatsMissedFrames += misses & 0xFFFF; + /* + * Pass 2.[012] of the 21140A-A[CDE] may hang and/or corrupt data + * on receive overflows. + */ + if ((misses & 0x0FFE0000) && (sc->lmc_features & LMC_HAVE_RXBADOVRFLW)) { + sc->lmc_dot3stats.dot3StatsInternalMacReceiveErrors++; + /* + * Stop the receiver process and spin until it's stopped. + * Tell rx_intr to drop the packets it dequeues. + */ + LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode & ~TULIP_CMD_RXRUN); + while ((LMC_CSR_READ(sc, csr_status) & TULIP_STS_RXSTOPPED) == 0) + ; + LMC_CSR_WRITE(sc, csr_status, TULIP_STS_RXSTOPPED); + sc->lmc_flags |= LMC_RXIGNORE; + } + lmc_rx_intr(sc); + if (sc->lmc_flags & LMC_RXIGNORE) { + /* + * Restart the receiver. + */ + sc->lmc_flags &= ~LMC_RXIGNORE; + LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode); + } + } + if (csr & TULIP_STS_ABNRMLINTR) { + u_int32_t tmp = csr & sc->lmc_intrmask + & ~(TULIP_STS_NORMALINTR|TULIP_STS_ABNRMLINTR); + if (csr & TULIP_STS_TXUNDERFLOW) { + if ((sc->lmc_cmdmode & TULIP_CMD_THRESHOLDCTL) != TULIP_CMD_THRSHLD160) { + sc->lmc_cmdmode += TULIP_CMD_THRSHLD96; + sc->lmc_flags |= LMC_NEWTXTHRESH; + } else if (sc->lmc_features & LMC_HAVE_STOREFWD) { + sc->lmc_cmdmode |= TULIP_CMD_STOREFWD; + sc->lmc_flags |= LMC_NEWTXTHRESH; + } + } + if (sc->lmc_flags & LMC_NOMESSAGES) { + sc->lmc_statusbits |= tmp; + } else { + lmc_print_abnormal_interrupt(sc, tmp); + sc->lmc_flags |= LMC_NOMESSAGES; + } + LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode); + } + + if (csr & TULIP_STS_TXINTR) + lmc_tx_intr(sc); + + if (sc->lmc_flags & LMC_WANTTXSTART) + lmc_ifstart(&sc->lmc_if); + } +} + +lmc_intrfunc_t +lmc_intr_normal(void *arg) +{ + lmc_softc_t * sc = (lmc_softc_t *) arg; + int progress = 0; + + lmc_intr_handler(sc, &progress); + +#if !defined(LMC_VOID_INTRFUNC) + return progress; +#endif +} + +static struct mbuf * +lmc_mbuf_compress(struct mbuf *m) +{ + struct mbuf *m0; +#if MCLBYTES >= LMC_MTU + PPP_HEADER_LEN && !defined(BIG_PACKET) + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 != NULL) { + if (m->m_pkthdr.len > MHLEN) { + MCLGET(m0, M_DONTWAIT); + if ((m0->m_flags & M_EXT) == 0) { + m_freem(m); + m_freem(m0); + return NULL; + } + } + m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t)); + m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len; + } +#else + int mlen = MHLEN; + int len = m->m_pkthdr.len; + struct mbuf **mp = &m0; + + while (len > 0) { + if (mlen == MHLEN) { + MGETHDR(*mp, M_DONTWAIT, MT_DATA); + } else { + MGET(*mp, M_DONTWAIT, MT_DATA); + } + if (*mp == NULL) { + m_freem(m0); + m0 = NULL; + break; + } + if (len > MLEN) { + MCLGET(*mp, M_DONTWAIT); + if (((*mp)->m_flags & M_EXT) == 0) { + m_freem(m0); + m0 = NULL; + break; + } + (*mp)->m_len = (len <= MCLBYTES ? len : MCLBYTES); + } else { + (*mp)->m_len = (len <= mlen ? len : mlen); + } + m_copydata(m, m->m_pkthdr.len - len, + (*mp)->m_len, mtod((*mp), caddr_t)); + len -= (*mp)->m_len; + mp = &(*mp)->m_next; + mlen = MLEN; + } +#endif + m_freem(m); + return m0; +} + +/* + * queue the mbuf handed to us for the interface. If we cannot + * queue it, return the mbuf. Return NULL if the mbuf was queued. + */ +static struct mbuf * +lmc_txput(lmc_softc_t * const sc, struct mbuf *m) +{ + lmc_ringinfo_t * const ri = &sc->lmc_txinfo; + tulip_desc_t *eop, *nextout; + int segcnt, free; + u_int32_t d_status; + struct mbuf *m0; + +#if defined(LMC_DEBUG) + if ((sc->lmc_cmdmode & TULIP_CMD_TXRUN) == 0) { + printf(LMC_PRINTF_FMT ": txput: tx not running\n", + LMC_PRINTF_ARGS); + sc->lmc_flags |= LMC_WANTTXSTART; + goto finish; + } +#endif + + /* + * Now we try to fill in our transmit descriptors. This is + * a bit reminiscent of going on the Ark two by two + * since each descriptor for the TULIP can describe + * two buffers. So we advance through packet filling + * each of the two entries at a time to fill each + * descriptor. Clear the first and last segment bits + * in each descriptor (actually just clear everything + * but the end-of-ring or chain bits) to make sure + * we don't get messed up by previously sent packets. + * + * We may fail to put the entire packet on the ring if + * there is either not enough ring entries free or if the + * packet has more than MAX_TXSEG segments. In the former + * case we will just wait for the ring to empty. In the + * latter case we have to recopy. + */ + again: + d_status = 0; + eop = nextout = ri->ri_nextout; + m0 = m; + segcnt = 0; + free = ri->ri_free; + do { + int len = m0->m_len; + caddr_t addr = mtod(m0, caddr_t); + unsigned clsize = CLBYTES - (((u_long) addr) & (CLBYTES-1)); + + while (len > 0) { + unsigned slen = min(len, clsize); +#ifdef BIG_PACKET + int partial = 0; + if (slen >= 2048) + slen = 2040, partial = 1; +#endif + segcnt++; + if (segcnt > LMC_MAX_TXSEG) { + /* + * The packet exceeds the number of transmit + * buffer entries that we can use for one + * packet, so we have recopy it into one mbuf + * and then try again. + */ + m = lmc_mbuf_compress(m); + if (m == NULL) + goto finish; + goto again; + } + if (segcnt & 1) { + if (--free == 0) { + /* + * See if there's any unclaimed space + * in the transmit ring. + */ + if ((free += lmc_tx_intr(sc)) == 0) { + /* + * There's no more room but + * since nothing has been + * committed at this point, + * just show output is active, + * put back the mbuf and + * return. + */ + sc->lmc_flags |= LMC_WANTTXSTART; + goto finish; + } + } + eop = nextout; + if (++nextout == ri->ri_last) + nextout = ri->ri_first; + eop->d_flag &= TULIP_DFLAG_ENDRING; + eop->d_flag |= TULIP_DFLAG_TxNOPADDING; + if (sc->ictl.crc_length == 16) + eop->d_flag |= TULIP_DFLAG_TxHASCRC; + eop->d_status = d_status; + eop->d_addr1 = LMC_KVATOPHYS(sc, addr); + eop->d_length1 = slen; + } else { + /* + * Fill in second half of descriptor + */ + eop->d_addr2 = LMC_KVATOPHYS(sc, addr); + eop->d_length2 = slen; + } + d_status = TULIP_DSTS_OWNER; + len -= slen; + addr += slen; +#ifdef BIG_PACKET + if (partial) + continue; +#endif + clsize = CLBYTES; + } + } while ((m0 = m0->m_next) != NULL); + + + /* + * The descriptors have been filled in. Now get ready + * to transmit. + */ + IF_ENQUEUE(&sc->lmc_txq, m); + m = NULL; + + /* + * Make sure the next descriptor after this packet is owned + * by us since it may have been set up above if we ran out + * of room in the ring. + */ + nextout->d_status = 0; + + /* + * If we only used the first segment of the last descriptor, + * make sure the second segment will not be used. + */ + if (segcnt & 1) { + eop->d_addr2 = 0; + eop->d_length2 = 0; + } + + /* + * Mark the last and first segments, indicate we want a transmit + * complete interrupt, and tell it to transmit! + */ + eop->d_flag |= TULIP_DFLAG_TxLASTSEG | TULIP_DFLAG_TxWANTINTR; + + /* + * Note that ri->ri_nextout is still the start of the packet + * and until we set the OWNER bit, we can still back out of + * everything we have done. + */ + ri->ri_nextout->d_flag |= TULIP_DFLAG_TxFIRSTSEG; + ri->ri_nextout->d_status = TULIP_DSTS_OWNER; + + LMC_CSR_WRITE(sc, csr_txpoll, 1); + + /* + * This advances the ring for us. + */ + ri->ri_nextout = nextout; + ri->ri_free = free; + + /* + * switch back to the single queueing ifstart. + */ + sc->lmc_flags &= ~LMC_WANTTXSTART; + sc->lmc_if.if_start = lmc_ifstart_one; + + /* + * If we want a txstart, there must be not enough space in the + * transmit ring. So we want to enable transmit done interrupts + * so we can immediately reclaim some space. When the transmit + * interrupt is posted, the interrupt handler will call tx_intr + * to reclaim space and then txstart (since WANTTXSTART is set). + * txstart will move the packet into the transmit ring and clear + * WANTTXSTART thereby causing TXINTR to be cleared. + */ + finish: + if (sc->lmc_flags & LMC_WANTTXSTART) { + sc->lmc_if.if_flags |= IFF_OACTIVE; + sc->lmc_if.if_start = lmc_ifstart; + } + + return m; +} + + +/* + * This routine is entered at splnet() (splsoftnet() on NetBSD) + */ +static int +lmc_ifioctl(struct ifnet * ifp, ioctl_cmd_t cmd, caddr_t data) +{ + lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp); +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + lmc_spl_t s; +#endif + int error = 0; + struct ifreq *ifr = (struct ifreq *)data; + u_int32_t new_state; + u_int32_t old_state; + lmc_ctl_t ctl; + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + s = LMC_RAISESPL(); +#endif + + switch (cmd) { + case LMCIOCGINFO: + error = copyout(&sc->ictl, ifr->ifr_data, sizeof(lmc_ctl_t)); + + goto out; + break; + + case LMCIOCSINFO: +#if 0 /* XXX */ + error = suser(p->p_ucred, &p->p_acflag); + if (error) + goto out; +#endif + + error = copyin(ifr->ifr_data, &ctl, sizeof(lmc_ctl_t)); + if (error != 0) + goto out; + + sc->lmc_media->set_status(sc, &ctl); + + goto out; + break; + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#if !defined(ifr_mtu) +#define ifr_mtu ifr_metric +#endif + case SIOCSIFMTU: + /* + * Don't allow the MTU to get larger than we can handle + */ + if (ifr->ifr_mtu > LMC_MTU) { + error = EINVAL; + goto out; + } +#endif + } + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + /* + * call the sppp ioctl layer + */ + error = sppp_ioctl(ifp, cmd, data); + if (error != 0) + goto out; +#endif + +#if defined(__bsdi__) + error = p2p_ioctl(ifp, cmd, data); +#endif + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + /* + * If we are transitioning from up to down or down to up, call + * our init routine. + */ + new_state = ifp->if_flags & IFF_UP; + old_state = sc->lmc_flags & LMC_IFUP; + + if (new_state && !old_state) + lmc_ifup(sc); + else if (!new_state && old_state) + lmc_ifdown(sc); +#endif + + out: +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + LMC_RESTORESPL(s); +#endif + + return error; +} + +/* + * These routines gets called at device spl (from sppp_output). + */ + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +static ifnet_ret_t +lmc_ifstart(struct ifnet * const ifp) +{ + lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp); + struct mbuf *m; + + if (sc->lmc_flags & LMC_IFUP) { + while (sppp_isempty(ifp) == 0) { + m = sppp_dequeue(ifp); + if ((m = lmc_txput(sc, m)) != NULL) { + IF_PREPEND(&((struct sppp *)ifp)->pp_fastq, m); + break; + } + } + LMC_CSR_WRITE(sc, csr_txpoll, 1); + } +} + +static ifnet_ret_t +lmc_ifstart_one(struct ifnet * const ifp) +{ + lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp); + struct mbuf *m; + + if ((sc->lmc_flags & LMC_IFUP) && (sppp_isempty(ifp) == 0)) { + m = sppp_dequeue(ifp); + if ((m = lmc_txput(sc, m)) != NULL) { + IF_PREPEND(&((struct sppp *)ifp)->pp_fastq, m); + } + LMC_CSR_WRITE(sc, csr_txpoll, 1); + } +} +#endif + +#if defined(__bsdi__) +static ifnet_ret_t +lmc_ifstart(struct ifnet * const ifp) +{ + lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp); + struct mbuf *m; + struct ifqueue *ifq; + + if ((sc->lmc_flags & LMC_IFUP) == 0) + return; + + for (;;) { + ifq = &sc->lmc_p2pcom.p2p_isnd; + + m = ifq->ifq_head; + if (m == NULL) { + ifq = &sc->lmc_if.if_snd; + m = ifq->ifq_head; + } + if (m == NULL) + break; + IF_DEQUEUE(ifq, m); + + m = lmc_txput(sc, m); + if (m != NULL) { + IF_PREPEND(ifq, m); + break; + } + } + + LMC_CSR_WRITE(sc, csr_txpoll, 1); +} + +static ifnet_ret_t +lmc_ifstart_one(struct ifnet * const ifp) +{ + lmc_softc_t * const sc = LMC_IFP_TO_SOFTC(ifp); + struct mbuf *m; + struct ifqueue *ifq; + + if ((sc->lmc_flags & LMC_IFUP) == 0) + return; + + ifq = &sc->lmc_p2pcom.p2p_isnd; + + m = ifq->ifq_head; + if (m == NULL) { + ifq = &sc->lmc_if.if_snd; + m = ifq->ifq_head; + } + if (m == NULL) + return 0; + IF_DEQUEUE(ifq, m); + + m = lmc_txput(sc, m); + if (m != NULL) + IF_PREPEND(ifq, m); + + LMC_CSR_WRITE(sc, csr_txpoll, 1); +} +#endif + +#if defined(__bsdi__) +int +lmc_getmdm(struct p2pcom *pp, caddr_t b) +{ + lmc_softc_t *sc = LMC_UNIT_TO_SOFTC(pp->p2p_if.if_unit); + + if (sc->lmc_media->get_link_status(sc)) { + *(int *)b = TIOCM_CAR; + } else { + *(int *)b = 0; + } + + return (0); +} + +int +lmc_mdmctl(struct p2pcom *pp, int flag) +{ + lmc_softc_t *sc = LMC_UNIT_TO_SOFTC(pp->p2p_if.if_unit); + + sc->lmc_media->set_link_status(sc, flag); + + if (flag) + if ((sc->lmc_flags & LMC_IFUP) == 0) + lmc_ifup(sc); + else + if ((sc->lmc_flags & LMC_IFUP) == LMC_IFUP) + lmc_ifdown(sc); + + return (0); +} +#endif + +/* + * Set up the OS interface magic and attach to the operating system + * network services. + */ +void +lmc_attach(lmc_softc_t * const sc) +{ + struct ifnet * const ifp = &sc->lmc_if; + + ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; + ifp->if_ioctl = lmc_ifioctl; + ifp->if_start = lmc_ifstart; + ifp->if_watchdog = lmc_watchdog; + ifp->if_timer = 1; + ifp->if_mtu = LMC_MTU; + +#if defined(__bsdi__) + ifp->if_type = IFT_NONE; + ifp->if_unit = (sc->lmc_dev.dv_unit); +#endif + + if_attach(ifp); + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + sppp_attach((struct ifnet *)&sc->lmc_sppp); + sc->lmc_sppp.pp_flags = PP_CISCO | PP_KEEPALIVE; +#endif +#if defined(__bsdi__) + sc->lmc_p2pcom.p2p_mdmctl = lmc_mdmctl; + sc->lmc_p2pcom.p2p_getmdm = lmc_getmdm; + p2p_attach(&sc->lmc_p2pcom); +#endif + +#if NBPFILTER > 0 + LMC_BPF_ATTACH(sc); +#endif + +#if defined(__NetBSD__) && NRND > 0 + rnd_attach_source(&sc->lmc_rndsource, sc->lmc_dev.dv_xname, + RND_TYPE_NET, 0); +#endif + + /* + * turn off those LEDs... + */ + sc->lmc_miireg16 |= LMC_MII16_LED_ALL; + lmc_led_on(sc, LMC_MII16_LED0); +} + +void +lmc_initring(lmc_softc_t * const sc, lmc_ringinfo_t * const ri, + tulip_desc_t *descs, int ndescs) +{ + ri->ri_max = ndescs; + ri->ri_first = descs; + ri->ri_last = ri->ri_first + ri->ri_max; + bzero((caddr_t) ri->ri_first, sizeof(ri->ri_first[0]) * ri->ri_max); + ri->ri_last[-1].d_flag = TULIP_DFLAG_ENDRING; +} diff --git a/sys/dev/pci/if_lmc_common.c b/sys/dev/pci/if_lmc_common.c new file mode 100644 index 00000000000..a005df8830c --- /dev/null +++ b/sys/dev/pci/if_lmc_common.c @@ -0,0 +1,408 @@ +/* $NetBSD: if_lmc_common.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */ + +/*- + * Copyright (c) 1997-1999 LAN Media Corporation (LMC) + * All rights reserved. www.lanmedia.com + * + * This code is written by Michael Graff <graff@vix.com> for LMC. + * The code is derived from permitted modifications to software created + * by Matt Thomas (matt@3am-software.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All marketing or advertising materials mentioning features or + * use of this software must display the following acknowledgement: + * This product includes software developed by LAN Media Corporation + * and its contributors. + * 4. Neither the name of LAN Media Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> /* only for declaration of wakeup() used by vm.h */ +#if defined(__FreeBSD__) +#include <machine/clock.h> +#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/device.h> +#endif + +#if defined(__NetBSD__) +#include <dev/pci/pcidevs.h> +#include "rnd.h" +#if NRND > 0 +#include <sys/rnd.h> +#endif +#endif + +#if defined(__OpenBSD__) +#include <dev/pci/pcidevs.h> +#endif + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/netisr.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <net/if_sppp.h> +#endif + +#if defined(__bsdi__) +#if INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#endif + +#include <net/netisr.h> +#include <net/if.h> +#include <net/netisr.h> +#include <net/if_types.h> +#include <net/if_p2p.h> +#include <net/if_c_hdlc.h> +#endif + +#if defined(__FreeBSD__) +#include <vm/pmap.h> +#include <pci.h> +#if NPCI > 0 +#include <pci/pcivar.h> +#include <pci/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "pci/" +#endif +#endif /* __FreeBSD__ */ + +#if defined(__bsdi__) +#include <i386/pci/ic/dc21040.h> +#include <i386/isa/isa.h> +#include <i386/isa/icu.h> +#include <i386/isa/dma.h> +#include <i386/isa/isavar.h> +#include <i386/pci/pci.h> + +#define INCLUDE_PATH_PREFIX "i386/pci/" +#endif /* __bsdi__ */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <machine/bus.h> +#if defined(__alpha__) && defined(__NetBSD__) +#include <machine/intr.h> +#endif +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/ic/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "dev/pci/" +#endif /* __NetBSD__ */ + +#if defined(__OpenBSD__) +#define d_length1 u.bd_length1 +#define d_length2 u.bd_length2 +#define d_flag u.bd_flag +#endif + +/* + * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use + * a C preprocessor that allows this hack, but BSDI does not. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#include INCLUDE_PATH_PREFIX "if_lmc_types.h" +#include INCLUDE_PATH_PREFIX "if_lmcioctl.h" +#include INCLUDE_PATH_PREFIX "if_lmcvar.h" +#else /* BSDI */ +#include "i386/pci/if_lmc_types.h" +#include "i386/pci/if_lmcioctl.h" +#include "i386/pci/if_lmcvar.h" +#endif + +void +lmc_gpio_mkinput(lmc_softc_t * const sc, u_int32_t bits) +{ + sc->lmc_gpio_io &= ~bits; + LMC_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET | (sc->lmc_gpio_io)); +} + +void +lmc_gpio_mkoutput(lmc_softc_t * const sc, u_int32_t bits) +{ + sc->lmc_gpio_io |= bits; + LMC_CSR_WRITE(sc, csr_gp, TULIP_GP_PINSET | (sc->lmc_gpio_io)); +} + +void +lmc_led_on(lmc_softc_t * const sc, u_int32_t led) +{ + sc->lmc_miireg16 &= ~led; + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + +void +lmc_led_off(lmc_softc_t * const sc, u_int32_t led) +{ + sc->lmc_miireg16 |= led; + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + +void +lmc_reset(lmc_softc_t * const sc) +{ + sc->lmc_miireg16 |= LMC_MII16_FIFO_RESET; + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); + + sc->lmc_miireg16 &= ~LMC_MII16_FIFO_RESET; + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); + + /* + * make some of the GPIO pins be outputs + */ + lmc_gpio_mkoutput(sc, LMC_GEP_DP | LMC_GEP_RESET); + + /* + * drive DP and RESET low to force configuration. This also forces + * the transmitter clock to be internal, but we expect to reset + * that later anyway. + */ + sc->lmc_gpio &= ~(LMC_GEP_DP | LMC_GEP_RESET); + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + + /* + * hold for more than 10 microseconds + */ + DELAY(50); + + /* + * stop driving Xilinx-related signals + */ + lmc_gpio_mkinput(sc, LMC_GEP_DP | LMC_GEP_RESET); + + /* + * busy wait for the chip to reset + */ + while ((LMC_CSR_READ(sc, csr_gp) & LMC_GEP_DP) == 0) + ; + + /* + * Call media specific init routine + */ + sc->lmc_media->init(sc); +} + +void +lmc_dec_reset(lmc_softc_t * const sc) +{ +#ifndef __linux__ + lmc_ringinfo_t *ri; + tulip_desc_t *di; +#endif + u_int32_t val; + + /* + * disable all interrupts + */ + sc->lmc_intrmask = 0; + LMC_CSR_WRITE(sc, csr_intr, sc->lmc_intrmask); + + /* + * we are, obviously, down. + */ +#ifndef __linux__ + sc->lmc_flags &= ~(LMC_IFUP | LMC_MODEMOK); + + DP(("lmc_dec_reset\n")); +#endif + + /* + * Reset the chip with a software reset command. + * Wait 10 microseconds (actually 50 PCI cycles but at + * 33MHz that comes to two microseconds but wait a + * bit longer anyways) + */ + LMC_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET); + DELAY(10); + sc->lmc_cmdmode = LMC_CSR_READ(sc, csr_command); + + /* + * We want: + * no ethernet address in frames we write + * disable padding (txdesc, padding disable) + * ignore runt frames (rdes0 bit 15) + * no receiver watchdog or transmitter jabber timer + * (csr15 bit 0,14 == 1) + * if using 16-bit CRC, turn off CRC (trans desc, crc disable) + */ + +#ifndef TULIP_CMD_RECEIVEALL +#define TULIP_CMD_RECEIVEALL 0x40000000L +#endif + + sc->lmc_cmdmode |= ( TULIP_CMD_PROMISCUOUS + | TULIP_CMD_FULLDUPLEX + | TULIP_CMD_PASSBADPKT + | TULIP_CMD_NOHEARTBEAT + | TULIP_CMD_PORTSELECT + | TULIP_CMD_RECEIVEALL + | TULIP_CMD_MUSTBEONE + ); + sc->lmc_cmdmode &= ~( TULIP_CMD_OPERMODE + | TULIP_CMD_THRESHOLDCTL + | TULIP_CMD_STOREFWD + | TULIP_CMD_TXTHRSHLDCTL + ); + + LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode); + + /* + * disable receiver watchdog and transmit jabber + */ + val = LMC_CSR_READ(sc, csr_sia_general); + val |= (TULIP_WATCHDOG_TXDISABLE | TULIP_WATCHDOG_RXDISABLE); + LMC_CSR_WRITE(sc, csr_sia_general, val); + + /* + * turn off those LEDs... + */ + sc->lmc_miireg16 |= LMC_MII16_LED_ALL; + lmc_led_on(sc, LMC_MII16_LED0); + +#ifndef __linux__ + /* + * reprogram the tx desc, rx desc, and PCI bus options + */ + LMC_CSR_WRITE(sc, csr_txlist, + LMC_KVATOPHYS(sc, &sc->lmc_txinfo.ri_first[0])); + LMC_CSR_WRITE(sc, csr_rxlist, + LMC_KVATOPHYS(sc, &sc->lmc_rxinfo.ri_first[0])); + LMC_CSR_WRITE(sc, csr_busmode, + (1 << (LMC_BURSTSIZE(sc->lmc_unit) + 8)) + |TULIP_BUSMODE_CACHE_ALIGN8 + |TULIP_BUSMODE_READMULTIPLE + |(BYTE_ORDER != LITTLE_ENDIAN ? TULIP_BUSMODE_BIGENDIAN : 0)); + + sc->lmc_txq.ifq_maxlen = LMC_TXDESCS; + + /* + * Free all the mbufs that were on the transmit ring. + */ + for (;;) { + struct mbuf *m; + + IF_DEQUEUE(&sc->lmc_txq, m); + if (m == NULL) + break; + m_freem(m); + } + + /* + * reset descriptor state and reclaim all descriptors. + */ + ri = &sc->lmc_txinfo; + ri->ri_nextin = ri->ri_nextout = ri->ri_first; + ri->ri_free = ri->ri_max; + for (di = ri->ri_first; di < ri->ri_last; di++) + di->d_status = 0; + + /* + * We need to collect all the mbufs were on the + * receive ring before we reinit it either to put + * them back on or to know if we have to allocate + * more. + */ + ri = &sc->lmc_rxinfo; + ri->ri_nextin = ri->ri_nextout = ri->ri_first; + ri->ri_free = ri->ri_max; + for (di = ri->ri_first; di < ri->ri_last; di++) { + di->d_status = 0; + di->d_length1 = 0; di->d_addr1 = 0; + di->d_length2 = 0; di->d_addr2 = 0; + } + for (;;) { + struct mbuf *m; + IF_DEQUEUE(&sc->lmc_rxq, m); + if (m == NULL) + break; + m_freem(m); + } +#endif +} + +void +lmc_initcsrs(lmc_softc_t * const sc, lmc_csrptr_t csr_base, + size_t csr_size) +{ + sc->lmc_csrs.csr_busmode = csr_base + 0 * csr_size; + sc->lmc_csrs.csr_txpoll = csr_base + 1 * csr_size; + sc->lmc_csrs.csr_rxpoll = csr_base + 2 * csr_size; + sc->lmc_csrs.csr_rxlist = csr_base + 3 * csr_size; + sc->lmc_csrs.csr_txlist = csr_base + 4 * csr_size; + sc->lmc_csrs.csr_status = csr_base + 5 * csr_size; + sc->lmc_csrs.csr_command = csr_base + 6 * csr_size; + sc->lmc_csrs.csr_intr = csr_base + 7 * csr_size; + sc->lmc_csrs.csr_missed_frames = csr_base + 8 * csr_size; + sc->lmc_csrs.csr_9 = csr_base + 9 * csr_size; + sc->lmc_csrs.csr_10 = csr_base + 10 * csr_size; + sc->lmc_csrs.csr_11 = csr_base + 11 * csr_size; + sc->lmc_csrs.csr_12 = csr_base + 12 * csr_size; + sc->lmc_csrs.csr_13 = csr_base + 13 * csr_size; + sc->lmc_csrs.csr_14 = csr_base + 14 * csr_size; + sc->lmc_csrs.csr_15 = csr_base + 15 * csr_size; +} diff --git a/sys/dev/pci/if_lmc_media.c b/sys/dev/pci/if_lmc_media.c new file mode 100644 index 00000000000..7bcfa3bff59 --- /dev/null +++ b/sys/dev/pci/if_lmc_media.c @@ -0,0 +1,797 @@ +/* $NetBSD: if_lmc_media.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */ + +/*- + * Copyright (c) 1997-1999 LAN Media Corporation (LMC) + * All rights reserved. www.lanmedia.com + * + * This code is written by Michael Graff <graff@vix.com> for LMC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All marketing or advertising materials mentioning features or + * use of this software must display the following acknowledgement: + * This product includes software developed by LAN Media Corporation + * and its contributors. + * 4. Neither the name of LAN Media Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> /* only for declaration of wakeup() used by vm.h */ +#if defined(__FreeBSD__) +#include <machine/clock.h> +#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/device.h> +#endif + +#if defined(__NetBSD__) +#include <dev/pci/pcidevs.h> +#include "rnd.h" +#if NRND > 0 +#include <sys/rnd.h> +#endif +#endif + +#if defined(__OpenBSD__) +#include <dev/pci/pcidevs.h> +#endif + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/netisr.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <net/if_sppp.h> +#endif + +#if defined(__bsdi__) +#if INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#endif + +#include <net/netisr.h> +#include <net/if.h> +#include <net/netisr.h> +#include <net/if_types.h> +#include <net/if_p2p.h> +#include <net/if_c_hdlc.h> +#endif + +#if defined(__FreeBSD__) +#include <vm/pmap.h> +#include <pci.h> +#if NPCI > 0 +#include <pci/pcivar.h> +#include <pci/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "pci/" +#endif +#endif /* __FreeBSD__ */ + +#if defined(__bsdi__) +#include <i386/pci/ic/dc21040.h> +#include <i386/isa/isa.h> +#include <i386/isa/icu.h> +#include <i386/isa/dma.h> +#include <i386/isa/isavar.h> +#include <i386/pci/pci.h> + +#define INCLUDE_PATH_PREFIX "i386/pci/" +#endif /* __bsdi__ */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <machine/bus.h> +#if defined(__alpha__) && defined(__NetBSD__) +#include <machine/intr.h> +#endif +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/ic/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "dev/pci/" +#endif /* __NetBSD__ */ + +/* + * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use + * a C preprocessor that allows this hack, but BSDI does not. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#include INCLUDE_PATH_PREFIX "if_lmc_types.h" +#include INCLUDE_PATH_PREFIX "if_lmcioctl.h" +#include INCLUDE_PATH_PREFIX "if_lmcvar.h" +#else /* BSDI */ +#include "i386/pci/if_lmc_types.h" +#include "i386/pci/if_lmcioctl.h" +#include "i386/pci/if_lmcvar.h" +#endif + +/* + * For lack of a better place, put the T1 cable stuff here. + */ +char *lmc_t1_cables[] = { + "V.10/RS423", "EIA530A", "reserved", "X.21", "V.35", + "EIA449/EIA530/V.36", "V.28/EIA232", "none", NULL +}; + +/* + * protocol independent method. + */ +static void lmc_set_protocol(lmc_softc_t * const, lmc_ctl_t *); + +/* + * media independent methods to check on media status, link, light LEDs, + * etc. + */ +static void lmc_ds3_init(lmc_softc_t * const); +static void lmc_ds3_default(lmc_softc_t * const); +static void lmc_ds3_set_status(lmc_softc_t * const, lmc_ctl_t *); +static void lmc_ds3_set_100ft(lmc_softc_t * const, int); +static int lmc_ds3_get_link_status(lmc_softc_t * const); +static void lmc_ds3_set_crc_length(lmc_softc_t * const, int); +static void lmc_ds3_set_scram(lmc_softc_t * const, int); + +static void lmc_hssi_init(lmc_softc_t * const); +static void lmc_hssi_default(lmc_softc_t * const); +static void lmc_hssi_set_status(lmc_softc_t * const, lmc_ctl_t *); +static void lmc_hssi_set_clock(lmc_softc_t * const, int); +static int lmc_hssi_get_link_status(lmc_softc_t * const); +static void lmc_hssi_set_link_status(lmc_softc_t * const, int); +static void lmc_hssi_set_crc_length(lmc_softc_t * const, int); + +static void lmc_t1_init(lmc_softc_t * const); +static void lmc_t1_default(lmc_softc_t * const); +static void lmc_t1_set_status(lmc_softc_t * const, lmc_ctl_t *); +static void lmc_t1_set_clock(lmc_softc_t * const, int); +static void lmc_t1_set_speed(lmc_softc_t * const, lmc_ctl_t *); +static int lmc_t1_get_link_status(lmc_softc_t * const); +static void lmc_t1_set_link_status(lmc_softc_t * const, int); +static void lmc_t1_set_crc_length(lmc_softc_t * const, int); + +static void lmc_dummy_set_1(lmc_softc_t * const, int); +static void lmc_dummy_set2_1(lmc_softc_t * const, lmc_ctl_t *); + +static inline void write_av9110_bit(lmc_softc_t *, int); +static void write_av9110(lmc_softc_t *, u_int32_t, u_int32_t, u_int32_t, + u_int32_t, u_int32_t); + +lmc_media_t lmc_ds3_media = { + lmc_ds3_init, /* special media init stuff */ + lmc_ds3_default, /* reset to default state */ + lmc_ds3_set_status, /* reset status to state provided */ + lmc_dummy_set_1, /* set clock source */ + lmc_dummy_set2_1, /* set line speed */ + lmc_ds3_set_100ft, /* set cable length */ + lmc_ds3_set_scram, /* set scrambler */ + lmc_ds3_get_link_status, /* get link status */ + lmc_dummy_set_1, /* set link status */ + lmc_ds3_set_crc_length, /* set CRC length */ +}; + +lmc_media_t lmc_hssi_media = { + lmc_hssi_init, /* special media init stuff */ + lmc_hssi_default, /* reset to default state */ + lmc_hssi_set_status, /* reset status to state provided */ + lmc_hssi_set_clock, /* set clock source */ + lmc_dummy_set2_1, /* set line speed */ + lmc_dummy_set_1, /* set cable length */ + lmc_dummy_set_1, /* set scrambler */ + lmc_hssi_get_link_status, /* get link status */ + lmc_hssi_set_link_status, /* set link status */ + lmc_hssi_set_crc_length, /* set CRC length */ +}; + +lmc_media_t lmc_t1_media = { + lmc_t1_init, /* special media init stuff */ + lmc_t1_default, /* reset to default state */ + lmc_t1_set_status, /* reset status to state provided */ + lmc_t1_set_clock, /* set clock source */ + lmc_t1_set_speed, /* set line speed */ + lmc_dummy_set_1, /* set cable length */ + lmc_dummy_set_1, /* set scrambler */ + lmc_t1_get_link_status, /* get link status */ + lmc_t1_set_link_status, /* set link status */ + lmc_t1_set_crc_length, /* set CRC length */ +}; + +static void +lmc_dummy_set_1(lmc_softc_t * const sc, int a) +{ +} + +static void +lmc_dummy_set2_1(lmc_softc_t * const sc, lmc_ctl_t *a) +{ +} + +/* + * HSSI methods + */ + +static void +lmc_hssi_init(lmc_softc_t * const sc) +{ + sc->ictl.cardtype = LMC_CTL_CARDTYPE_LMC5200; + + lmc_gpio_mkoutput(sc, LMC_GEP_HSSI_CLOCK); +} + +static void +lmc_hssi_default(lmc_softc_t * const sc) +{ + sc->lmc_miireg16 = LMC_MII16_LED_ALL; + + sc->lmc_media->set_link_status(sc, 0); + sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT); + sc->lmc_media->set_crc_length(sc, LMC_CTL_CRC_LENGTH_16); +} + +/* + * Given a user provided state, set ourselves up to match it. This will + * always reset the card if needed. + */ +static void +lmc_hssi_set_status(lmc_softc_t * const sc, lmc_ctl_t *ctl) +{ + if (ctl == NULL) { + sc->lmc_media->set_clock_source(sc, sc->ictl.clock_source); + lmc_set_protocol(sc, NULL); + + return; + } + + /* + * check for change in clock source + */ + if (ctl->clock_source && !sc->ictl.clock_source) + sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_INT); + else if (!ctl->clock_source && sc->ictl.clock_source) + sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT); + + lmc_set_protocol(sc, ctl); +} + +/* + * 1 == internal, 0 == external + */ +static void +lmc_hssi_set_clock(lmc_softc_t * const sc, int ie) +{ + if (ie == LMC_CTL_CLOCK_SOURCE_EXT) { + sc->lmc_gpio |= LMC_GEP_HSSI_CLOCK; + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_EXT; + printf(LMC_PRINTF_FMT ": clock external\n", + LMC_PRINTF_ARGS); + } else { + sc->lmc_gpio &= ~(LMC_GEP_HSSI_CLOCK); + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_INT; + printf(LMC_PRINTF_FMT ": clock internal\n", + LMC_PRINTF_ARGS); + } +} + +/* + * return hardware link status. + * 0 == link is down, 1 == link is up. + */ +static int +lmc_hssi_get_link_status(lmc_softc_t * const sc) +{ + u_int16_t link_status; + + link_status = lmc_mii_readreg(sc, 0, 16); + + if ((link_status & LMC_MII16_HSSI_CA) == LMC_MII16_HSSI_CA) + return 1; + else + return 0; +} + +static void +lmc_hssi_set_link_status(lmc_softc_t * const sc, int state) +{ + if (state) + sc->lmc_miireg16 |= LMC_MII16_HSSI_TA; + else + sc->lmc_miireg16 &= ~LMC_MII16_HSSI_TA; + + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + +/* + * 0 == 16bit, 1 == 32bit + */ +static void +lmc_hssi_set_crc_length(lmc_softc_t * const sc, int state) +{ + if (state == LMC_CTL_CRC_LENGTH_32) { + /* 32 bit */ + sc->lmc_miireg16 |= LMC_MII16_HSSI_CRC; + sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_32; + } else { + /* 16 bit */ + sc->lmc_miireg16 &= ~LMC_MII16_HSSI_CRC; + sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_16; + } + + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + + +/* + * DS3 methods + */ + +/* + * Set cable length + */ +static void +lmc_ds3_set_100ft(lmc_softc_t * const sc, int ie) +{ + if (ie == LMC_CTL_CABLE_LENGTH_GT_100FT) { + sc->lmc_miireg16 &= ~LMC_MII16_DS3_ZERO; + sc->ictl.cable_length = LMC_CTL_CABLE_LENGTH_GT_100FT; + } else if (ie == LMC_CTL_CABLE_LENGTH_LT_100FT) { + sc->lmc_miireg16 |= LMC_MII16_DS3_ZERO; + sc->ictl.cable_length = LMC_CTL_CABLE_LENGTH_LT_100FT; + } + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + +static void +lmc_ds3_default(lmc_softc_t * const sc) +{ + sc->lmc_miireg16 = LMC_MII16_LED_ALL; + + sc->lmc_media->set_link_status(sc, 0); + sc->lmc_media->set_cable_length(sc, LMC_CTL_CABLE_LENGTH_LT_100FT); + sc->lmc_media->set_scrambler(sc, LMC_CTL_OFF); + sc->lmc_media->set_crc_length(sc, LMC_CTL_CRC_LENGTH_16); +} + +/* + * Given a user provided state, set ourselves up to match it. This will + * always reset the card if needed. + */ +static void +lmc_ds3_set_status(lmc_softc_t * const sc, lmc_ctl_t *ctl) +{ + if (ctl == NULL) { + sc->lmc_media->set_cable_length(sc, sc->ictl.cable_length); + sc->lmc_media->set_scrambler(sc, sc->ictl.scrambler_onoff); + lmc_set_protocol(sc, NULL); + + return; + } + + /* + * check for change in cable length setting + */ + if (ctl->cable_length && !sc->ictl.cable_length) + lmc_ds3_set_100ft(sc, LMC_CTL_CABLE_LENGTH_GT_100FT); + else if (!ctl->cable_length && sc->ictl.cable_length) + lmc_ds3_set_100ft(sc, LMC_CTL_CABLE_LENGTH_LT_100FT); + + /* + * Check for change in scrambler setting (requires reset) + */ + if (ctl->scrambler_onoff && !sc->ictl.scrambler_onoff) + lmc_ds3_set_scram(sc, LMC_CTL_ON); + else if (!ctl->scrambler_onoff && sc->ictl.scrambler_onoff) + lmc_ds3_set_scram(sc, LMC_CTL_OFF); + + lmc_set_protocol(sc, ctl); +} + +static void +lmc_ds3_init(lmc_softc_t * const sc) +{ + int i; + + sc->ictl.cardtype = LMC_CTL_CARDTYPE_LMC5245; + + /* writes zeros everywhere */ + for (i = 0 ; i < 21 ; i++) { + lmc_mii_writereg(sc, 0, 17, i); + lmc_mii_writereg(sc, 0, 18, 0); + } + + /* set some essential bits */ + lmc_mii_writereg(sc, 0, 17, 1); + lmc_mii_writereg(sc, 0, 18, 0x05); /* ser, xtx */ + + lmc_mii_writereg(sc, 0, 17, 5); + lmc_mii_writereg(sc, 0, 18, 0x80); /* emode */ + + lmc_mii_writereg(sc, 0, 17, 14); + lmc_mii_writereg(sc, 0, 18, 0x30); /* rcgen, tcgen */ + + /* clear counters and latched bits */ + for (i = 0 ; i < 21 ; i++) { + lmc_mii_writereg(sc, 0, 17, i); + lmc_mii_readreg(sc, 0, 18); + } +} + +/* + * 1 == DS3 payload scrambled, 0 == not scrambled + */ +static void +lmc_ds3_set_scram(lmc_softc_t * const sc, int ie) +{ + if (ie == LMC_CTL_ON) { + sc->lmc_miireg16 |= LMC_MII16_DS3_SCRAM; + sc->ictl.scrambler_onoff = LMC_CTL_ON; + } else { + sc->lmc_miireg16 &= ~LMC_MII16_DS3_SCRAM; + sc->ictl.scrambler_onoff = LMC_CTL_OFF; + } + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + +/* + * return hardware link status. + * 0 == link is down, 1 == link is up. + */ +static int +lmc_ds3_get_link_status(lmc_softc_t * const sc) +{ + u_int16_t link_status; + + lmc_mii_writereg(sc, 0, 17, 7); + link_status = lmc_mii_readreg(sc, 0, 18); + + if ((link_status & LMC_FRAMER_REG0_DLOS) == 0) + return 1; + else + return 0; +} + +/* + * 0 == 16bit, 1 == 32bit + */ +static void +lmc_ds3_set_crc_length(lmc_softc_t * const sc, int state) +{ + if (state == LMC_CTL_CRC_LENGTH_32) { + /* 32 bit */ + sc->lmc_miireg16 |= LMC_MII16_DS3_CRC; + sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_32; + } else { + /* 16 bit */ + sc->lmc_miireg16 &= ~LMC_MII16_DS3_CRC; + sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_16; + } + + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + + +/* + * T1 methods + */ + +static void +lmc_t1_init(lmc_softc_t * const sc) +{ + u_int16_t mii17; + int cable; + + sc->ictl.cardtype = LMC_CTL_CARDTYPE_LMC1000; + + mii17 = lmc_mii_readreg(sc, 0, 17); + + cable = (mii17 & LMC_MII17_T1_CABLE_MASK) >> LMC_MII17_T1_CABLE_SHIFT; + sc->ictl.cable_type = cable; + + lmc_gpio_mkoutput(sc, LMC_GEP_T1_TXCLOCK); +} + +static void +lmc_t1_default(lmc_softc_t * const sc) +{ + sc->lmc_miireg16 = LMC_MII16_LED_ALL; + + /* + * make TXCLOCK always be an output + */ + lmc_gpio_mkoutput(sc, LMC_GEP_T1_TXCLOCK); + + sc->lmc_media->set_link_status(sc, 0); + sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT); + sc->lmc_media->set_speed(sc, NULL); + sc->lmc_media->set_crc_length(sc, LMC_CTL_CRC_LENGTH_16); +} + +/* + * Given a user provided state, set ourselves up to match it. This will + * always reset the card if needed. + */ +static void +lmc_t1_set_status(lmc_softc_t * const sc, lmc_ctl_t *ctl) +{ + if (ctl == NULL) { + sc->lmc_media->set_clock_source(sc, sc->ictl.clock_source); + sc->lmc_media->set_speed(sc, &sc->ictl); + lmc_set_protocol(sc, NULL); + + return; + } + + /* + * check for change in clock source + */ + if (ctl->clock_source == LMC_CTL_CLOCK_SOURCE_INT + && sc->ictl.clock_source == LMC_CTL_CLOCK_SOURCE_EXT) + sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_INT); + else if (ctl->clock_source == LMC_CTL_CLOCK_SOURCE_EXT + && sc->ictl.clock_source == LMC_CTL_CLOCK_SOURCE_INT) + sc->lmc_media->set_clock_source(sc, LMC_CTL_CLOCK_SOURCE_EXT); + + if (ctl->clock_rate != sc->ictl.clock_rate) + sc->lmc_media->set_speed(sc, ctl); + + lmc_set_protocol(sc, ctl); +} + +/* + * 1 == internal, 0 == external + */ +static void +lmc_t1_set_clock(lmc_softc_t * const sc, int ie) +{ + if (ie == LMC_CTL_CLOCK_SOURCE_EXT) { + sc->lmc_gpio &= ~(LMC_GEP_T1_TXCLOCK); + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_EXT; + printf(LMC_PRINTF_FMT ": clock external\n", + LMC_PRINTF_ARGS); + } else { + sc->lmc_gpio |= LMC_GEP_T1_TXCLOCK; + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + sc->ictl.clock_source = LMC_CTL_CLOCK_SOURCE_INT; + printf(LMC_PRINTF_FMT ": clock internal\n", + LMC_PRINTF_ARGS); + } +} + +static void +lmc_t1_set_speed(lmc_softc_t * const sc, lmc_ctl_t *ctl) +{ + lmc_ctl_t *ictl = &sc->ictl; + lmc_av9110_t *av; + + if (ctl == NULL) { + av = &ictl->cardspec.t1; + ictl->clock_rate = 100000; + av->f = ictl->clock_rate; + av->n = 8; + av->m = 25; + av->v = 0; + av->x = 0; + av->r = 2; + + write_av9110(sc, av->n, av->m, av->v, av->x, av->r); + return; + } + + av = &ctl->cardspec.t1; + + if (av->f == 0) + return; + + ictl->clock_rate = av->f; /* really, this is the rate we are */ + ictl->cardspec.t1 = *av; + + write_av9110(sc, av->n, av->m, av->v, av->x, av->r); +} + +/* + * return hardware link status. + * 0 == link is down, 1 == link is up. + */ +static int +lmc_t1_get_link_status(lmc_softc_t * const sc) +{ + u_int16_t link_status; + + /* + * missing CTS? Hmm. If we require CTS on, we may never get the + * link to come up, so omit it in this test. + * + * Also, it seems that with a loopback cable, DCD isn't asserted, + * so just check for things like this: + * DSR _must_ be asserted. + * One of DCD or CTS must be asserted. + */ + link_status = lmc_mii_readreg(sc, 0, 16); + + if ((link_status & LMC_MII16_T1_DSR) == 0) + return (0); + + if ((link_status & (LMC_MII16_T1_CTS | LMC_MII16_T1_DCD)) == 0) + return (0); + + return (1); +} + +static void +lmc_t1_set_link_status(lmc_softc_t * const sc, int state) +{ + if (state) { + sc->lmc_miireg16 |= (LMC_MII16_T1_DTR | LMC_MII16_T1_RTS); + printf(LMC_PRINTF_FMT ": asserting DTR and RTS\n", + LMC_PRINTF_ARGS); + } else { + sc->lmc_miireg16 &= ~(LMC_MII16_T1_DTR | LMC_MII16_T1_RTS); + printf(LMC_PRINTF_FMT ": deasserting DTR and RTS\n", + LMC_PRINTF_ARGS); + } + + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + +/* + * 0 == 16bit, 1 == 32bit + */ +static void +lmc_t1_set_crc_length(lmc_softc_t * const sc, int state) +{ + if (state == LMC_CTL_CRC_LENGTH_32) { + /* 32 bit */ + sc->lmc_miireg16 |= LMC_MII16_T1_CRC; + sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_32; + } else { + /* 16 bit */ + sc->lmc_miireg16 &= ~LMC_MII16_T1_CRC; + sc->ictl.crc_length = LMC_CTL_CRC_LENGTH_16; + } + + lmc_mii_writereg(sc, 0, 16, sc->lmc_miireg16); +} + +/* + * These are bits to program the T1 frequency generator + */ +static inline void +write_av9110_bit(lmc_softc_t *sc, int c) +{ + /* + * set the data bit as we need it. + */ + sc->lmc_gpio &= ~(LMC_GEP_SERIALCLK); + if (c & 0x01) + sc->lmc_gpio |= LMC_GEP_SERIAL; + else + sc->lmc_gpio &= ~(LMC_GEP_SERIAL); + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + + /* + * set the clock to high + */ + sc->lmc_gpio |= LMC_GEP_SERIALCLK; + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + + /* + * set the clock to low again. + */ + sc->lmc_gpio &= ~(LMC_GEP_SERIALCLK); + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); +} + +static void +write_av9110(lmc_softc_t *sc, u_int32_t n, u_int32_t m, u_int32_t v, + u_int32_t x, u_int32_t r) +{ + int i; + +#if 0 + printf(LMC_PRINTF_FMT ": speed %u, %d %d %d %d %d\n", + LMC_PRINTF_ARGS, sc->ictl.clock_rate, + n, m, v, x, r); +#endif + + sc->lmc_gpio |= LMC_GEP_T1_GENERATOR; + sc->lmc_gpio &= ~(LMC_GEP_SERIAL | LMC_GEP_SERIALCLK); + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + + /* + * Set the TXCLOCK, GENERATOR, SERIAL, and SERIALCLK + * as outputs. + */ + lmc_gpio_mkoutput(sc, (LMC_GEP_SERIAL | LMC_GEP_SERIALCLK + | LMC_GEP_T1_GENERATOR)); + + sc->lmc_gpio &= ~(LMC_GEP_T1_GENERATOR); + LMC_CSR_WRITE(sc, csr_gp, sc->lmc_gpio); + + /* + * a shifting we will go... + */ + for (i = 0 ; i < 7 ; i++) + write_av9110_bit(sc, n >> i); + for (i = 0 ; i < 7 ; i++) + write_av9110_bit(sc, m >> i); + for (i = 0 ; i < 1 ; i++) + write_av9110_bit(sc, v >> i); + for (i = 0 ; i < 2 ; i++) + write_av9110_bit(sc, x >> i); + for (i = 0 ; i < 2 ; i++) + write_av9110_bit(sc, r >> i); + for (i = 0 ; i < 5 ; i++) + write_av9110_bit(sc, 0x17 >> i); + + /* + * stop driving serial-related signals + */ + lmc_gpio_mkinput(sc, + (LMC_GEP_SERIAL | LMC_GEP_SERIALCLK + | LMC_GEP_T1_GENERATOR)); +} + +static void +lmc_set_protocol(lmc_softc_t * const sc, lmc_ctl_t *ctl) +{ + if (ctl == 0) { + sc->ictl.keepalive_onoff = LMC_CTL_ON; + + return; + } + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + if (ctl->keepalive_onoff != sc->ictl.keepalive_onoff) { + switch (ctl->keepalive_onoff) { + case LMC_CTL_ON: + printf(LMC_PRINTF_FMT ": enabling keepalive\n", + LMC_PRINTF_ARGS); + sc->ictl.keepalive_onoff = LMC_CTL_ON; + sc->lmc_sppp.pp_flags = PP_CISCO | PP_KEEPALIVE; + break; + case LMC_CTL_OFF: + printf(LMC_PRINTF_FMT ": disabling keepalive\n", + LMC_PRINTF_ARGS); + sc->ictl.keepalive_onoff = LMC_CTL_OFF; + sc->lmc_sppp.pp_flags = PP_CISCO; + } + } +#endif +} diff --git a/sys/dev/pci/if_lmc_obsd.c b/sys/dev/pci/if_lmc_obsd.c new file mode 100644 index 00000000000..534616400d2 --- /dev/null +++ b/sys/dev/pci/if_lmc_obsd.c @@ -0,0 +1,427 @@ +/* $NetBSD: if_lmc_nbsd.c,v 1.1 1999/03/25 03:32:43 explorer Exp $ */ + +/*- + * Copyright (c) 1997-1999 LAN Media Corporation (LMC) + * All rights reserved. www.lanmedia.com + * + * This code is written by Michael Graff <graff@vix.com> for LMC. + * The code is derived from permitted modifications to software created + * by Matt Thomas (matt@3am-software.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All marketing or advertising materials mentioning features or + * use of this software must display the following acknowledgement: + * This product includes software developed by LAN Media Corporation + * and its contributors. + * 4. Neither the name of LAN Media Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> /* only for declaration of wakeup() used by vm.h */ +#if defined(__FreeBSD__) +#include <machine/clock.h> +#elif defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/device.h> +#endif + +#if defined(__NetBSD__) +#include <dev/pci/pcidevs.h> +#include "rnd.h" +#if NRND > 0 +#include <sys/rnd.h> +#endif +#endif + +#if defined(__OpenBSD__) +#include <dev/pci/pcidevs.h> +#endif + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/netisr.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <net/if_sppp.h> +#endif + +#if defined(__bsdi__) +#if INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#endif + +#include <net/netisr.h> +#include <net/if.h> +#include <net/netisr.h> +#include <net/if_types.h> +#include <net/if_p2p.h> +#include <net/if_c_hdlc.h> +#endif + +#if defined(__FreeBSD__) +#include <vm/pmap.h> +#include <pci.h> +#if NPCI > 0 +#include <pci/pcivar.h> +#include <pci/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "pci/" +#endif +#endif /* __FreeBSD__ */ + +#if defined(__bsdi__) +#include <i386/pci/ic/dc21040.h> +#include <i386/isa/isa.h> +#include <i386/isa/icu.h> +#include <i386/isa/dma.h> +#include <i386/isa/isavar.h> +#include <i386/pci/pci.h> + +#define INCLUDE_PATH_PREFIX "i386/pci/" +#endif /* __bsdi__ */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <machine/bus.h> +#if defined(__alpha__) && defined(__NetBSD__) +#include <machine/intr.h> +#endif +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/ic/dc21040reg.h> +#define INCLUDE_PATH_PREFIX "dev/pci/" +#endif /* __NetBSD__ */ + +/* + * Sigh. Every OS puts these in different places. NetBSD and FreeBSD use + * a C preprocessor that allows this hack, but BSDI does not. Grr. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#include INCLUDE_PATH_PREFIX "if_lmc_types.h" +#include INCLUDE_PATH_PREFIX "if_lmcioctl.h" +#include INCLUDE_PATH_PREFIX "if_lmcvar.h" +#else /* BSDI */ +#include "i386/pci/if_lmctypes.h" +#include "i386/pci/if_lmcioctl.h" +#include "i386/pci/if_lmcvar.h" +#endif + +/* + * This file is INCLUDED (gross, I know, but...) + */ + +static void lmc_shutdown(void *arg); + +static int +lmc_pci_probe(struct device *parent, +#if defined (__BROKEN_INDIRECT_CONFIG) || defined(__OpenBSD__) + void *match, +#else + struct cfdata *match, +#endif + void *aux) +{ + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + u_int32_t id; + + /* + * check first for the DEC chip we expect to find. We expect + * 21140A, pass 2.2 or higher. + */ + if (PCI_VENDORID(pa->pa_id) != PCI_VENDOR_DEC) + return 0; + if (PCI_CHIPID(pa->pa_id) != PCI_PRODUCT_DEC_21140) + return 0; + id = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFRV) & 0xff; + if (id < 0x22) + return 0; + + /* + * Next, check the subsystem ID and see if it matches what we + * expect. + */ + id = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SSID); + if (PCI_VENDORID(id) != PCI_VENDOR_LMC) + return 0; + if ((PCI_CHIPID(id) != PCI_PRODUCT_LMC_HSSI) + && (PCI_CHIPID(id) != PCI_PRODUCT_LMC_DS3) + && (PCI_CHIPID(id) != PCI_PRODUCT_LMC_SSI)) + return 0; + + return 10; /* must be > than any other tulip driver */ +} + +static void lmc_pci_attach(struct device * const parent, + struct device * const self, void * const aux); + +struct cfattach lmc_ca = { + sizeof(lmc_softc_t), lmc_pci_probe, lmc_pci_attach +}; + +struct cfdriver lmc_cd = { + 0, "lmc", DV_IFNET +}; + +static void +lmc_pci_attach(struct device * const parent, + struct device * const self, void * const aux) +{ + u_int32_t revinfo, cfdainfo, id, ssid; + pci_intr_handle_t intrhandle; + const char *intrstr; +#if !defined(LMC_IOMAPPED) + vm_offset_t pa_csrs; +#endif + unsigned csroffset = LMC_PCI_CSROFFSET; + unsigned csrsize = LMC_PCI_CSRSIZE; + lmc_csrptr_t csr_base; + lmc_spl_t s; + lmc_intrfunc_t (*intr_rtn)(void *) = lmc_intr_normal; + lmc_softc_t * const sc = (lmc_softc_t *) self; + struct pci_attach_args * const pa = (struct pci_attach_args *) aux; + extern lmc_media_t lmc_hssi_media; + extern lmc_media_t lmc_ds3_media; + extern lmc_media_t lmc_t1_media; + + revinfo = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFRV) & 0xFF; + id = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFID); + cfdainfo = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CFDA); + ssid = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SSID); + + switch (PCI_CHIPID(ssid)) { + case PCI_PRODUCT_LMC_HSSI: + printf(": Lan Media Corporation HSSI\n"); + sc->lmc_media = &lmc_hssi_media; + break; + case PCI_PRODUCT_LMC_DS3: + printf(": Lan Media Corporation DS3\n"); + sc->lmc_media = &lmc_ds3_media; + break; + case PCI_PRODUCT_LMC_SSI: + printf(": Lan Media Corporation SSI\n"); + sc->lmc_media = &lmc_t1_media; + break; + } + + sc->lmc_pci_busno = parent; + sc->lmc_pci_devno = pa->pa_device; + + sc->lmc_chipid = LMC_21140A; + sc->lmc_features |= LMC_HAVE_STOREFWD; + if (sc->lmc_chipid == LMC_21140A && revinfo <= 0x22) + sc->lmc_features |= LMC_HAVE_RXBADOVRFLW; + + if (cfdainfo & (TULIP_CFDA_SLEEP | TULIP_CFDA_SNOOZE)) { + cfdainfo &= ~(TULIP_CFDA_SLEEP | TULIP_CFDA_SNOOZE); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_CFDA, cfdainfo); + DELAY(11 * 1000); + } + + bcopy(self->dv_xname, sc->lmc_if.if_xname, IFNAMSIZ); + sc->lmc_if.if_softc = sc; + sc->lmc_pc = pa->pa_pc; + + sc->lmc_revinfo = revinfo; + sc->lmc_if.if_softc = sc; + + csr_base = 0; + { + bus_space_tag_t iot, memt; + bus_space_handle_t ioh, memh; + int ioh_valid, memh_valid; + +#if defined(__OpenBSD__) + pci_chipset_tag_t pc = pa->pa_pc; + bus_addr_t lmcbase; + bus_size_t lmcsize; + int retval; +#endif + +#if defined(__NetBSD__) + + ioh_valid = (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, + 0, &iot, &ioh, NULL, NULL) == 0); + memh_valid = (pci_mapreg_map(pa, PCI_CBMA, + PCI_MAPREG_TYPE_MEM | + PCI_MAPREG_MEM_TYPE_32BIT, + 0, &memt, &memh, NULL, + NULL) == 0); +#elif defined(__OpenBSD__) + ioh_valid = 0; + memh_valid = 0; + csr_base = 0; + + iot = pa->pa_iot; + retval = pci_io_find(pc, pa->pa_tag, PCI_CBIO, &lmcbase, &lmcsize); + + if(!retval) + retval = bus_space_map(pa->pa_iot, lmcbase, lmcsize, 0, &ioh); + ioh_valid = (retval == 0); + + if(!ioh_valid) { /* Can this if just be removed? */ + memt = pa->pa_memt; + retval = pci_mem_find(pc, pa->pa_tag, PCI_CBMA, &lmcbase, &lmcsize, NULL); + if(!retval) + retval = bus_space_map(pa->pa_memt, lmcbase, lmcsize, 0, &memh); + + memh_valid = (retval == 0); + } +#endif + + + if (memh_valid) { + sc->lmc_bustag = memt; + sc->lmc_bushandle = memh; + } else if (ioh_valid) { + sc->lmc_bustag = iot; + sc->lmc_bushandle = ioh; + } else { + printf("%s: unable to map device registers\n", + sc->lmc_dev.dv_xname); + return; + } + } + + lmc_initcsrs(sc, csr_base + csroffset, csrsize); + lmc_initring(sc, &sc->lmc_rxinfo, sc->lmc_rxdescs, + LMC_RXDESCS); + lmc_initring(sc, &sc->lmc_txinfo, sc->lmc_txdescs, + LMC_TXDESCS); + + lmc_gpio_mkinput(sc, 0xff); + sc->lmc_gpio = 0; /* drive no signals yet */ + + sc->lmc_media->defaults(sc); + + sc->lmc_media->set_link_status(sc, 0); /* down */ + + /* + * Make sure there won't be any interrupts or such... + */ + LMC_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET); + + /* + * Wait 10 microseconds (actually 50 PCI cycles but at + * 33MHz that comes to two microseconds but wait a + * bit longer anyways) + */ + DELAY(100); + + lmc_read_macaddr(sc); + printf("%s: pass %d.%d, serial " LMC_EADDR_FMT "\n", + sc->lmc_dev.dv_xname, + (sc->lmc_revinfo & 0xF0) >> 4, sc->lmc_revinfo & 0x0F, + LMC_EADDR_ARGS(sc->lmc_enaddr)); + + if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &intrhandle)) { + printf("%s: couldn't map interrupt\n", + sc->lmc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pa->pa_pc, intrhandle); + +#if defined(__OpenBSD__) + sc->lmc_ih = pci_intr_establish(pa->pa_pc, intrhandle, IPL_NET, + intr_rtn, sc, self->dv_xname); +#else + sc->lmc_ih = pci_intr_establish(pa->pa_pc, intrhandle, IPL_NET, + intr_rtn, sc); +#endif + + if (sc->lmc_ih == NULL) { + printf("%s: couldn't establish interrupt", + sc->lmc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf("%s: interrupting at %s\n", sc->lmc_dev.dv_xname, + intrstr); + + sc->lmc_ats = shutdownhook_establish(lmc_shutdown, sc); + if (sc->lmc_ats == NULL) + printf("%s: warning: couldn't establish shutdown hook\n", + sc->lmc_xname); + + s = LMC_RAISESPL(); + lmc_dec_reset(sc); + lmc_reset(sc); + lmc_attach(sc); + LMC_RESTORESPL(s); +} + +static void +lmc_shutdown(void *arg) +{ + lmc_softc_t * const sc = arg; + LMC_CSR_WRITE(sc, csr_busmode, TULIP_BUSMODE_SWRESET); + DELAY(10); + + sc->lmc_miireg16 = 0; /* deassert ready, and all others too */ + lmc_led_on(sc, LMC_MII16_LED_ALL); +} diff --git a/sys/dev/pci/if_lmc_types.h b/sys/dev/pci/if_lmc_types.h new file mode 100644 index 00000000000..8858f1c9b05 --- /dev/null +++ b/sys/dev/pci/if_lmc_types.h @@ -0,0 +1,88 @@ +/* $NetBSD: if_lmc_types.h,v 1.2 1999/03/25 04:09:33 explorer Exp $ */ + +/*- + * Copyright (c) 1997-1999 LAN Media Corporation (LMC) + * All rights reserved. www.lanmedia.com + * + * This code is written by Michael Graff <graff@vix.com> for LMC. + * The code is derived from permitted modifications to software created + * by Matt Thomas (matt@3am-software.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All marketing or advertising materials mentioning features or + * use of this software must display the following acknowledgement: + * This product includes software developed by LAN Media Corporation + * and its contributors. + * 4. Neither the name of LAN Media Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LMC_TYPES_H +#define LMC_TYPES_H + +/* + * NetBSD uses _KERNEL, FreeBSD uses KERNEL. + */ +#if defined(_KERNEL) && (defined(__NetBSD__) || defined(__OpenBSD__)) +#define LMC_IS_KERNEL +#endif +#if defined(KERNEL) && defined(__FreeBSD__) +#define LMC_IS_KERNEL +#endif +#if defined(__KERNEL__) && defined(linux) +#define LMC_IS_KERNEL +#endif + +#if defined(LMC_IS_KERNEL) +#if defined(__NetBSD__) || defined(__OpenBSD__) +typedef bus_addr_t lmc_csrptr_t; +#else +#ifdef LMC_IOMAPPED +typedef u_int16_t lmc_csrptr_t; +#else +typedef volatile u_int32_t *lmc_csrptr_t; +#endif +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#define lmc_intrfunc_t int +#endif /* __NetBSD__ */ + +#if defined(__FreeBSD__) +#define lmc_intrfunc_t void +#endif /* __FreeBSD__ */ + +#if defined(__bsdi__) +#define lmc_intrfunc_t int +#endif /* __bsdi__ */ + +typedef struct lmc___softc lmc_softc_t; +typedef struct lmc___media lmc_media_t; +typedef struct lmc_ringinfo lmc_ringinfo_t; + +#endif /* LMC_IS_KERNEL */ + +typedef struct lmc___ctl lmc_ctl_t; + +#endif /* LMC_TYPES_H */ diff --git a/sys/dev/pci/if_lmcioctl.h b/sys/dev/pci/if_lmcioctl.h new file mode 100644 index 00000000000..f1921d1f502 --- /dev/null +++ b/sys/dev/pci/if_lmcioctl.h @@ -0,0 +1,243 @@ +/* $NetBSD: if_lmcioctl.h,v 1.2 1999/03/25 04:09:34 explorer Exp $ */ + +/*- + * Copyright (c) 1997-1999 LAN Media Corporation (LMC) + * All rights reserved. www.lanmedia.com + * + * This code is written by Michael Graff <graff@vix.com> for LMC. + * The code is derived from permitted modifications to software created + * by Matt Thomas (matt@3am-software.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All marketing or advertising materials mentioning features or + * use of this software must display the following acknowledgement: + * This product includes software developed by LAN Media Corporation + * and its contributors. + * 4. Neither the name of LAN Media Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(linux) +/* + * IOCTLs that we use for linux. The structures passed in really should + * go into an OS inspecific file, since BSD will use these as well. + * + * Under linux, the 16 reserved-for-device IOCTLs are numbered 0x89f0 + * through 0x89ff. + */ +#define LMCIOCGINFO 0x89f0 /* get current state */ +#define LMCIOCSINFO 0x89f1 /* set state to user provided values */ + +#else +/* + * IOCTLs for the sane world. + */ +#define LMCIOCGINFO _IOW('i', 240, struct ifreq) +#define LMCIOCSINFO _IOWR('i', 241, struct ifreq) + +#endif + +typedef struct { + u_int32_t n; + u_int32_t m; + u_int32_t v; + u_int32_t x; + u_int32_t r; + u_int32_t f; + u_int32_t exact; +} lmc_av9110_t; + +/* + * Common structure passed to the ioctl code. + */ +struct lmc___ctl { + u_int32_t cardtype; + u_int32_t clock_source; /* HSSI, T1 */ + u_int32_t clock_rate; /* T1 */ + u_int32_t crc_length; + u_int32_t cable_length; /* DS3 */ + u_int32_t scrambler_onoff; /* DS3 */ + u_int32_t cable_type; /* T1 */ + u_int32_t keepalive_onoff; /* protocol */ + u_int32_t ticks; /* ticks/sec */ + union { + lmc_av9110_t t1; + } cardspec; +}; + +#define LMC_CTL_CARDTYPE_LMC5200 0 /* HSSI */ +#define LMC_CTL_CARDTYPE_LMC5245 1 /* DS3 */ +#define LMC_CTL_CARDTYPE_LMC1000 2 /* T1, E1, etc */ + +#define LMC_CTL_OFF 0 /* generic OFF value */ +#define LMC_CTL_ON 1 /* generic ON value */ + +#define LMC_CTL_CLOCK_SOURCE_EXT 0 /* clock off line */ +#define LMC_CTL_CLOCK_SOURCE_INT 1 /* internal clock */ + +#define LMC_CTL_CRC_LENGTH_16 16 +#define LMC_CTL_CRC_LENGTH_32 32 + +#define LMC_CTL_CABLE_LENGTH_LT_100FT 0 /* DS3 cable < 100 feet */ +#define LMC_CTL_CABLE_LENGTH_GT_100FT 1 /* DS3 cable >= 100 feet */ + +/* + * These are not in the least IOCTL related, but I want them common. + */ +/* + * assignments for the GPIO register on the DEC chip (common) + */ +#define LMC_GEP_INIT 0x01 /* 0: */ +#define LMC_GEP_RESET 0x02 /* 1: */ +#define LMC_GEP_LOAD 0x10 /* 4: */ +#define LMC_GEP_DP 0x20 /* 5: */ +#define LMC_GEP_SERIAL 0x40 /* 6: serial out */ +#define LMC_GEP_SERIALCLK 0x80 /* 7: serial clock */ + +/* + * HSSI GPIO assignments + */ +#define LMC_GEP_HSSI_ST 0x04 /* 2: receive timing sense (deprecated) */ +#define LMC_GEP_HSSI_CLOCK 0x08 /* 3: clock source */ + +/* + * T1 GPIO assignments + */ +#define LMC_GEP_T1_GENERATOR 0x04 /* 2: enable prog freq gen serial i/f */ +#define LMC_GEP_T1_TXCLOCK 0x08 /* 3: provide clock on TXCLOCK output */ + +/* + * Common MII16 bits + */ +#define LMC_MII16_LED0 0x0080 +#define LMC_MII16_LED1 0x0100 +#define LMC_MII16_LED2 0x0200 +#define LMC_MII16_LED3 0x0400 /* Error, and the red one */ +#define LMC_MII16_LED_ALL 0x0780 /* LED bit mask */ +#define LMC_MII16_FIFO_RESET 0x0800 + +/* + * definitions for HSSI + */ +#define LMC_MII16_HSSI_TA 0x0001 +#define LMC_MII16_HSSI_CA 0x0002 +#define LMC_MII16_HSSI_LA 0x0004 +#define LMC_MII16_HSSI_LB 0x0008 +#define LMC_MII16_HSSI_LC 0x0010 +#define LMC_MII16_HSSI_TM 0x0020 +#define LMC_MII16_HSSI_CRC 0x0040 + +/* + * assignments for the MII register 16 (DS3) + */ +#define LMC_MII16_DS3_ZERO 0x0001 +#define LMC_MII16_DS3_TRLBK 0x0002 +#define LMC_MII16_DS3_LNLBK 0x0004 +#define LMC_MII16_DS3_RAIS 0x0008 +#define LMC_MII16_DS3_TAIS 0x0010 +#define LMC_MII16_DS3_BIST 0x0020 +#define LMC_MII16_DS3_DLOS 0x0040 +#define LMC_MII16_DS3_CRC 0x1000 +#define LMC_MII16_DS3_SCRAM 0x2000 + +/* + * And T1 + */ +#define LMC_MII16_T1_DTR 0x0001 /* DTR output RW */ +#define LMC_MII16_T1_DSR 0x0002 /* DSR input RO */ +#define LMC_MII16_T1_RTS 0x0004 /* RTS output RW */ +#define LMC_MII16_T1_CTS 0x0008 /* CTS input RO */ +#define LMC_MII16_T1_DCD 0x0010 /* DCD input RO */ +#define LMC_MII16_T1_RI 0x0020 /* RI input RO */ +#define LMC_MII16_T1_CRC 0x0040 /* CRC select */ + +/* + * bits 0x0080 through 0x0800 are generic, and described + * above with LMC_MII16_LED[0123] _LED_ALL, and _FIFO_RESET + */ +#define LMC_MII16_T1_LL 0x1000 /* LL output RW */ +#define LMC_MII16_T1_RL 0x2000 /* RL output RW */ +#define LMC_MII16_T1_TM 0x4000 /* TM input RO */ +#define LMC_MII16_T1_LOOP 0x8000 /* loopback enable RW */ + +/* + * Some of the MII16 bits are mirrored in the MII17 register as well, + * but let's keep thing seperate for now, and get only the cable from + * the MII17. + */ +#define LMC_MII17_T1_CABLE_MASK 0x0038 /* mask to extract the cable type */ +#define LMC_MII17_T1_CABLE_SHIFT 3 /* shift to extract the cable type */ + +/* + * framer register 0 and 7 (7 is latched and reset on read) + */ +#define LMC_FRAMER_REG0_DLOS 0x80 /* digital loss of service */ +#define LMC_FRAMER_REG0_OOFS 0x40 /* out of frame sync */ +#define LMC_FRAMER_REG0_AIS 0x20 /* alarm indication signal */ +#define LMC_FRAMER_REG0_CIS 0x10 /* channel idle */ +#define LMC_FRAMER_REG0_LOC 0x08 /* loss of clock */ + +#define LMC_CARDTYPE_UNKNOWN -1 +#define LMC_CARDTYPE_HSSI 1 /* probed card is a HSSI card */ +#define LMC_CARDTYPE_DS3 2 /* probed card is a DS3 card */ +#define LMC_CARDTYPE_T1 3 /* probed card is a T1 card */ + +#if defined(LMC_IS_KERNEL) /* defined in if_lmc_types.h */ + +/* + * media independent methods to check on media status, link, light LEDs, + * etc. + */ +struct lmc___media { + void (* init)(lmc_softc_t * const); + void (* defaults)(lmc_softc_t * const); + void (* set_status)(lmc_softc_t * const, lmc_ctl_t *); + void (* set_clock_source)(lmc_softc_t * const, int); + void (* set_speed)(lmc_softc_t * const, lmc_ctl_t *); + void (* set_cable_length)(lmc_softc_t * const, int); + void (* set_scrambler)(lmc_softc_t * const, int); + int (* get_link_status)(lmc_softc_t * const); + void (* set_link_status)(lmc_softc_t * const, int); + void (* set_crc_length)(lmc_softc_t * const, int); +}; + +u_int32_t lmc_mii_readreg(lmc_softc_t * const sc, u_int32_t devaddr, + u_int32_t regno); +void lmc_mii_writereg(lmc_softc_t * const sc, u_int32_t devaddr, + u_int32_t regno, u_int32_t data); +void lmc_initcsrs(lmc_softc_t * const sc, lmc_csrptr_t csr_base, + size_t csr_size); +void lmc_dec_reset(lmc_softc_t * const sc); +void lmc_reset(lmc_softc_t * const sc); +void lmc_led_on(lmc_softc_t * const sc, u_int32_t led); +void lmc_led_off(lmc_softc_t * const sc, u_int32_t led); +void lmc_gpio_mkinput(lmc_softc_t * const sc, u_int32_t bits); +void lmc_gpio_mkoutput(lmc_softc_t * const sc, u_int32_t bits); +lmc_intrfunc_t lmc_intr_normal(void *); +int lmc_read_macaddr(lmc_softc_t * const sc); +void lmc_attach(lmc_softc_t * const sc); +void lmc_initring(lmc_softc_t * const sc, lmc_ringinfo_t * const ri, + tulip_desc_t *descs, int ndescs); + +#endif /* LMC_IS_KERNEL */ diff --git a/sys/dev/pci/if_lmcvar.h b/sys/dev/pci/if_lmcvar.h new file mode 100644 index 00000000000..f1693079fcf --- /dev/null +++ b/sys/dev/pci/if_lmcvar.h @@ -0,0 +1,609 @@ +/* $NetBSD: if_lmcvar.h,v 1.1 1999/03/25 03:32:43 explorer Exp $ */ + +/*- + * Copyright (c) 1997-1999 LAN Media Corporation (LMC) + * All rights reserved. www.lanmedia.com + * + * This code is written by Michael Graff <graff@vix.com> for LMC. + * The code is derived from permitted modifications to software created + * by Matt Thomas (matt@3am-software.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. All marketing or advertising materials mentioning features or + * use of this software must display the following acknowledgement: + * This product includes software developed by LAN Media Corporation + * and its contributors. + * 4. Neither the name of LAN Media Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY LAN MEDIA CORPORATION AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(_DEVAR_H) +#define _DEVAR_H + +#define LMC_MTU 1500 +#define PPP_HEADER_LEN 4 +#define BIG_PACKET + +/* + * Intel CPUs should use I/O mapped access. XXXMLG Is this true on NetBSD + * too? + */ +#if defined(__i386__) +#define LMC_IOMAPPED +#endif + +/* + * This turns on all sort of debugging stuff and make the + * driver much larger. + */ +#if 0 +#define LMC_DEBUG +#define DP(x) printf x +#else +#define DP(x) +#endif + +/* + * the dec chip has its own idea of what a receive error is, but we don't + * want to use it, as it will get the crc error wrong when 16-bit + * crcs are used. So, we only care about certain conditions. + */ +#ifndef TULIP_DSTS_RxMIIERR +#define TULIP_DSTS_RxMIIERR 0x00000008 +#endif +#define LMC_DSTS_ERRSUM (TULIP_DSTS_RxMIIERR) + +/* + * This is the PCI configuration support. + */ +#define PCI_CFID 0x00 /* Configuration ID */ +#define PCI_CFCS 0x04 /* Configurtion Command/Status */ +#define PCI_CFRV 0x08 /* Configuration Revision */ +#define PCI_CFLT 0x0c /* Configuration Latency Timer */ +#define PCI_CBIO 0x10 /* Configuration Base IO Address */ +#define PCI_CBMA 0x14 /* Configuration Base Memory Address */ +#define PCI_SSID 0x2c /* subsystem config register */ +#define PCI_CFIT 0x3c /* Configuration Interrupt */ +#define PCI_CFDA 0x40 /* Configuration Driver Area */ + +#define LMC_HZ 10 + +#ifndef TULIP_GP_PINSET +#define TULIP_GP_PINSET 0x00000100L +#endif +#ifndef TULIP_BUSMODE_READMULTIPLE +#define TULIP_BUSMODE_READMULTIPLE 0x00200000L +#endif + +#if defined(__NetBSD__) + +#include "rnd.h" +#if NRND > 0 +#include <sys/rnd.h> +#endif + +#endif /* NetBSD */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#define LMC_CSR_READ(sc, csr) \ + bus_space_read_4((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr) +#define LMC_CSR_WRITE(sc, csr, val) \ + bus_space_write_4((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr, (val)) + +#define LMC_CSR_READBYTE(sc, csr) \ + bus_space_read_1((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr) +#define LMC_CSR_WRITEBYTE(sc, csr, val) \ + bus_space_write_1((sc)->lmc_bustag, (sc)->lmc_bushandle, (sc)->lmc_csrs.csr, (val)) +#endif /* __NetBSD__ */ + +#ifdef LMC_IOMAPPED +#define LMC_EISA_CSRSIZE 16 +#define LMC_EISA_CSROFFSET 0 +#define LMC_PCI_CSRSIZE 8 +#define LMC_PCI_CSROFFSET 0 + +#if !defined(__NetBSD__) && !defined(__OpenBSD__) +#define LMC_CSR_READ(sc, csr) (inl((sc)->lmc_csrs.csr)) +#define LMC_CSR_WRITE(sc, csr, val) outl((sc)->lmc_csrs.csr, val) + +#define LMC_CSR_READBYTE(sc, csr) (inb((sc)->lmc_csrs.csr)) +#define LMC_CSR_WRITEBYTE(sc, csr, val) outb((sc)->lmc_csrs.csr, val) +#endif /* __NetBSD__ */ + +#else /* LMC_IOMAPPED */ + +#define LMC_PCI_CSRSIZE 8 +#define LMC_PCI_CSROFFSET 0 + +#if !defined(__NetBSD__) && !defined(__OpenBSD__) +/* + * macros to read and write CSRs. Note that the "0 +" in + * READ_CSR is to prevent the macro from being an lvalue + * and WRITE_CSR shouldn't be assigned from. + */ +#define LMC_CSR_READ(sc, csr) (0 + *(sc)->lmc_csrs.csr) +#define LMC_CSR_WRITE(sc, csr, val) ((void)(*(sc)->lmc_csrs.csr = (val))) +#endif /* __NetBSD__ */ + +#endif /* LMC_IOMAPPED */ + +/* + * This structure contains "pointers" for the registers on + * the various 21x4x chips. CSR0 through CSR8 are common + * to all chips. After that, it gets messy... + */ +typedef struct { + lmc_csrptr_t csr_busmode; /* CSR0 */ + lmc_csrptr_t csr_txpoll; /* CSR1 */ + lmc_csrptr_t csr_rxpoll; /* CSR2 */ + lmc_csrptr_t csr_rxlist; /* CSR3 */ + lmc_csrptr_t csr_txlist; /* CSR4 */ + lmc_csrptr_t csr_status; /* CSR5 */ + lmc_csrptr_t csr_command; /* CSR6 */ + lmc_csrptr_t csr_intr; /* CSR7 */ + lmc_csrptr_t csr_missed_frames; /* CSR8 */ + lmc_csrptr_t csr_9; /* CSR9 */ + lmc_csrptr_t csr_10; /* CSR10 */ + lmc_csrptr_t csr_11; /* CSR11 */ + lmc_csrptr_t csr_12; /* CSR12 */ + lmc_csrptr_t csr_13; /* CSR13 */ + lmc_csrptr_t csr_14; /* CSR14 */ + lmc_csrptr_t csr_15; /* CSR15 */ +} lmc_regfile_t; + +#define csr_enetrom csr_9 /* 21040 */ +#define csr_reserved csr_10 /* 21040 */ +#define csr_full_duplex csr_11 /* 21040 */ +#define csr_bootrom csr_10 /* 21041/21140A/?? */ +#define csr_gp csr_12 /* 21140* */ +#define csr_watchdog csr_15 /* 21140* */ +#define csr_gp_timer csr_11 /* 21041/21140* */ +#define csr_srom_mii csr_9 /* 21041/21140* */ +#define csr_sia_status csr_12 /* 2104x */ +#define csr_sia_connectivity csr_13 /* 2104x */ +#define csr_sia_tx_rx csr_14 /* 2104x */ +#define csr_sia_general csr_15 /* 2104x */ + +/* + * While 21x4x allows chaining of its descriptors, this driver + * doesn't take advantage of it. We keep the descriptors in a + * traditional FIFO ring. + */ +struct lmc_ringinfo { + tulip_desc_t *ri_first; /* first entry in ring */ + tulip_desc_t *ri_last; /* one after last entry */ + tulip_desc_t *ri_nextin; /* next to processed by host */ + tulip_desc_t *ri_nextout; /* next to processed by adapter */ + int ri_max; + int ri_free; +}; + +/* + * The 21040 has a stupid restriction in that the receive + * buffers must be longword aligned. But since Ethernet + * headers are not a multiple of longwords in size this forces + * the data to non-longword aligned. Since IP requires the + * data to be longword aligned, we need to copy it after it has + * been DMA'ed in our memory. + * + * Since we have to copy it anyways, we might as well as allocate + * dedicated receive space for the input. This allows to use a + * small receive buffer size and more ring entries to be able to + * better keep with a flood of tiny Ethernet packets. + * + * The receive space MUST ALWAYS be a multiple of the page size. + * And the number of receive descriptors multiplied by the size + * of the receive buffers must equal the recevive space. This + * is so that we can manipulate the page tables so that even if a + * packet wraps around the end of the receive space, we can + * treat it as virtually contiguous. + * + * The above used to be true (the stupid restriction is still true) + * but we gone to directly DMA'ing into MBUFs (unless it's on an + * architecture which can't handle unaligned accesses) because with + * 100Mb/s cards the copying is just too much of a hit. + */ + +#define LMC_RXDESCS 48 +#define LMC_TXDESCS 128 +#define LMC_RXQ_TARGET 32 +#if LMC_RXQ_TARGET >= LMC_RXDESCS +#error LMC_RXQ_TARGET must be less than LMC_RXDESCS +#endif + +#define LMC_RX_BUFLEN ((MCLBYTES < 2048 ? MCLBYTES : 2048) - 16) + +/* + * The various controllers support. Technically the DE425 is just + * a 21040 on EISA. But since it remarkably difference from normal + * 21040s, we give it its own chip id. + */ + +typedef enum { + LMC_21140, LMC_21140A, + LMC_CHIPID_UNKNOWN +} lmc_chipid_t; + +#define LMC_BIT(b) (1L << ((int)(b))) + +typedef struct { + /* + * Transmit Statistics + */ + u_int32_t dot3StatsSingleCollisionFrames; + u_int32_t dot3StatsMultipleCollisionFrames; + u_int32_t dot3StatsSQETestErrors; + u_int32_t dot3StatsDeferredTransmissions; + u_int32_t dot3StatsLateCollisions; + u_int32_t dot3StatsExcessiveCollisions; + u_int32_t dot3StatsCarrierSenseErrors; + u_int32_t dot3StatsInternalMacTransmitErrors; + u_int32_t dot3StatsInternalTransmitUnderflows; /* not in rfc1650! */ + u_int32_t dot3StatsInternalTransmitBabbles; /* not in rfc1650! */ + /* + * Receive Statistics + */ + u_int32_t dot3StatsMissedFrames; /* not in rfc1650! */ + u_int32_t dot3StatsAlignmentErrors; + u_int32_t dot3StatsFCSErrors; + u_int32_t dot3StatsFrameTooLongs; + u_int32_t dot3StatsInternalMacReceiveErrors; +} lmc_dot3_stats_t; + +/* + * Now to important stuff. This is softc structure (where does softc + * come from??? No idea) for the tulip device. + * + */ +struct lmc___softc { +#if defined(__bsdi__) + struct device lmc_dev; /* base device */ + struct isadev lmc_id; /* ISA device */ + struct intrhand lmc_ih; /* intrrupt vectoring */ + struct atshutdown lmc_ats; /* shutdown hook */ + struct p2pcom lmc_p2pcom; /* point-to-point common stuff */ + +#define lmc_if lmc_p2pcom.p2p_if /* network-visible interface */ +#endif /* __bsdi__ */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) + struct device lmc_dev; /* base device */ + void *lmc_ih; /* intrrupt vectoring */ + void *lmc_ats; /* shutdown hook */ + bus_space_tag_t lmc_bustag; + bus_space_handle_t lmc_bushandle; /* CSR region handle */ + pci_chipset_tag_t lmc_pc; +#endif + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + struct sppp lmc_sppp; +#define lmc_if lmc_sppp.pp_if +#endif + + u_int8_t lmc_enaddr[6]; /* yes, a small hack... */ + lmc_regfile_t lmc_csrs; + volatile u_int32_t lmc_txtick; + volatile u_int32_t lmc_rxtick; + u_int32_t lmc_flags; + + u_int32_t lmc_features; /* static bits indicating features of chip */ + u_int32_t lmc_intrmask; /* our copy of csr_intr */ + u_int32_t lmc_cmdmode; /* our copy of csr_cmdmode */ + u_int32_t lmc_last_system_error : 3; /* last system error (only value is LMC_SYSTEMERROR is also set) */ + u_int32_t lmc_system_errors; /* number of system errors encountered */ + u_int32_t lmc_statusbits; /* status bits from CSR5 that may need to be printed */ + + u_int8_t lmc_revinfo; /* revision of chip */ + u_int8_t lmc_cardtype; /* LMC_CARDTYPE_HSSI or ..._DS3 */ + u_int32_t lmc_gpio_io; /* state of in/out settings */ + u_int32_t lmc_gpio; /* state of outputs */ + u_int8_t lmc_gp; + + lmc_chipid_t lmc_chipid; /* type of chip we are using */ + u_int32_t lmc_miireg16; + struct ifqueue lmc_txq; + struct ifqueue lmc_rxq; + lmc_dot3_stats_t lmc_dot3stats; + lmc_ringinfo_t lmc_rxinfo; + lmc_ringinfo_t lmc_txinfo; + u_int8_t lmc_rombuf[128]; + lmc_media_t *lmc_media; + lmc_ctl_t ictl; + +#if defined(__NetBSD__) || defined(__OpenBSD__) + struct device *lmc_pci_busno; /* needed for multiport boards */ +#else + u_int8_t lmc_pci_busno; /* needed for multiport boards */ +#endif + u_int8_t lmc_pci_devno; /* needed for multiport boards */ +#if defined(__FreeBSD__) + tulip_desc_t *lmc_rxdescs; + tulip_desc_t *lmc_txdescs; +#else + tulip_desc_t lmc_rxdescs[LMC_RXDESCS]; + tulip_desc_t lmc_txdescs[LMC_TXDESCS]; +#endif +#if defined(__NetBSD__) && NRND > 0 + rndsource_element_t lmc_rndsource; +#endif +}; + +/* + * lmc_flags + */ +#define LMC_IFUP 0x00000001 +#define LMC_00000002 0x00000002 +#define LMC_00000004 0x00000004 +#define LMC_00000008 0x00000008 +#define LMC_00000010 0x00000010 +#define LMC_MODEMOK 0x00000020 +#define LMC_00000040 0x00000040 +#define LMC_00000080 0x00000080 +#define LMC_RXACT 0x00000100 +#define LMC_INRESET 0x00000200 +#define LMC_NEEDRESET 0x00000400 +#define LMC_00000800 0x00000800 +#define LMC_00001000 0x00001000 +#define LMC_00002000 0x00002000 +#define LMC_WANTTXSTART 0x00004000 +#define LMC_NEWTXTHRESH 0x00008000 +#define LMC_NOAUTOSENSE 0x00010000 +#define LMC_PRINTLINKUP 0x00020000 +#define LMC_LINKUP 0x00040000 +#define LMC_RXBUFSLOW 0x00080000 +#define LMC_NOMESSAGES 0x00100000 +#define LMC_SYSTEMERROR 0x00200000 +#define LMC_TIMEOUTPENDING 0x00400000 +#define LMC_00800000 0x00800000 +#define LMC_01000000 0x01000000 +#define LMC_02000000 0x02000000 +#define LMC_RXIGNORE 0x04000000 +#define LMC_08000000 0x08000000 +#define LMC_10000000 0x10000000 +#define LMC_20000000 0x20000000 +#define LMC_40000000 0x40000000 +#define LMC_80000000 0x80000000 + +/* + * lmc_features + */ +#define LMC_HAVE_GPR 0x00000001 /* have gp register (140[A]) */ +#define LMC_HAVE_RXBADOVRFLW 0x00000002 /* RX corrupts on overflow */ +#define LMC_HAVE_POWERMGMT 0x00000004 /* Snooze/sleep modes */ +#define LMC_HAVE_MII 0x00000008 /* Some medium on MII */ +#define LMC_HAVE_SIANWAY 0x00000010 /* SIA does NWAY */ +#define LMC_HAVE_DUALSENSE 0x00000020 /* SIA senses both AUI & TP */ +#define LMC_HAVE_SIAGP 0x00000040 /* SIA has a GP port */ +#define LMC_HAVE_BROKEN_HASH 0x00000080 /* Broken Multicast Hash */ +#define LMC_HAVE_ISVSROM 0x00000100 /* uses ISV SROM Format */ +#define LMC_HAVE_BASEROM 0x00000200 /* Board ROM can be cloned */ +#define LMC_HAVE_SLAVEDROM 0x00000400 /* Board ROM cloned */ +#define LMC_HAVE_SLAVEDINTR 0x00000800 /* Board slaved interrupt */ +#define LMC_HAVE_SHAREDINTR 0x00001000 /* Board shares interrupts */ +#define LMC_HAVE_OKROM 0x00002000 /* ROM was recognized */ +#define LMC_HAVE_NOMEDIA 0x00004000 /* did not detect any media */ +#define LMC_HAVE_STOREFWD 0x00008000 /* have CMD_STOREFWD */ +#define LMC_HAVE_SIA100 0x00010000 /* has LS100 in SIA status */ + +static const char * const lmc_system_errors[] = { + "parity error", + "master abort", + "target abort", + "reserved #3", + "reserved #4", + "reserved #5", + "reserved #6", + "reserved #7", +}; + +static const char * const lmc_status_bits[] = { + NULL, + "transmit process stopped", + NULL, + "transmit jabber timeout", + + NULL, + "transmit underflow", + NULL, + "receive underflow", + + "receive process stopped", + "receive watchdog timeout", + NULL, + NULL, + + "link failure", + NULL, + NULL, +}; + +/* + * This driver supports a maximum of 32 tulip boards. + * This should be enough for the forseeable future. + */ +#define LMC_MAX_DEVICES 32 + +#if defined(__FreeBSD__) +typedef void ifnet_ret_t; +typedef int ioctl_cmd_t; +static lmc_softc_t *tulips[LMC_MAX_DEVICES]; +#if BSD >= 199506 +#define LMC_IFP_TO_SOFTC(ifp) ((lmc_softc_t *)((ifp)->if_softc)) +#if NBPFILTER > 0 +#define LMC_BPF_MTAP(sc, m) bpf_mtap(&(sc)->lmc_sppp.pp_if, m) +#define LMC_BPF_TAP(sc, p, l) bpf_tap(&(sc)->lmc_sppp.pp_if, p, l) +#define LMC_BPF_ATTACH(sc) bpfattach(&(sc)->lmc_sppp.pp_if, DLT_PPP, PPP_HEADER_LEN) +#endif +#define LMC_VOID_INTRFUNC +#define IFF_NOTRAILERS 0 +#define CLBYTES PAGE_SIZE +#define LMC_EADDR_FMT "%6D" +#define LMC_EADDR_ARGS(addr) addr, ":" +#else +extern int bootverbose; +#define LMC_IFP_TO_SOFTC(ifp) (LMC_UNIT_TO_SOFTC((ifp)->if_unit)) +#include <sys/devconf.h> +#define LMC_DEVCONF +#endif +#define LMC_UNIT_TO_SOFTC(unit) (tulips[unit]) +#define LMC_BURSTSIZE(unit) pci_max_burst_len +#define loudprintf if (bootverbose) printf +#endif + +#if defined(__bsdi__) +typedef int ifnet_ret_t; +typedef u_long ioctl_cmd_t; +extern struct cfdriver lmccd; +#define LMC_UNIT_TO_SOFTC(unit) ((lmc_softc_t *)lmccd.cd_devs[unit]) +#define LMC_IFP_TO_SOFTC(ifp) (LMC_UNIT_TO_SOFTC((ifp)->if_unit)) +#define loudprintf aprint_verbose +#define MCNT(x) (sizeof(x) / sizeof(struct ifmedia_entry)) +#define lmc_unit lmc_dev.dv_unit +#define lmc_name lmc_p2pcom.p2p_if.if_name +#define LMC_BPF_MTAP(sc, m) +#define LMC_BPF_TAP(sc, p, l) +#define LMC_BPF_ATTACH(sc) +#endif /* __bsdi__ */ + +#if defined(__NetBSD__) || defined(__OpenBSD__) +typedef void ifnet_ret_t; +typedef u_long ioctl_cmd_t; +extern struct cfattach lmc_ca; +extern struct cfdriver lmc_cd; +#define LMC_UNIT_TO_SOFTC(unit) ((lmc_softc_t *) lmc_cd.cd_devs[unit]) +#define LMC_IFP_TO_SOFTC(ifp) ((lmc_softc_t *)((ifp)->if_softc)) +#define lmc_unit lmc_dev.dv_unit +#define lmc_xname lmc_if.if_xname +#define LMC_RAISESPL() splnet() +#define LMC_RAISESOFTSPL() splsoftnet() +#define LMC_RESTORESPL(s) splx(s) +/* #define lmc_enaddr lmc_enaddr */ +#define loudprintf printf +#define LMC_PRINTF_FMT "%s" +#define LMC_PRINTF_ARGS sc->lmc_xname +#if defined(__alpha__) +/* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ +#define LMC_KVATOPHYS(sc, va) alpha_XXX_dmamap((vm_offset_t)(va)) +#endif +#endif /* __NetBSD__ */ + +#ifndef LMC_PRINTF_FMT +#define LMC_PRINTF_FMT "%s%d" +#endif +#ifndef LMC_PRINTF_ARGS +#define LMC_PRINTF_ARGS sc->lmc_name, sc->lmc_unit +#endif + +#ifndef LMC_BURSTSIZE +#define LMC_BURSTSIZE(unit) 3 +#endif + +#ifndef lmc_unit +#define lmc_unit lmc_sppp.pp_if.if_unit +#endif + +#ifndef lmc_name +#define lmc_name lmc_sppp.pp_if.if_name +#endif + +#if !defined(lmc_bpf) +#if defined(__NetBSD__) || defined(__FreeBSD__) | defined(__OpenBSD__) +#define lmc_bpf lmc_sppp.pp_if.if_bpf +#endif +#if defined(__bsdi__) +#define lmc_bpf lmc_if.if_bpf +#endif +#endif + +#if !defined(LMC_KVATOPHYS) +#define LMC_KVATOPHYS(sc, va) vtophys(va) +#endif + +#ifndef LMC_RAISESPL +#define LMC_RAISESPL() splimp() +#endif +#ifndef LMC_RAISESOFTSPL +#define LMC_RAISESOFTSPL() splnet() +#endif +#ifndef TULUP_RESTORESPL +#define LMC_RESTORESPL(s) splx(s) +#endif + +/* + * While I think FreeBSD's 2.2 change to the bpf is a nice simplification, + * it does add yet more conditional code to this driver. Sigh. + */ +#if !defined(LMC_BPF_MTAP) && NBPFILTER > 0 +#define LMC_BPF_MTAP(sc, m) bpf_mtap((sc)->lmc_bpf, m) +#define LMC_BPF_TAP(sc, p, l) bpf_tap((sc)->lmc_bpf, p, l) +#define LMC_BPF_ATTACH(sc) bpfattach(&(sc)->lmc_bpf, &(sc)->lmc_sppp.pp_if, DLT_PPP, PPP_HEADER_LEN) +#endif + +/* + * However, this change to FreeBSD I am much less enamored with. + */ +#if !defined(LMC_EADDR_FMT) +#define LMC_EADDR_FMT "%s" +#define LMC_EADDR_ARGS(addr) ether_sprintf(addr) +#endif + +#define LMC_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian */ +#define LMC_MAX_TXSEG 30 + +#define LMC_ADDREQUAL(a1, a2) \ + (((u_int16_t *)a1)[0] == ((u_int16_t *)a2)[0] \ + && ((u_int16_t *)a1)[1] == ((u_int16_t *)a2)[1] \ + && ((u_int16_t *)a1)[2] == ((u_int16_t *)a2)[2]) +#define LMC_ADDRBRDCST(a1) \ + (((u_int16_t *)a1)[0] == 0xFFFFU \ + && ((u_int16_t *)a1)[1] == 0xFFFFU \ + && ((u_int16_t *)a1)[2] == 0xFFFFU) + +typedef int lmc_spl_t; + +#endif /* !defined(_DEVAR_H) */ diff --git a/sys/net/if_sppp.h b/sys/net/if_sppp.h new file mode 100644 index 00000000000..c9bdc1525f4 --- /dev/null +++ b/sys/net/if_sppp.h @@ -0,0 +1,193 @@ +/* $NetBSD: if_sppp.h,v 1.2.2.1 1999/04/04 06:57:39 explorer Exp $ */ + +/* + * Defines for synchronous PPP/Cisco link level subroutines. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@cronyx.ru> + * + * Heavily revamped to conform to RFC 1661. + * Copyright (C) 1997, Joerg Wunsch. + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organizations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * From: Version 2.0, Fri Oct 6 20:39:21 MSK 1995 + * + * From: if_sppp.h,v 1.8 1997/10/11 11:25:20 joerg Exp + * + * From: Id: if_sppp.h,v 1.7 1998/12/01 20:20:19 hm Exp + */ + +#ifndef _NET_IF_HDLC_H_ +#define _NET_IF_HDLC_H_ 1 + +#define IDX_LCP 0 /* idx into state table */ + +struct slcp { + u_long opts; /* LCP options to send (bitfield) */ + u_long magic; /* local magic number */ + u_long mru; /* our max receive unit */ + u_long their_mru; /* their max receive unit */ + u_long protos; /* bitmask of protos that are started */ + u_char echoid; /* id of last keepalive echo request */ + /* restart max values, see RFC 1661 */ + int timeout; + int max_terminate; + int max_configure; + int max_failure; +}; + +#define IDX_IPCP 1 /* idx into state table */ + +struct sipcp { + u_long opts; /* IPCP options to send (bitfield) */ + u_int flags; +#define IPCP_HISADDR_SEEN 1 /* have seen his address already */ +#define IPCP_MYADDR_DYN 2 /* my address is dynamically assigned */ +#define IPCP_MYADDR_SEEN 4 /* have seen his address already */ +}; + +#define AUTHNAMELEN 32 +#define AUTHKEYLEN 16 + +struct sauth { + u_short proto; /* authentication protocol to use */ + u_short flags; +#define AUTHFLAG_NOCALLOUT 1 /* do not require authentication on */ + /* callouts */ +#define AUTHFLAG_NORECHALLENGE 2 /* do not re-challenge CHAP */ + u_char name[AUTHNAMELEN]; /* system identification name */ + u_char secret[AUTHKEYLEN]; /* secret password */ + u_char challenge[AUTHKEYLEN]; /* random challenge */ +}; + +#define IDX_PAP 2 +#define IDX_CHAP 3 + +#define IDX_COUNT (IDX_CHAP + 1) /* bump this when adding cp's! */ + +/* + * Don't change the order of this. Ordering the phases this way allows + * for a comparision of ``pp_phase >= PHASE_AUTHENTICATE'' in order to + * know whether LCP is up. + */ +enum ppp_phase { + PHASE_DEAD, PHASE_ESTABLISH, PHASE_TERMINATE, + PHASE_AUTHENTICATE, PHASE_NETWORK +}; + +struct sppp { + /* NB: pp_if _must_ be first */ + struct ifnet pp_if; /* network interface data */ + struct ifqueue pp_fastq; /* fast output queue */ + struct ifqueue pp_cpq; /* PPP control protocol queue */ + struct sppp *pp_next; /* next interface in keepalive list */ + u_int pp_flags; /* use Cisco protocol instead of PPP */ + u_short pp_alivecnt; /* keepalive packets counter */ + u_short pp_loopcnt; /* loopback detection counter */ + u_long pp_seq; /* local sequence number */ + u_long pp_rseq; /* remote sequence number */ + enum ppp_phase pp_phase; /* phase we're currently in */ + int state[IDX_COUNT]; /* state machine */ + u_char confid[IDX_COUNT]; /* id of last configuration request */ + int rst_counter[IDX_COUNT]; /* restart counter */ + int fail_counter[IDX_COUNT]; /* negotiation failure counter */ +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + struct callout_handle ch[IDX_COUNT]; /* per-proto and if callouts */ + struct callout_handle pap_my_to_ch; /* PAP needs one more... */ +#endif + struct slcp lcp; /* LCP params */ + struct sipcp ipcp; /* IPCP params */ + struct sauth myauth; /* auth params, i'm peer */ + struct sauth hisauth; /* auth params, i'm authenticator */ + /* + * These functions are filled in by sppp_attach(), and are + * expected to be used by the lower layer (hardware) drivers + * in order to communicate the (un)availability of the + * communication link. Lower layer drivers that are always + * ready to communicate (like hardware HDLC) can shortcut + * pp_up from pp_tls, and pp_down from pp_tlf. + */ + void (*pp_up)(struct sppp *sp); + void (*pp_down)(struct sppp *sp); + /* + * These functions need to be filled in by the lower layer + * (hardware) drivers if they request notification from the + * PPP layer whether the link is actually required. They + * correspond to the tls and tlf actions. + */ + void (*pp_tls)(struct sppp *sp); + void (*pp_tlf)(struct sppp *sp); + /* + * These (optional) functions may be filled by the hardware + * driver if any notification of established connections + * (currently: IPCP up) is desired (pp_con) or any internal + * state change of the interface state machine should be + * signaled for monitoring purposes (pp_chg). + */ + void (*pp_con)(struct sppp *sp); + void (*pp_chg)(struct sppp *sp, int new_state); +}; + +#define PP_KEEPALIVE 0x01 /* use keepalive protocol */ +#define PP_CISCO 0x02 /* use Cisco protocol instead of PPP */ + /* 0x04 was PP_TIMO */ +#define PP_CALLIN 0x08 /* we are being called */ +#define PP_NEEDAUTH 0x10 /* remote requested authentication */ + + +#define PP_MTU 1500 /* default/minimal MRU */ +#define PP_MAX_MRU 2048 /* maximal MRU we want to negotiate */ + +/* + * Definitions to pass struct sppp data down into the kernel using the + * SIOC[SG]IFGENERIC ioctl interface. + * + * In order to use this, create a struct spppreq, fill in the cmd + * field with SPPPIOGDEFS, and put the address of this structure into + * the ifr_data portion of a struct ifreq. Pass this struct to a + * SIOCGIFGENERIC ioctl. Then replace the cmd field by SPPPIOCDEFS, + * modify the defs field as desired, and pass the struct ifreq now + * to a SIOCSIFGENERIC ioctl. + */ + +#define SPPPIOGDEFS ((caddr_t)(('S' << 24) + (1 << 16) + sizeof(struct sppp))) +#define SPPPIOSDEFS ((caddr_t)(('S' << 24) + (2 << 16) + sizeof(struct sppp))) + +struct spppreq { + int cmd; + struct sppp defs; +}; + +#if (defined(__FreeBSD_version) && __FreeBSD_version < 300000) || \ + (defined(__FreeBSD__) && __FreeBSD__ < 3) || \ + defined(__NetBSD__) || defined (__OpenBSD__) +#define SIOCSIFGENERIC _IOW('i', 57, struct ifreq) /* generic IF set op */ +#define SIOCGIFGENERIC _IOWR('i', 58, struct ifreq) /* generic IF get op */ +#endif + +#if defined(KERNEL) || defined(_KERNEL) +void sppp_attach (struct ifnet *ifp); +void sppp_detach (struct ifnet *ifp); +void sppp_input (struct ifnet *ifp, struct mbuf *m); +#if defined(__OpenBSD__) +/* Workaround */ +void spppattach (struct ifnet *ifp); +#endif +#if (defined(__FreeBSD_version) && __FreeBSD_version >= 300003) +int sppp_ioctl(struct ifnet *ifp, int cmd, void *data); +#else +int sppp_ioctl(struct ifnet *ifp, u_long cmd, void *data); +#endif +struct mbuf *sppp_dequeue (struct ifnet *ifp); +struct mbuf *sppp_pick(struct ifnet *ifp); +int sppp_isempty (struct ifnet *ifp); +void sppp_flush (struct ifnet *ifp); +#endif + +#endif /* _NET_IF_HDLC_H_ */ diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c new file mode 100644 index 00000000000..97404927160 --- /dev/null +++ b/sys/net/if_spppsubr.c @@ -0,0 +1,4223 @@ +/* + * Synchronous PPP/Cisco link level subroutines. + * Keepalive protocol implemented in both Cisco and PPP modes. + * + * Copyright (C) 1994-1996 Cronyx Engineering Ltd. + * Author: Serge Vakulenko, <vak@cronyx.ru> + * + * Heavily revamped to conform to RFC 1661. + * Copyright (C) 1997, Joerg Wunsch. + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 2.6, Tue May 12 17:10:39 MSD 1998 + * + * $Id: if_spppsubr.c,v 1.1 1999/07/01 23:10:40 deraadt Exp $ + */ + +#include <sys/param.h> + +#if defined (__FreeBSD__) +#include "opt_inet.h" +#include "opt_ipx.h" +#endif + +#ifdef NetBSD1_3 +# if NetBSD1_3 > 6 +# include "opt_inet.h" +# include "opt_iso.h" +# endif +#endif + +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/sockio.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> + +#if defined (__OpenBSD__) +#include <sys/md5k.h> +#else +#include <sys/md5.h> +#endif + +#include <net/if.h> +#include <net/netisr.h> +#include <net/if_types.h> + +#if defined (__FreeBSD__) || defined(__OpenBSD_) || defined(__NetBSD__) +#include <machine/random.h> +#endif +#if defined (__NetBSD__) || defined (__OpenBSD__) +#include <machine/cpu.h> /* XXX for softnet */ +#endif +#include <machine/stdarg.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +# if defined (__FreeBSD__) || defined (__OpenBSD__) +# include <netinet/if_ether.h> +# else +# include <net/ethertypes.h> +# endif +#else +# error Huh? sppp without INET? +#endif + +#ifdef IPX +#include <netipx/ipx.h> +#include <netipx/ipx_if.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#ifdef ISO +#include <netiso/argo_debug.h> +#include <netiso/iso.h> +#include <netiso/iso_var.h> +#include <netiso/iso_snpac.h> +#endif + +#include <net/if_sppp.h> + +#if defined (__FreeBSD__) +# define UNTIMEOUT(fun, arg, handle) \ + untimeout(fun, arg, handle) +#else +# define UNTIMEOUT(fun, arg, handle) \ + untimeout(fun, arg) +#endif +#define MAXALIVECNT 3 /* max. alive packets */ + +/* + * Interface flags that can be set in an ifconfig command. + * + * Setting link0 will make the link passive, i.e. it will be marked + * as being administrative openable, but won't be opened to begin + * with. Incoming calls will be answered, or subsequent calls with + * -link1 will cause the administrative open of the LCP layer. + * + * Setting link1 will cause the link to auto-dial only as packets + * arrive to be sent. + * + * Setting IFF_DEBUG will syslog the option negotiation and state + * transitions at level kern.debug. Note: all logs consistently look + * like + * + * <if-name><unit>: <proto-name> <additional info...> + * + * with <if-name><unit> being something like "bppp0", and <proto-name> + * being one of "lcp", "ipcp", "cisco", "chap", "pap", etc. + */ + +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#define IFF_AUTO IFF_LINK1 /* auto-dial on output */ + +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_IP 0x0021 /* Internet Protocol */ +#define PPP_ISO 0x0023 /* ISO OSI Protocol */ +#define PPP_XNS 0x0025 /* Xerox NS Protocol */ +#define PPP_IPX 0x002b /* Novell IPX Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_CHAP 0xc223 /* Challenge-Handshake Auth Protocol */ +#define PPP_IPCP 0x8021 /* Internet Protocol Control Protocol */ + +#define CONF_REQ 1 /* PPP configure request */ +#define CONF_ACK 2 /* PPP configure acknowledge */ +#define CONF_NAK 3 /* PPP configure negative ack */ +#define CONF_REJ 4 /* PPP configure reject */ +#define TERM_REQ 5 /* PPP terminate request */ +#define TERM_ACK 6 /* PPP terminate acknowledge */ +#define CODE_REJ 7 /* PPP code reject */ +#define PROTO_REJ 8 /* PPP protocol reject */ +#define ECHO_REQ 9 /* PPP echo request */ +#define ECHO_REPLY 10 /* PPP echo reply */ +#define DISC_REQ 11 /* PPP discard request */ + +#define LCP_OPT_MRU 1 /* maximum receive unit */ +#define LCP_OPT_ASYNC_MAP 2 /* async control character map */ +#define LCP_OPT_AUTH_PROTO 3 /* authentication protocol */ +#define LCP_OPT_QUAL_PROTO 4 /* quality protocol */ +#define LCP_OPT_MAGIC 5 /* magic number */ +#define LCP_OPT_RESERVED 6 /* reserved */ +#define LCP_OPT_PROTO_COMP 7 /* protocol field compression */ +#define LCP_OPT_ADDR_COMP 8 /* address/control field compression */ + +#define IPCP_OPT_ADDRESSES 1 /* both IP addresses; deprecated */ +#define IPCP_OPT_COMPRESSION 2 /* IP compression protocol (VJ) */ +#define IPCP_OPT_ADDRESS 3 /* local IP address */ + +#define PAP_REQ 1 /* PAP name/password request */ +#define PAP_ACK 2 /* PAP acknowledge */ +#define PAP_NAK 3 /* PAP fail */ + +#define CHAP_CHALLENGE 1 /* CHAP challenge request */ +#define CHAP_RESPONSE 2 /* CHAP challenge response */ +#define CHAP_SUCCESS 3 /* CHAP response ok */ +#define CHAP_FAILURE 4 /* CHAP response failed */ + +#define CHAP_MD5 5 /* hash algorithm - MD5 */ + +#define CISCO_MULTICAST 0x8f /* Cisco multicast address */ +#define CISCO_UNICAST 0x0f /* Cisco unicast address */ +#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ +#define CISCO_ADDR_REQ 0 /* Cisco address request */ +#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ +#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ + +/* states are named and numbered according to RFC 1661 */ +#define STATE_INITIAL 0 +#define STATE_STARTING 1 +#define STATE_CLOSED 2 +#define STATE_STOPPED 3 +#define STATE_CLOSING 4 +#define STATE_STOPPING 5 +#define STATE_REQ_SENT 6 +#define STATE_ACK_RCVD 7 +#define STATE_ACK_SENT 8 +#define STATE_OPENED 9 + +struct ppp_header { + u_char address; + u_char control; + u_short protocol; +}; +#define PPP_HEADER_LEN sizeof (struct ppp_header) + +struct lcp_header { + u_char type; + u_char ident; + u_short len; +}; +#define LCP_HEADER_LEN sizeof (struct lcp_header) + +struct cisco_packet { + u_long type; + u_long par1; + u_long par2; + u_short rel; + u_short time0; + u_short time1; +}; +#define CISCO_PACKET_LEN 18 + +/* + * We follow the spelling and capitalization of RFC 1661 here, to make + * it easier comparing with the standard. Please refer to this RFC in + * case you can't make sense out of these abbreviation; it will also + * explain the semantics related to the various events and actions. + */ +struct cp { + u_short proto; /* PPP control protocol number */ + u_char protoidx; /* index into state table in struct sppp */ + u_char flags; +#define CP_LCP 0x01 /* this is the LCP */ +#define CP_AUTH 0x02 /* this is an authentication protocol */ +#define CP_NCP 0x04 /* this is a NCP */ +#define CP_QUAL 0x08 /* this is a quality reporting protocol */ + const char *name; /* name of this control protocol */ + /* event handlers */ + void (*Up)(struct sppp *sp); + void (*Down)(struct sppp *sp); + void (*Open)(struct sppp *sp); + void (*Close)(struct sppp *sp); + void (*TO)(void *sp); + int (*RCR)(struct sppp *sp, struct lcp_header *h, int len); + void (*RCN_rej)(struct sppp *sp, struct lcp_header *h, int len); + void (*RCN_nak)(struct sppp *sp, struct lcp_header *h, int len); + /* actions */ + void (*tlu)(struct sppp *sp); + void (*tld)(struct sppp *sp); + void (*tls)(struct sppp *sp); + void (*tlf)(struct sppp *sp); + void (*scr)(struct sppp *sp); +}; + +static struct sppp *spppq; +#if defined (__FreeBSD__) +static struct callout_handle keepalive_ch; +#endif + +#if defined (__FreeBSD__) +#define SPP_FMT "%s%d: " +#define SPP_ARGS(ifp) (ifp)->if_name, (ifp)->if_unit +#else +#define SPP_FMT "%s: " +#define SPP_ARGS(ifp) (ifp)->if_xname +#endif + +/* + * The following disgusting hack gets around the problem that IP TOS + * can't be set yet. We want to put "interactive" traffic on a high + * priority queue. To decide if traffic is interactive, we check that + * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control. + * + * XXX is this really still necessary? - joerg - + */ +static u_short interactive_ports[8] = { + 0, 513, 0, 0, + 0, 21, 0, 23, +}; +#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) + +/* almost every function needs these */ +#define STDDCL \ + struct ifnet *ifp = &sp->pp_if; \ + int debug = ifp->if_flags & IFF_DEBUG + +static int sppp_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt); + +static void sppp_cisco_send(struct sppp *sp, int type, long par1, long par2); +static void sppp_cisco_input(struct sppp *sp, struct mbuf *m); + +static void sppp_cp_input(const struct cp *cp, struct sppp *sp, + struct mbuf *m); +static void sppp_cp_send(struct sppp *sp, u_short proto, u_char type, + u_char ident, u_short len, void *data); +#ifdef notyet +static void sppp_cp_timeout(void *arg); +#endif +static void sppp_cp_change_state(const struct cp *cp, struct sppp *sp, + int newstate); +static void sppp_auth_send(const struct cp *cp, + struct sppp *sp, u_char type, u_char id, + ...); + +static void sppp_up_event(const struct cp *cp, struct sppp *sp); +static void sppp_down_event(const struct cp *cp, struct sppp *sp); +static void sppp_open_event(const struct cp *cp, struct sppp *sp); +static void sppp_close_event(const struct cp *cp, struct sppp *sp); +static void sppp_to_event(const struct cp *cp, struct sppp *sp); + +static void sppp_null(struct sppp *sp); + +static void sppp_lcp_init(struct sppp *sp); +static void sppp_lcp_up(struct sppp *sp); +static void sppp_lcp_down(struct sppp *sp); +static void sppp_lcp_open(struct sppp *sp); +static void sppp_lcp_close(struct sppp *sp); +static void sppp_lcp_TO(void *sp); +static int sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_lcp_tlu(struct sppp *sp); +static void sppp_lcp_tld(struct sppp *sp); +static void sppp_lcp_tls(struct sppp *sp); +static void sppp_lcp_tlf(struct sppp *sp); +static void sppp_lcp_scr(struct sppp *sp); +static void sppp_lcp_check_and_close(struct sppp *sp); +static int sppp_ncp_check(struct sppp *sp); + +static void sppp_ipcp_init(struct sppp *sp); +static void sppp_ipcp_up(struct sppp *sp); +static void sppp_ipcp_down(struct sppp *sp); +static void sppp_ipcp_open(struct sppp *sp); +static void sppp_ipcp_close(struct sppp *sp); +static void sppp_ipcp_TO(void *sp); +static int sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); +static void sppp_ipcp_tlu(struct sppp *sp); +static void sppp_ipcp_tld(struct sppp *sp); +static void sppp_ipcp_tls(struct sppp *sp); +static void sppp_ipcp_tlf(struct sppp *sp); +static void sppp_ipcp_scr(struct sppp *sp); + +static void sppp_pap_input(struct sppp *sp, struct mbuf *m); +static void sppp_pap_init(struct sppp *sp); +static void sppp_pap_open(struct sppp *sp); +static void sppp_pap_close(struct sppp *sp); +static void sppp_pap_TO(void *sp); +static void sppp_pap_my_TO(void *sp); +static void sppp_pap_tlu(struct sppp *sp); +static void sppp_pap_tld(struct sppp *sp); +static void sppp_pap_scr(struct sppp *sp); + +static void sppp_chap_input(struct sppp *sp, struct mbuf *m); +static void sppp_chap_init(struct sppp *sp); +static void sppp_chap_open(struct sppp *sp); +static void sppp_chap_close(struct sppp *sp); +static void sppp_chap_TO(void *sp); +static void sppp_chap_tlu(struct sppp *sp); +static void sppp_chap_tld(struct sppp *sp); +static void sppp_chap_scr(struct sppp *sp); + +static const char *sppp_auth_type_name(u_short proto, u_char type); +static const char *sppp_cp_type_name(u_char type); +static const char *sppp_dotted_quad(u_long addr); +static const char *sppp_ipcp_opt_name(u_char opt); +static const char *sppp_lcp_opt_name(u_char opt); +static const char *sppp_phase_name(enum ppp_phase phase); +static const char *sppp_proto_name(u_short proto); +static const char *sppp_state_name(int state); +static int sppp_params(struct sppp *sp, u_long cmd, void *data); +static int sppp_strnlen(u_char *p, int max); +static void sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, + u_long *srcmask); +static void sppp_keepalive(void *dummy); +static void sppp_phase_network(struct sppp *sp); +static void sppp_print_bytes(const u_char *p, u_short len); +static void sppp_print_string(const char *p, u_short len); +static void sppp_qflush(struct ifqueue *ifq); +static void sppp_set_ip_addr(struct sppp *sp, u_long src); + +/* our control protocol descriptors */ +static const struct cp lcp = { + PPP_LCP, IDX_LCP, CP_LCP, "lcp", + sppp_lcp_up, sppp_lcp_down, sppp_lcp_open, sppp_lcp_close, + sppp_lcp_TO, sppp_lcp_RCR, sppp_lcp_RCN_rej, sppp_lcp_RCN_nak, + sppp_lcp_tlu, sppp_lcp_tld, sppp_lcp_tls, sppp_lcp_tlf, + sppp_lcp_scr +}; + +static const struct cp ipcp = { + PPP_IPCP, IDX_IPCP, CP_NCP, "ipcp", + sppp_ipcp_up, sppp_ipcp_down, sppp_ipcp_open, sppp_ipcp_close, + sppp_ipcp_TO, sppp_ipcp_RCR, sppp_ipcp_RCN_rej, sppp_ipcp_RCN_nak, + sppp_ipcp_tlu, sppp_ipcp_tld, sppp_ipcp_tls, sppp_ipcp_tlf, + sppp_ipcp_scr +}; + +static const struct cp pap = { + PPP_PAP, IDX_PAP, CP_AUTH, "pap", + sppp_null, sppp_null, sppp_pap_open, sppp_pap_close, + sppp_pap_TO, 0, 0, 0, + sppp_pap_tlu, sppp_pap_tld, sppp_null, sppp_null, + sppp_pap_scr +}; + +static const struct cp chap = { + PPP_CHAP, IDX_CHAP, CP_AUTH, "chap", + sppp_null, sppp_null, sppp_chap_open, sppp_chap_close, + sppp_chap_TO, 0, 0, 0, + sppp_chap_tlu, sppp_chap_tld, sppp_null, sppp_null, + sppp_chap_scr +}; + +static const struct cp *cps[IDX_COUNT] = { + &lcp, /* IDX_LCP */ + &ipcp, /* IDX_IPCP */ + &pap, /* IDX_PAP */ + &chap, /* IDX_CHAP */ +}; + + +/* + * Exported functions, comprising our interface to the lower layer. + */ + +#if defined(__OpenBSD__) +/* Workaround */ +void +spppattach(struct ifnet *ifp) +{ +} +#endif + +/* + * Process the received packet. + */ +void +sppp_input(struct ifnet *ifp, struct mbuf *m) +{ + struct ppp_header *h; + struct ifqueue *inq = 0; + int s; + struct sppp *sp = (struct sppp *)ifp; + int debug = ifp->if_flags & IFF_DEBUG; + + if (ifp->if_flags & IFF_UP) + /* Count received bytes, add FCS and one flag */ + ifp->if_ibytes += m->m_pkthdr.len + 3; + + if (m->m_pkthdr.len <= PPP_HEADER_LEN) { + /* Too small packet, drop it. */ + if (debug) + log(LOG_DEBUG, + SPP_FMT "input packet is too small, %d bytes\n", + SPP_ARGS(ifp), m->m_pkthdr.len); + drop: + ++ifp->if_ierrors; + ++ifp->if_iqdrops; + m_freem (m); + return; + } + + /* Get PPP header. */ + h = mtod (m, struct ppp_header*); + m_adj (m, PPP_HEADER_LEN); + + switch (h->address) { + case PPP_ALLSTATIONS: + if (h->control != PPP_UI) + goto invalid; + if (sp->pp_flags & PP_CISCO) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "PPP packet in Cisco mode " + "<addr=0x%x ctrl=0x%x proto=0x%x>\n", + SPP_ARGS(ifp), + h->address, h->control, ntohs(h->protocol)); + goto drop; + } + switch (ntohs (h->protocol)) { + default: + if (sp->state[IDX_LCP] == STATE_OPENED) + sppp_cp_send (sp, PPP_LCP, PROTO_REJ, + ++sp->pp_seq, m->m_pkthdr.len + 2, + &h->protocol); + if (debug) + log(LOG_DEBUG, + SPP_FMT "invalid input protocol " + "<addr=0x%x ctrl=0x%x proto=0x%x>\n", + SPP_ARGS(ifp), + h->address, h->control, ntohs(h->protocol)); + ++ifp->if_noproto; + goto drop; + case PPP_LCP: + sppp_cp_input(&lcp, sp, m); + m_freem (m); + return; + case PPP_PAP: + if (sp->pp_phase >= PHASE_AUTHENTICATE) + sppp_pap_input(sp, m); + m_freem (m); + return; + case PPP_CHAP: + if (sp->pp_phase >= PHASE_AUTHENTICATE) + sppp_chap_input(sp, m); + m_freem (m); + return; +#ifdef INET + case PPP_IPCP: + if (sp->pp_phase == PHASE_NETWORK) + sppp_cp_input(&ipcp, sp, m); + m_freem (m); + return; + case PPP_IP: + if (sp->state[IDX_IPCP] == STATE_OPENED) { + schednetisr (NETISR_IP); + inq = &ipintrq; + } + break; +#endif +#ifdef IPX + case PPP_IPX: + /* IPX IPXCP not implemented yet */ + if (sp->pp_phase == PHASE_NETWORK) { + schednetisr (NETISR_IPX); + inq = &ipxintrq; + } + break; +#endif +#ifdef NS + case PPP_XNS: + /* XNS IDPCP not implemented yet */ + if (sp->pp_phase == PHASE_NETWORK) { + schednetisr (NETISR_NS); + inq = &nsintrq; + } + break; +#endif +#ifdef ISO + case PPP_ISO: + /* OSI NLCP not implemented yet */ + if (sp->pp_phase == PHASE_NETWORK) { + schednetisr (NETISR_ISO); + inq = &clnlintrq; + } + break; +#endif + } + break; + case CISCO_MULTICAST: + case CISCO_UNICAST: + /* Don't check the control field here (RFC 1547). */ + if (! (sp->pp_flags & PP_CISCO)) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "Cisco packet in PPP mode " + "<addr=0x%x ctrl=0x%x proto=0x%x>\n", + SPP_ARGS(ifp), + h->address, h->control, ntohs(h->protocol)); + goto drop; + } + switch (ntohs (h->protocol)) { + default: + ++ifp->if_noproto; + goto invalid; + case CISCO_KEEPALIVE: + sppp_cisco_input ((struct sppp*) ifp, m); + m_freem (m); + return; +#ifdef INET + case ETHERTYPE_IP: + schednetisr (NETISR_IP); + inq = &ipintrq; + break; +#endif +#ifdef IPX + case ETHERTYPE_IPX: + schednetisr (NETISR_IPX); + inq = &ipxintrq; + break; +#endif +#ifdef NS + case ETHERTYPE_NS: + schednetisr (NETISR_NS); + inq = &nsintrq; + break; +#endif + } + break; + default: /* Invalid PPP packet. */ + invalid: + if (debug) + log(LOG_DEBUG, + SPP_FMT "invalid input packet " + "<addr=0x%x ctrl=0x%x proto=0x%x>\n", + SPP_ARGS(ifp), + h->address, h->control, ntohs(h->protocol)); + goto drop; + } + + if (! (ifp->if_flags & IFF_UP) || ! inq) + goto drop; + + /* Check queue. */ + s = splimp(); + if (IF_QFULL (inq)) { + /* Queue overflow. */ + IF_DROP(inq); + splx(s); + if (debug) + log(LOG_DEBUG, SPP_FMT "protocol queue overflow\n", + SPP_ARGS(ifp)); + goto drop; + } + IF_ENQUEUE(inq, m); + splx(s); +} + +/* + * Enqueue transmit packet. + */ +static int +sppp_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt) +{ + struct sppp *sp = (struct sppp*) ifp; + struct ppp_header *h; + struct ifqueue *ifq; + int s, rv = 0; + + s = splimp(); + + if ((ifp->if_flags & IFF_UP) == 0 || + (ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == 0) { + m_freem (m); + splx (s); + return (ENETDOWN); + } + + if ((ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == IFF_AUTO) { + /* + * Interface is not yet running, but auto-dial. Need + * to start LCP for it. + */ + ifp->if_flags |= IFF_RUNNING; + splx(s); + lcp.Open(sp); + s = splimp(); + } + + ifq = &ifp->if_snd; +#ifdef INET + /* + * Put low delay, telnet, rlogin and ftp control packets + * in front of the queue. + */ + if (dst->sa_family == AF_INET) { + /* XXX Check mbuf length here? */ + struct ip *ip = mtod (m, struct ip*); + struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl); + + /* + * When using dynamic local IP address assignment by using + * 0.0.0.0 as a local address, the first TCP session will + * not connect because the local TCP checksum is computed + * using 0.0.0.0 which will later become our real IP address + * so the TCP checksum computed at the remote end will + * become invalid. So we + * - don't let packets with src ip addr 0 thru + * - we flag TCP packets with src ip 0 as an error + */ + + if(ip->ip_src.s_addr == INADDR_ANY) /* -hm */ + { + m_freem(m); + splx(s); + if(ip->ip_p == IPPROTO_TCP) + return(EADDRNOTAVAIL); + else + return(0); + } + + + if (! IF_QFULL (&sp->pp_fastq) && + ((ip->ip_tos & IPTOS_LOWDELAY) || + ((ip->ip_p == IPPROTO_TCP && + m->m_len >= sizeof (struct ip) + sizeof (struct tcphdr) && + (INTERACTIVE (ntohs (tcp->th_sport)))) || + INTERACTIVE (ntohs (tcp->th_dport))))) + ifq = &sp->pp_fastq; + } +#endif + + /* + * Prepend general data packet PPP header. For now, IP only. + */ + M_PREPEND (m, PPP_HEADER_LEN, M_DONTWAIT); + if (! m) { + if (ifp->if_flags & IFF_DEBUG) + log(LOG_DEBUG, SPP_FMT "no memory for transmit header\n", + SPP_ARGS(ifp)); + ++ifp->if_oerrors; + splx (s); + return (ENOBUFS); + } + /* + * May want to check size of packet + * (albeit due to the implementation it's always enough) + */ + h = mtod (m, struct ppp_header*); + if (sp->pp_flags & PP_CISCO) { + h->address = CISCO_UNICAST; /* unicast address */ + h->control = 0; + } else { + h->address = PPP_ALLSTATIONS; /* broadcast address */ + h->control = PPP_UI; /* Unnumbered Info */ + } + + switch (dst->sa_family) { +#ifdef INET + case AF_INET: /* Internet Protocol */ + if (sp->pp_flags & PP_CISCO) + h->protocol = htons (ETHERTYPE_IP); + else { + /* + * Don't choke with an ENETDOWN early. It's + * possible that we just started dialing out, + * so don't drop the packet immediately. If + * we notice that we run out of buffer space + * below, we will however remember that we are + * not ready to carry IP packets, and return + * ENETDOWN, as opposed to ENOBUFS. + */ + h->protocol = htons(PPP_IP); + if (sp->state[IDX_IPCP] != STATE_OPENED) + rv = ENETDOWN; + } + break; +#endif +#ifdef NS + case AF_NS: /* Xerox NS Protocol */ + h->protocol = htons ((sp->pp_flags & PP_CISCO) ? + ETHERTYPE_NS : PPP_XNS); + break; +#endif +#ifdef IPX + case AF_IPX: /* Novell IPX Protocol */ + h->protocol = htons ((sp->pp_flags & PP_CISCO) ? + ETHERTYPE_IPX : PPP_IPX); + break; +#endif +#ifdef ISO + case AF_ISO: /* ISO OSI Protocol */ + if (sp->pp_flags & PP_CISCO) + goto nosupport; + h->protocol = htons (PPP_ISO); + break; +nosupport: +#endif + default: + m_freem (m); + ++ifp->if_oerrors; + splx (s); + return (EAFNOSUPPORT); + } + + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + if (IF_QFULL (ifq)) { + IF_DROP (&ifp->if_snd); + m_freem (m); + ++ifp->if_oerrors; + splx (s); + return (rv? rv: ENOBUFS); + } + IF_ENQUEUE (ifq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + + /* + * Count output packets and bytes. + * The packet length includes header, FCS and 1 flag, + * according to RFC 1333. + */ + ifp->if_obytes += m->m_pkthdr.len + 3; + splx (s); + return (0); +} + +void +sppp_attach(struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*) ifp; + + /* Initialize keepalive handler. */ + if (! spppq) +#if defined (__FreeBSD__) + keepalive_ch = +#endif + timeout(sppp_keepalive, 0, hz * 10); + + /* Insert new entry into the keepalive list. */ + sp->pp_next = spppq; + spppq = sp; + + sp->pp_if.if_type = IFT_PPP; + sp->pp_if.if_output = sppp_output; + sp->pp_if.if_snd.ifq_maxlen = 50; + sp->pp_fastq.ifq_maxlen = 50; + sp->pp_cpq.ifq_maxlen = 50; + sp->pp_loopcnt = 0; + sp->pp_alivecnt = 0; + sp->pp_seq = 0; + sp->pp_rseq = 0; + sp->pp_phase = PHASE_DEAD; + sp->pp_up = lcp.Up; + sp->pp_down = lcp.Down; + + sppp_lcp_init(sp); + sppp_ipcp_init(sp); + sppp_pap_init(sp); + sppp_chap_init(sp); +} + +void +sppp_detach(struct ifnet *ifp) +{ + struct sppp **q, *p, *sp = (struct sppp*) ifp; + int i; + + /* Remove the entry from the keepalive list. */ + for (q = &spppq; (p = *q); q = &p->pp_next) + if (p == sp) { + *q = p->pp_next; + break; + } + + /* Stop keepalive handler. */ + if (! spppq) + UNTIMEOUT(sppp_keepalive, 0, keepalive_ch); + + for (i = 0; i < IDX_COUNT; i++) + UNTIMEOUT((cps[i])->TO, (void *)sp, sp->ch[i]); + UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); +} + +/* + * Flush the interface output queue. + */ +void +sppp_flush(struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*) ifp; + + sppp_qflush (&sp->pp_if.if_snd); + sppp_qflush (&sp->pp_fastq); + sppp_qflush (&sp->pp_cpq); +} + +/* + * Check if the output queue is empty. + */ +int +sppp_isempty(struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*) ifp; + int empty, s; + + s = splimp(); + empty = !sp->pp_fastq.ifq_head && !sp->pp_cpq.ifq_head && + !sp->pp_if.if_snd.ifq_head; + splx(s); + return (empty); +} + +/* + * Get next packet to send. + */ +struct mbuf * +sppp_dequeue(struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*) ifp; + struct mbuf *m; + int s; + + s = splimp(); + /* + * Process only the control protocol queue until we have at + * least one NCP open. + * + * Do always serve all three queues in Cisco mode. + */ + IF_DEQUEUE(&sp->pp_cpq, m); + if (m == NULL && + (sppp_ncp_check(sp) || (sp->pp_flags & PP_CISCO) != 0)) { + IF_DEQUEUE(&sp->pp_fastq, m); + if (m == NULL) + IF_DEQUEUE (&sp->pp_if.if_snd, m); + } + splx(s); + return m; +} + +/* + * Pick the next packet, do not remove it from the queue. + */ +struct mbuf * +sppp_pick(struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*)ifp; + struct mbuf *m; + int s; + + s= splimp (); + + m = sp->pp_cpq.ifq_head; + if (m == NULL && + (sp->pp_phase == PHASE_NETWORK || + (sp->pp_flags & PP_CISCO) != 0)) + if ((m = sp->pp_fastq.ifq_head) == NULL) + m = sp->pp_if.if_snd.ifq_head; + splx (s); + return (m); +} + +/* + * Process an ioctl request. Called on low priority level. + */ +int +sppp_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct ifreq *ifr = (struct ifreq*) data; + struct sppp *sp = (struct sppp*) ifp; + int s, rv, going_up, going_down; + + s = splimp(); + rv = 0; + switch (cmd) { + case SIOCAIFADDR: + case SIOCSIFDSTADDR: + break; + + case SIOCSIFADDR: + if_up(ifp); + /* fall through... */ + + case SIOCSIFFLAGS: + /* sanity */ + if (ifp->if_flags & IFF_PASSIVE) + ifp->if_flags &= ~IFF_AUTO; + + going_up = (ifp->if_flags & IFF_UP) && ! (ifp->if_flags & + (IFF_RUNNING | IFF_AUTO | IFF_PASSIVE)); + going_down = ! (ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING); + + if (going_up || going_down) { + if (! (sp->pp_flags & PP_CISCO)) + lcp.Close(sp); + else { + sppp_flush(ifp); + ifp->if_flags &= ~IFF_RUNNING; + } + } + if (going_up) { + /* neither auto-dial nor passive */ + ifp->if_flags |= IFF_RUNNING; + if (!(sp->pp_flags & PP_CISCO)) + lcp.Open(sp); + } + break; + +#ifdef SIOCSIFMTU +#ifndef ifr_mtu +#define ifr_mtu ifr_metric +#endif + case SIOCSIFMTU: + if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > sp->lcp.their_mru) + return (EINVAL); + ifp->if_mtu = ifr->ifr_mtu; + break; +#endif +#ifdef SLIOCSETMTU + case SLIOCSETMTU: + if (*(short*)data < 128 || *(short*)data > sp->lcp.their_mru) + return (EINVAL); + ifp->if_mtu = *(short*)data; + break; +#endif +#ifdef SIOCGIFMTU + case SIOCGIFMTU: + ifr->ifr_mtu = ifp->if_mtu; + break; +#endif +#ifdef SLIOCGETMTU + case SLIOCGETMTU: + *(short*)data = ifp->if_mtu; + break; +#endif + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + + case SIOCGIFGENERIC: + case SIOCSIFGENERIC: + rv = sppp_params(sp, cmd, data); + break; + + default: + rv = ENOTTY; + } + splx(s); + return rv; +} + + +/* + * Cisco framing implementation. + */ + +/* + * Handle incoming Cisco keepalive protocol packets. + */ +static void +sppp_cisco_input(struct sppp *sp, struct mbuf *m) +{ + STDDCL; + struct cisco_packet *h; + u_long me, mymask; + + if (m->m_pkthdr.len < CISCO_PACKET_LEN) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "cisco invalid packet length: %d bytes\n", + SPP_ARGS(ifp), m->m_pkthdr.len); + return; + } + h = mtod (m, struct cisco_packet*); + if (debug) + log(LOG_DEBUG, + SPP_FMT "cisco input: %d bytes " + "<0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n", + SPP_ARGS(ifp), m->m_pkthdr.len, + (u_long)ntohl (h->type), (u_long)h->par1, (u_long)h->par2, (u_int)h->rel, + (u_int)h->time0, (u_int)h->time1); + switch (ntohl (h->type)) { + default: + if (debug) + addlog(SPP_FMT "cisco unknown packet type: 0x%lx\n", + SPP_ARGS(ifp), (u_long)ntohl (h->type)); + break; + case CISCO_ADDR_REPLY: + /* Reply on address request, ignore */ + break; + case CISCO_KEEPALIVE_REQ: + sp->pp_alivecnt = 0; + sp->pp_rseq = ntohl (h->par1); + if (sp->pp_seq == sp->pp_rseq) { + /* Local and remote sequence numbers are equal. + * Probably, the line is in loopback mode. */ + if (sp->pp_loopcnt >= MAXALIVECNT) { + printf (SPP_FMT "loopback\n", + SPP_ARGS(ifp)); + sp->pp_loopcnt = 0; + if (ifp->if_flags & IFF_UP) { + if_down (ifp); + sppp_qflush (&sp->pp_cpq); + } + } + ++sp->pp_loopcnt; + + /* Generate new local sequence number */ +#if defined (__FreeBSD__) || defined (__NetBSD__) || defined(__OpenBSD__) + sp->pp_seq = random(); +#else + sp->pp_seq ^= time.tv_sec ^ time.tv_usec; +#endif + break; + } + sp->pp_loopcnt = 0; + if (! (ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING)) { + if_up(ifp); + printf (SPP_FMT "up\n", SPP_ARGS(ifp)); + } + break; + case CISCO_ADDR_REQ: + sppp_get_ip_addrs(sp, &me, 0, &mymask); + if (me != 0L) + sppp_cisco_send(sp, CISCO_ADDR_REPLY, me, mymask); + break; + } +} + +/* + * Send Cisco keepalive packet. + */ +static void +sppp_cisco_send(struct sppp *sp, int type, long par1, long par2) +{ + STDDCL; + struct ppp_header *h; + struct cisco_packet *ch; + struct mbuf *m; +#if defined (__FreeBSD__) + struct timeval tv; +#else + u_long t = (time.tv_sec - boottime.tv_sec) * 1000; +#endif + +#if defined (__FreeBSD__) + getmicrouptime(&tv); +#endif + + MGETHDR (m, M_DONTWAIT, MT_DATA); + if (! m) + return; + m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN; + m->m_pkthdr.rcvif = 0; + + h = mtod (m, struct ppp_header*); + h->address = CISCO_MULTICAST; + h->control = 0; + h->protocol = htons (CISCO_KEEPALIVE); + + ch = (struct cisco_packet*) (h + 1); + ch->type = htonl (type); + ch->par1 = htonl (par1); + ch->par2 = htonl (par2); + ch->rel = -1; + +#if defined (__FreeBSD__) + ch->time0 = htons ((u_short) (tv.tv_sec >> 16)); + ch->time1 = htons ((u_short) tv.tv_sec); +#else + ch->time0 = htons ((u_short) (t >> 16)); + ch->time1 = htons ((u_short) t); +#endif + + if (debug) + log(LOG_DEBUG, + SPP_FMT "cisco output: <0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n", + SPP_ARGS(ifp), (u_long)ntohl (ch->type), (u_long)ch->par1, + (u_long)ch->par2, (u_int)ch->rel, (u_int)ch->time0, (u_int)ch->time1); + + if (IF_QFULL (&sp->pp_cpq)) { + IF_DROP (&sp->pp_fastq); + IF_DROP (&ifp->if_snd); + m_freem (m); + } else + IF_ENQUEUE (&sp->pp_cpq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + ifp->if_obytes += m->m_pkthdr.len + 3; +} + +/* + * PPP protocol implementation. + */ + +/* + * Send PPP control protocol packet. + */ +static void +sppp_cp_send(struct sppp *sp, u_short proto, u_char type, + u_char ident, u_short len, void *data) +{ + STDDCL; + struct ppp_header *h; + struct lcp_header *lh; + struct mbuf *m; + + if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) + len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN; + MGETHDR (m, M_DONTWAIT, MT_DATA); + if (! m) + return; + m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len; + m->m_pkthdr.rcvif = 0; + + h = mtod (m, struct ppp_header*); + h->address = PPP_ALLSTATIONS; /* broadcast address */ + h->control = PPP_UI; /* Unnumbered Info */ + h->protocol = htons (proto); /* Link Control Protocol */ + + lh = (struct lcp_header*) (h + 1); + lh->type = type; + lh->ident = ident; + lh->len = htons (LCP_HEADER_LEN + len); + if (len) + bcopy (data, lh+1, len); + + if (debug) { + log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d", + SPP_ARGS(ifp), + sppp_proto_name(proto), + sppp_cp_type_name (lh->type), lh->ident, + ntohs (lh->len)); + if (len) + sppp_print_bytes ((u_char*) (lh+1), len); + addlog(">\n"); + } + if (IF_QFULL (&sp->pp_cpq)) { + IF_DROP (&sp->pp_fastq); + IF_DROP (&ifp->if_snd); + m_freem (m); + ++ifp->if_oerrors; + } else + IF_ENQUEUE (&sp->pp_cpq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + ifp->if_obytes += m->m_pkthdr.len + 3; +} + +/* + * Handle incoming PPP control protocol packets. + */ +static void +sppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m) +{ + STDDCL; + struct lcp_header *h; + int len = m->m_pkthdr.len; + int rv; + u_char *p; + + if (len < 4) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "%s invalid packet length: %d bytes\n", + SPP_ARGS(ifp), cp->name, len); + return; + } + h = mtod (m, struct lcp_header*); + if (debug) { + log(LOG_DEBUG, + SPP_FMT "%s input(%s): <%s id=0x%x len=%d", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx]), + sppp_cp_type_name (h->type), h->ident, ntohs (h->len)); + if (len > 4) + sppp_print_bytes ((u_char*) (h+1), len-4); + addlog(">\n"); + } + if (len > ntohs (h->len)) + len = ntohs (h->len); + p = (u_char *)(h + 1); + switch (h->type) { + case CONF_REQ: + if (len < 4) { + if (debug) + addlog(SPP_FMT "%s invalid conf-req length %d\n", + SPP_ARGS(ifp), cp->name, + len); + ++ifp->if_ierrors; + break; + } + /* handle states where RCR doesn't get a SCA/SCN */ + switch (sp->state[cp->protoidx]) { + case STATE_CLOSING: + case STATE_STOPPING: + return; + case STATE_CLOSED: + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, + 0, 0); + return; + } + rv = (cp->RCR)(sp, h, len); + switch (sp->state[cp->protoidx]) { + case STATE_OPENED: + sppp_cp_change_state(cp, sp, rv? + STATE_ACK_SENT: STATE_REQ_SENT); + (cp->tld)(sp); + (cp->scr)(sp); + break; + case STATE_ACK_SENT: + case STATE_REQ_SENT: + sppp_cp_change_state(cp, sp, rv? + STATE_ACK_SENT: STATE_REQ_SENT); + break; + case STATE_STOPPED: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + sppp_cp_change_state(cp, sp, rv? + STATE_ACK_SENT: STATE_REQ_SENT); + (cp->scr)(sp); + break; + case STATE_ACK_RCVD: + if (rv) { + sppp_cp_change_state(cp, sp, STATE_OPENED); + if (debug) + log(LOG_DEBUG, SPP_FMT "%s tlu\n", + SPP_ARGS(ifp), + cp->name); + (cp->tlu)(sp); + } else + sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); + break; + default: + /* printf(SPP_FMT "%s illegal %s in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); */ + ++ifp->if_ierrors; + } + break; + case CONF_ACK: + if (h->ident != sp->confid[cp->protoidx]) { + if (debug) + addlog(SPP_FMT "%s id mismatch 0x%x != 0x%x\n", + SPP_ARGS(ifp), cp->name, + h->ident, sp->confid[cp->protoidx]); + ++ifp->if_ierrors; + break; + } + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); + break; + case STATE_CLOSING: + case STATE_STOPPING: + break; + case STATE_REQ_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); + break; + case STATE_OPENED: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + (cp->tld)(sp); + (cp->scr)(sp); + break; + case STATE_ACK_RCVD: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + (cp->scr)(sp); + break; + case STATE_ACK_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + sppp_cp_change_state(cp, sp, STATE_OPENED); + if (debug) + log(LOG_DEBUG, SPP_FMT "%s tlu\n", + SPP_ARGS(ifp), cp->name); + (cp->tlu)(sp); + break; + default: + /* printf(SPP_FMT "%s illegal %s in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); */ + ++ifp->if_ierrors; + } + break; + case CONF_NAK: + case CONF_REJ: + if (h->ident != sp->confid[cp->protoidx]) { + if (debug) + addlog(SPP_FMT "%s id mismatch 0x%x != 0x%x\n", + SPP_ARGS(ifp), cp->name, + h->ident, sp->confid[cp->protoidx]); + ++ifp->if_ierrors; + break; + } + if (h->type == CONF_NAK) + (cp->RCN_nak)(sp, h, len); + else /* CONF_REJ */ + (cp->RCN_rej)(sp, h, len); + + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); + break; + case STATE_REQ_SENT: + case STATE_ACK_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + (cp->scr)(sp); + break; + case STATE_OPENED: + sppp_cp_change_state(cp, sp, STATE_ACK_SENT); + (cp->tld)(sp); + (cp->scr)(sp); + break; + case STATE_ACK_RCVD: + sppp_cp_change_state(cp, sp, STATE_ACK_SENT); + (cp->scr)(sp); + break; + case STATE_CLOSING: + case STATE_STOPPING: + break; + default: + /* printf(SPP_FMT "%s illegal %s in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); */ + ++ifp->if_ierrors; + } + break; + + case TERM_REQ: + switch (sp->state[cp->protoidx]) { + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + /* fall through */ + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_CLOSING: + case STATE_STOPPING: + case STATE_REQ_SENT: + sta: + /* Send Terminate-Ack packet. */ + if (debug) + log(LOG_DEBUG, SPP_FMT "%s send terminate-ack\n", + SPP_ARGS(ifp), cp->name); + sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); + break; + case STATE_OPENED: + sp->rst_counter[cp->protoidx] = 0; + sppp_cp_change_state(cp, sp, STATE_STOPPING); + (cp->tld)(sp); + goto sta; + break; + default: + /* printf(SPP_FMT "%s illegal %s in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); */ + ++ifp->if_ierrors; + } + break; + case TERM_ACK: + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_REQ_SENT: + case STATE_ACK_SENT: + break; + case STATE_CLOSING: + sppp_cp_change_state(cp, sp, STATE_CLOSED); + (cp->tlf)(sp); + break; + case STATE_STOPPING: + sppp_cp_change_state(cp, sp, STATE_STOPPED); + (cp->tlf)(sp); + break; + case STATE_ACK_RCVD: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; + case STATE_OPENED: + sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); + (cp->tld)(sp); + (cp->scr)(sp); + break; + default: + /* printf(SPP_FMT "%s illegal %s in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); */ + ++ifp->if_ierrors; + } + break; + case CODE_REJ: + case PROTO_REJ: + /* XXX catastrophic rejects (RXJ-) aren't handled yet. */ + log(LOG_INFO, + SPP_FMT "%s: ignoring RXJ (%s) for proto 0x%x, " + "danger will robinson\n", + SPP_ARGS(ifp), cp->name, + sppp_cp_type_name(h->type), ntohs(*((u_short *)p))); + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_REQ_SENT: + case STATE_ACK_SENT: + case STATE_CLOSING: + case STATE_STOPPING: + case STATE_OPENED: + break; + case STATE_ACK_RCVD: + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + break; + default: + /* printf(SPP_FMT "%s illegal %s in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_cp_type_name(h->type), + sppp_state_name(sp->state[cp->protoidx])); */ + ++ifp->if_ierrors; + } + break; + case DISC_REQ: + if (cp->proto != PPP_LCP) + goto illegal; + /* Discard the packet. */ + break; + case ECHO_REQ: + if (cp->proto != PPP_LCP) + goto illegal; + if (sp->state[cp->protoidx] != STATE_OPENED) { + if (debug) + addlog(SPP_FMT "lcp echo req but lcp closed\n", + SPP_ARGS(ifp)); + ++ifp->if_ierrors; + break; + } + if (len < 8) { + if (debug) + addlog(SPP_FMT "invalid lcp echo request " + "packet length: %d bytes\n", + SPP_ARGS(ifp), len); + break; + } + if (ntohl (*(long*)(h+1)) == sp->lcp.magic) { + /* Line loopback mode detected. */ + printf(SPP_FMT "loopback\n", SPP_ARGS(ifp)); + /* Shut down the PPP link. */ + lcp.Close(sp); + break; + } + *(long*)(h+1) = htonl (sp->lcp.magic); + if (debug) + addlog(SPP_FMT "got lcp echo req, sending echo rep\n", + SPP_ARGS(ifp)); + sppp_cp_send (sp, PPP_LCP, ECHO_REPLY, h->ident, len-4, h+1); + break; + case ECHO_REPLY: + if (cp->proto != PPP_LCP) + goto illegal; + if (h->ident != sp->lcp.echoid) { + ++ifp->if_ierrors; + break; + } + if (len < 8) { + if (debug) + addlog(SPP_FMT "lcp invalid echo reply " + "packet length: %d bytes\n", + SPP_ARGS(ifp), len); + break; + } + if (debug) + addlog(SPP_FMT "lcp got echo rep\n", + SPP_ARGS(ifp)); + if (ntohl (*(long*)(h+1)) != sp->lcp.magic) + sp->pp_alivecnt = 0; + break; + default: + /* Unknown packet type -- send Code-Reject packet. */ + illegal: + if (debug) + addlog(SPP_FMT "%s send code-rej for 0x%x\n", + SPP_ARGS(ifp), cp->name, h->type); + sppp_cp_send(sp, cp->proto, CODE_REJ, ++sp->pp_seq, + m->m_pkthdr.len, h); + ++ifp->if_ierrors; + } +} + + +/* + * The generic part of all Up/Down/Open/Close/TO event handlers. + * Basically, the state transition handling in the automaton. + */ +static void +sppp_up_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "%s up(%s)\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_INITIAL: + sppp_cp_change_state(cp, sp, STATE_CLOSED); + break; + case STATE_STARTING: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + (cp->scr)(sp); + break; + default: + /* printf(SPP_FMT "%s illegal up in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx])); */ + } +} + +static void +sppp_down_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "%s down(%s)\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_CLOSED: + case STATE_CLOSING: + sppp_cp_change_state(cp, sp, STATE_INITIAL); + break; + case STATE_STOPPED: + sppp_cp_change_state(cp, sp, STATE_STARTING); + (cp->tls)(sp); + break; + case STATE_STOPPING: + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sppp_cp_change_state(cp, sp, STATE_STARTING); + break; + case STATE_OPENED: + sppp_cp_change_state(cp, sp, STATE_STARTING); + (cp->tld)(sp); + break; + default: + /* printf(SPP_FMT "%s illegal down in state %s\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx])); */ + } +} + + +static void +sppp_open_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "%s open(%s)\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_INITIAL: + sppp_cp_change_state(cp, sp, STATE_STARTING); + (cp->tls)(sp); + break; + case STATE_STARTING: + break; + case STATE_CLOSED: + sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + (cp->scr)(sp); + break; + case STATE_STOPPED: + case STATE_STOPPING: + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + case STATE_OPENED: + break; + case STATE_CLOSING: + sppp_cp_change_state(cp, sp, STATE_STOPPING); + break; + } +} + + +static void +sppp_close_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "%s close(%s)\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx])); + + switch (sp->state[cp->protoidx]) { + case STATE_INITIAL: + case STATE_CLOSED: + case STATE_CLOSING: + break; + case STATE_STARTING: + sppp_cp_change_state(cp, sp, STATE_INITIAL); + (cp->tlf)(sp); + break; + case STATE_STOPPED: + sppp_cp_change_state(cp, sp, STATE_CLOSED); + break; + case STATE_STOPPING: + sppp_cp_change_state(cp, sp, STATE_CLOSING); + break; + case STATE_OPENED: + sppp_cp_change_state(cp, sp, STATE_CLOSING); + sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate; + sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0); + (cp->tld)(sp); + break; + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate; + sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0); + sppp_cp_change_state(cp, sp, STATE_CLOSING); + break; + } +} + +static void +sppp_increasing_timeout (const struct cp *cp, struct sppp *sp) +{ + int timo; + + timo = sp->lcp.max_configure - sp->rst_counter[cp->protoidx]; + if (timo < 1) + timo = 1; +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + sp->ch[cp->protoidx] = +#endif + timeout(cp->TO, (void *)sp, timo * sp->lcp.timeout); +} + +static void +sppp_to_event(const struct cp *cp, struct sppp *sp) +{ + STDDCL; + int s; + + s = splimp(); + if (debug) + log(LOG_DEBUG, SPP_FMT "%s TO(%s) rst_counter = %d\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx]), + sp->rst_counter[cp->protoidx]); + + if (--sp->rst_counter[cp->protoidx] < 0) + /* TO- event */ + switch (sp->state[cp->protoidx]) { + case STATE_CLOSING: + sppp_cp_change_state(cp, sp, STATE_CLOSED); + (cp->tlf)(sp); + break; + case STATE_STOPPING: + sppp_cp_change_state(cp, sp, STATE_STOPPED); + (cp->tlf)(sp); + break; + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sppp_cp_change_state(cp, sp, STATE_STOPPED); + (cp->tlf)(sp); + break; + } + else + /* TO+ event */ + switch (sp->state[cp->protoidx]) { + case STATE_CLOSING: + case STATE_STOPPING: + sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, + 0, 0); + sppp_increasing_timeout (cp, sp); + break; + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + /* sppp_cp_change_state() will restart the timer */ + sppp_cp_change_state(cp, sp, STATE_REQ_SENT); + (cp->scr)(sp); + break; + case STATE_ACK_SENT: + sppp_increasing_timeout (cp, sp); + (cp->scr)(sp); + break; + } + + splx(s); +} + +/* + * Change the state of a control protocol in the state automaton. + * Takes care of starting/stopping the restart timer. + */ +void +sppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate) +{ + STDDCL; + + if (debug && sp->state[cp->protoidx] != newstate) + log(LOG_DEBUG, SPP_FMT "%s %s->%s\n", + SPP_ARGS(ifp), cp->name, + sppp_state_name(sp->state[cp->protoidx]), + sppp_state_name(newstate)); + sp->state[cp->protoidx] = newstate; + + UNTIMEOUT(cp->TO, (void *)sp, sp->ch[cp->protoidx]); + switch (newstate) { + case STATE_INITIAL: + case STATE_STARTING: + case STATE_CLOSED: + case STATE_STOPPED: + case STATE_OPENED: + break; + case STATE_CLOSING: + case STATE_STOPPING: + case STATE_REQ_SENT: + case STATE_ACK_RCVD: + case STATE_ACK_SENT: + sppp_increasing_timeout (cp, sp); + break; + } +} +/* + *--------------------------------------------------------------------------* + * * + * The LCP implementation. * + * * + *--------------------------------------------------------------------------* + */ +static void +sppp_lcp_init(struct sppp *sp) +{ + sp->lcp.opts = (1 << LCP_OPT_MAGIC); + sp->lcp.magic = 0; + sp->state[IDX_LCP] = STATE_INITIAL; + sp->fail_counter[IDX_LCP] = 0; + sp->lcp.protos = 0; + sp->lcp.mru = sp->lcp.their_mru = PP_MTU; + + /* + * Initialize counters and timeout values. Note that we don't + * use the 3 seconds suggested in RFC 1661 since we are likely + * running on a fast link. XXX We should probably implement + * the exponential backoff option. Note that these values are + * relevant for all control protocols, not just LCP only. + */ + sp->lcp.timeout = 1 * hz; + sp->lcp.max_terminate = 2; + sp->lcp.max_configure = 10; + sp->lcp.max_failure = 10; +#if defined (__FreeBSD__) + callout_handle_init(&sp->ch[IDX_LCP]); +#endif +} + +static void +sppp_lcp_up(struct sppp *sp) +{ + STDDCL; + + sp->pp_alivecnt = 0; + sp->lcp.opts = (1 << LCP_OPT_MAGIC); + sp->lcp.magic = 0; + sp->lcp.protos = 0; + sp->lcp.mru = sp->lcp.their_mru = PP_MTU; + /* + * If this interface is passive or dial-on-demand, and we are + * still in Initial state, it means we've got an incoming + * call. Activate the interface. + */ + ifp->if_flags |= IFF_RUNNING; + if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) != 0) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "Up event", SPP_ARGS(ifp)); + if (sp->state[IDX_LCP] == STATE_INITIAL) { + if (debug) + addlog("(incoming call)\n"); + sp->pp_flags |= PP_CALLIN; + lcp.Open(sp); + } else if (debug) + addlog("\n"); + } + + sppp_up_event(&lcp, sp); +} + +static void +sppp_lcp_down(struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "Down event (carrier loss)\n", + SPP_ARGS(ifp)); + sppp_down_event(&lcp, sp); + + if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) != 0) + lcp.Close(sp); + sp->pp_flags &= ~PP_CALLIN; + ifp->if_flags &= ~IFF_RUNNING; + sppp_flush(ifp); +} + +static void +sppp_lcp_open(struct sppp *sp) +{ + /* + * If we are authenticator, negotiate LCP_AUTH + */ + if (sp->hisauth.proto != 0) + sp->lcp.opts |= (1 << LCP_OPT_AUTH_PROTO); + else + sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO); + sp->pp_flags &= ~PP_NEEDAUTH; + sppp_open_event(&lcp, sp); +} + +static void +sppp_lcp_close(struct sppp *sp) +{ + sppp_close_event(&lcp, sp); +} + +static void +sppp_lcp_TO(void *cookie) +{ + sppp_to_event(&lcp, (struct sppp *)cookie); +} + +/* + * Analyze a configure request. Return true if it was agreeable, and + * caused action sca, false if it has been rejected or nak'ed, and + * caused action scn. (The return value is used to make the state + * transition decision in the state automaton.) + */ +static int +sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len) +{ + STDDCL; + u_char *buf, *r, *p; + int origlen, rlen; + u_long nmagic; + u_short authproto; + + len -= 4; + origlen = len; + buf = r = malloc (len, M_TEMP, M_NOWAIT); + if (! buf) + return (0); + + if (debug) + log(LOG_DEBUG, SPP_FMT "lcp parse opts: ", + SPP_ARGS(ifp)); + + /* pass 1: check for things that need to be rejected */ + p = (void*) (h+1); + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog("%s ", sppp_lcp_opt_name(*p)); + switch (*p) { + case LCP_OPT_MAGIC: + /* Magic number. */ + if (len >= 6 && p[1] == 6) + continue; + if (debug) + addlog("[invalid] "); + break; + case LCP_OPT_ASYNC_MAP: + /* Async control character map. */ + if (len >= 6 && p[1] == 6) + continue; + if (debug) + addlog("[invalid] "); + break; + case LCP_OPT_MRU: + /* Maximum receive unit. */ + if (len >= 4 && p[1] == 4) + continue; + if (debug) + addlog("[invalid] "); + break; + case LCP_OPT_AUTH_PROTO: + if (len < 4) { + if (debug) + addlog("[invalid] "); + break; + } + authproto = (p[2] << 8) + p[3]; + if (authproto == PPP_CHAP && p[1] != 5) { + if (debug) + addlog("[invalid chap len] "); + break; + } + if (sp->myauth.proto == 0) { + /* we are not configured to do auth */ + if (debug) + addlog("[not configured] "); + break; + } + /* + * Remote want us to authenticate, remember this, + * so we stay in PHASE_AUTHENTICATE after LCP got + * up. + */ + sp->pp_flags |= PP_NEEDAUTH; + continue; + default: + /* Others not supported. */ + if (debug) + addlog("[rej] "); + break; + } + /* Add the option to rejected list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + if (rlen) { + if (debug) + addlog(" send conf-rej\n"); + sppp_cp_send (sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf); + return 0; + } else if (debug) + addlog("\n"); + + /* + * pass 2: check for option values that are unacceptable and + * thus require to be nak'ed. + */ + if (debug) + log(LOG_DEBUG, SPP_FMT "lcp parse opt values: ", + SPP_ARGS(ifp)); + + p = (void*) (h+1); + len = origlen; + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog("%s ", sppp_lcp_opt_name(*p)); + switch (*p) { + case LCP_OPT_MAGIC: + /* Magic number -- extract. */ + nmagic = (u_long)p[2] << 24 | + (u_long)p[3] << 16 | p[4] << 8 | p[5]; + if (nmagic != sp->lcp.magic) { + if (debug) + addlog("0x%lx ", nmagic); + continue; + } + if (debug) + addlog("[glitch] "); + ++sp->pp_loopcnt; + /* + * We negate our magic here, and NAK it. If + * we see it later in an NAK packet, we + * suggest a new one. + */ + nmagic = ~sp->lcp.magic; + /* Gonna NAK it. */ + p[2] = nmagic >> 24; + p[3] = nmagic >> 16; + p[4] = nmagic >> 8; + p[5] = nmagic; + break; + + case LCP_OPT_ASYNC_MAP: + /* Async control character map -- check to be zero. */ + if (! p[2] && ! p[3] && ! p[4] && ! p[5]) { + if (debug) + addlog("[empty] "); + continue; + } + if (debug) + addlog("[non-empty] "); + /* suggest a zero one */ + p[2] = p[3] = p[4] = p[5] = 0; + break; + + case LCP_OPT_MRU: + /* + * Maximum receive unit. Always agreeable, + * but ignored by now. + */ + sp->lcp.their_mru = p[2] * 256 + p[3]; + if (debug) + addlog("%lu ", sp->lcp.their_mru); + continue; + + case LCP_OPT_AUTH_PROTO: + authproto = (p[2] << 8) + p[3]; + if (sp->myauth.proto != authproto) { + /* not agreed, nak */ + if (debug) + addlog("[mine %s != his %s] ", + sppp_proto_name(sp->hisauth.proto), + sppp_proto_name(authproto)); + p[2] = sp->myauth.proto >> 8; + p[3] = sp->myauth.proto; + break; + } + if (authproto == PPP_CHAP && p[4] != CHAP_MD5) { + if (debug) + addlog("[chap not MD5] "); + p[4] = CHAP_MD5; + break; + } + continue; + } + /* Add the option to nak'ed list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + if (rlen) { + if (++sp->fail_counter[IDX_LCP] < sp->lcp.max_failure) { + if (debug) + addlog("send conf-nak\n"); + sppp_cp_send (sp, PPP_LCP, CONF_NAK, h->ident, rlen, buf); + return 0; + } + if (debug) + addlog("max_failure (%d) exceeded, closing\n", + sp->lcp.max_failure); + if (sp->pp_loopcnt >= MAXALIVECNT) + printf (SPP_FMT "loopback\n", SPP_ARGS(ifp)); + lcp.Close(sp); + sp->fail_counter[IDX_LCP] = 0; + sp->pp_loopcnt = 0; + return 0; + } else { + if (debug) + addlog("send conf-ack\n"); + sp->fail_counter[IDX_LCP] = 0; + sp->pp_loopcnt = 0; + sppp_cp_send (sp, PPP_LCP, CONF_ACK, + h->ident, origlen, h+1); + } + + free (buf, M_TEMP); + return (rlen == 0); +} + +/* + * Analyze the LCP Configure-Reject option list, and adjust our + * negotiation. + */ +static void +sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) +{ + STDDCL; + u_char *buf, *p; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, SPP_FMT "lcp rej opts: ", + SPP_ARGS(ifp)); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog("%s ", sppp_lcp_opt_name(*p)); + switch (*p) { + case LCP_OPT_MAGIC: + /* Magic number -- can't use it, use 0 */ + sp->lcp.opts &= ~(1 << LCP_OPT_MAGIC); + sp->lcp.magic = 0; + break; + case LCP_OPT_MRU: + /* + * Should not be rejected anyway, since we only + * negotiate a MRU if explicitly requested by + * peer. + */ + sp->lcp.opts &= ~(1 << LCP_OPT_MRU); + break; + case LCP_OPT_AUTH_PROTO: + /* + * Peer doesn't want to authenticate himself, + * deny unless this is a dialout call, and + * AUTHFLAG_NOCALLOUT is set. + */ + if ((sp->pp_flags & PP_CALLIN) == 0 && + (sp->hisauth.flags & AUTHFLAG_NOCALLOUT) != 0) { + if (debug) + addlog("[don't insist on auth " + "for callout]"); + sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO); + break; + } + if (debug) + addlog("[access denied]\n"); + lcp.Close(sp); + break; + } + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +/* + * Analyze the LCP Configure-NAK option list, and adjust our + * negotiation. + */ +static void +sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) +{ + STDDCL; + u_char *buf, *p; + u_long magic; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, SPP_FMT "lcp nak opts: ", + SPP_ARGS(ifp)); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog("%s ", sppp_lcp_opt_name(*p)); + switch (*p) { + case LCP_OPT_MAGIC: + /* Magic number -- renegotiate */ + if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) && + len >= 6 && p[1] == 6) { + magic = (u_long)p[2] << 24 | + (u_long)p[3] << 16 | p[4] << 8 | p[5]; + /* + * If the remote magic is our negated one, + * this looks like a loopback problem. + * Suggest a new magic to make sure. + */ + if (magic == ~sp->lcp.magic) { + if (debug) + addlog("magic glitch "); +#if defined (__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + sp->lcp.magic = random(); +#else + sp->lcp.magic = time.tv_sec + time.tv_usec; +#endif + } else { + sp->lcp.magic = magic; + if (debug) + addlog("%lu ", magic); + } + } + break; + case LCP_OPT_MRU: + /* + * Peer wants to advise us to negotiate an MRU. + * Agree on it if it's reasonable, or use + * default otherwise. + */ + if (len >= 4 && p[1] == 4) { + u_int mru = p[2] * 256 + p[3]; + if (debug) + addlog("%d ", mru); + if (mru < PP_MTU || mru > PP_MAX_MRU) + mru = PP_MTU; + sp->lcp.mru = mru; + sp->lcp.opts |= (1 << LCP_OPT_MRU); + } + break; + case LCP_OPT_AUTH_PROTO: + /* + * Peer doesn't like our authentication method, + * deny. + */ + if (debug) + addlog("[access denied]\n"); + lcp.Close(sp); + break; + } + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +static void +sppp_lcp_tlu(struct sppp *sp) +{ + struct ifnet *ifp = &sp->pp_if; + int i; + u_long mask; + + /* XXX ? */ + if (! (ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING)) { + /* Coming out of loopback mode. */ + if_up(ifp); + printf (SPP_FMT "up\n", SPP_ARGS(ifp)); + } + + for (i = 0; i < IDX_COUNT; i++) + if ((cps[i])->flags & CP_QUAL) + (cps[i])->Open(sp); + + if ((sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0 || + (sp->pp_flags & PP_NEEDAUTH) != 0) + sp->pp_phase = PHASE_AUTHENTICATE; + else + sp->pp_phase = PHASE_NETWORK; + + log(LOG_INFO, SPP_FMT "phase %s\n", SPP_ARGS(ifp), + sppp_phase_name(sp->pp_phase)); + + /* + * Open all authentication protocols. This is even required + * if we already proceeded to network phase, since it might be + * that remote wants us to authenticate, so we might have to + * send a PAP request. Undesired authentication protocols + * don't do anything when they get an Open event. + */ + for (i = 0; i < IDX_COUNT; i++) + if ((cps[i])->flags & CP_AUTH) + (cps[i])->Open(sp); + + if (sp->pp_phase == PHASE_NETWORK) { + /* Notify all NCPs. */ + for (i = 0; i < IDX_COUNT; i++) + if ((cps[i])->flags & CP_NCP) + (cps[i])->Open(sp); + } + + /* Send Up events to all started protos. */ + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) + (cps[i])->Up(sp); + + if (sp->pp_phase == PHASE_NETWORK) + /* if no NCP is starting, close down */ + sppp_lcp_check_and_close(sp); +} + +static void +sppp_lcp_tld(struct sppp *sp) +{ + struct ifnet *ifp = &sp->pp_if; + int i; + u_long mask; + + sp->pp_phase = PHASE_TERMINATE; + + log(LOG_INFO, SPP_FMT "phase %s\n", SPP_ARGS(ifp), + sppp_phase_name(sp->pp_phase)); + + /* + * Take upper layers down. We send the Down event first and + * the Close second to prevent the upper layers from sending + * ``a flurry of terminate-request packets'', as the RFC + * describes it. + */ + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) { + (cps[i])->Down(sp); + (cps[i])->Close(sp); + } +} + +static void +sppp_lcp_tls(struct sppp *sp) +{ + struct ifnet *ifp = &sp->pp_if; + + sp->pp_phase = PHASE_ESTABLISH; + + log(LOG_INFO, SPP_FMT "phase %s\n", SPP_ARGS(ifp), + sppp_phase_name(sp->pp_phase)); + + /* Notify lower layer if desired. */ + if (sp->pp_tls) + (sp->pp_tls)(sp); +} + +static void +sppp_lcp_tlf(struct sppp *sp) +{ + struct ifnet *ifp = &sp->pp_if; + + sp->pp_phase = PHASE_DEAD; + log(LOG_INFO, SPP_FMT "phase %s\n", SPP_ARGS(ifp), + sppp_phase_name(sp->pp_phase)); + + /* Notify lower layer if desired. */ + if (sp->pp_tlf) + (sp->pp_tlf)(sp); +} + +static void +sppp_lcp_scr(struct sppp *sp) +{ + char opt[6 /* magicnum */ + 4 /* mru */ + 5 /* chap */]; + int i = 0; + u_short authproto; + + if (sp->lcp.opts & (1 << LCP_OPT_MAGIC)) { + if (! sp->lcp.magic) +#if defined (__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + sp->lcp.magic = random(); +#else + sp->lcp.magic = time.tv_sec + time.tv_usec; +#endif + opt[i++] = LCP_OPT_MAGIC; + opt[i++] = 6; + opt[i++] = sp->lcp.magic >> 24; + opt[i++] = sp->lcp.magic >> 16; + opt[i++] = sp->lcp.magic >> 8; + opt[i++] = sp->lcp.magic; + } + + if (sp->lcp.opts & (1 << LCP_OPT_MRU)) { + opt[i++] = LCP_OPT_MRU; + opt[i++] = 4; + opt[i++] = sp->lcp.mru >> 8; + opt[i++] = sp->lcp.mru; + } + + if (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) { + authproto = sp->hisauth.proto; + opt[i++] = LCP_OPT_AUTH_PROTO; + opt[i++] = authproto == PPP_CHAP? 5: 4; + opt[i++] = authproto >> 8; + opt[i++] = authproto; + if (authproto == PPP_CHAP) + opt[i++] = CHAP_MD5; + } + + sp->confid[IDX_LCP] = ++sp->pp_seq; + sppp_cp_send (sp, PPP_LCP, CONF_REQ, sp->confid[IDX_LCP], i, &opt); +} + +/* + * Check the open NCPs, return true if at least one NCP is open. + */ +static int +sppp_ncp_check(struct sppp *sp) +{ + int i, mask; + + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + if (sp->lcp.protos & mask && (cps[i])->flags & CP_NCP) + return 1; + return 0; +} + +/* + * Re-check the open NCPs and see if we should terminate the link. + * Called by the NCPs during their tlf action handling. + */ +static void +sppp_lcp_check_and_close(struct sppp *sp) +{ + + if (sp->pp_phase < PHASE_NETWORK) + /* don't bother, we are already going down */ + return; + + if (sppp_ncp_check(sp)) + return; + + lcp.Close(sp); +} +/* + *--------------------------------------------------------------------------* + * * + * The IPCP implementation. * + * * + *--------------------------------------------------------------------------* + */ + +static void +sppp_ipcp_init(struct sppp *sp) +{ + sp->ipcp.opts = 0; + sp->ipcp.flags = 0; + sp->state[IDX_IPCP] = STATE_INITIAL; + sp->fail_counter[IDX_IPCP] = 0; +#if defined (__FreeBSD__) + callout_handle_init(&sp->ch[IDX_IPCP]); +#endif +} + +static void +sppp_ipcp_up(struct sppp *sp) +{ + sppp_up_event(&ipcp, sp); +} + +static void +sppp_ipcp_down(struct sppp *sp) +{ + sppp_down_event(&ipcp, sp); +} + +static void +sppp_ipcp_open(struct sppp *sp) +{ + STDDCL; + u_long myaddr, hisaddr; + + sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0); + /* + * If we don't have his address, this probably means our + * interface doesn't want to talk IP at all. (This could + * be the case if somebody wants to speak only IPX, for + * example.) Don't open IPCP in this case. + */ + if (hisaddr == 0L) { + /* XXX this message should go away */ + if (debug) + log(LOG_DEBUG, SPP_FMT "ipcp_open(): no IP interface\n", + SPP_ARGS(ifp)); + return; + } + + if (myaddr == 0L) { + /* + * I don't have an assigned address, so i need to + * negotiate my address. + */ + sp->ipcp.flags |= IPCP_MYADDR_DYN; + sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS); + } + sppp_open_event(&ipcp, sp); +} + +static void +sppp_ipcp_close(struct sppp *sp) +{ + sppp_close_event(&ipcp, sp); + if (sp->ipcp.flags & IPCP_MYADDR_DYN) + /* + * My address was dynamic, clear it again. + */ + sppp_set_ip_addr(sp, 0L); +} + +static void +sppp_ipcp_TO(void *cookie) +{ + sppp_to_event(&ipcp, (struct sppp *)cookie); +} + +/* + * Analyze a configure request. Return true if it was agreeable, and + * caused action sca, false if it has been rejected or nak'ed, and + * caused action scn. (The return value is used to make the state + * transition decision in the state automaton.) + */ +static int +sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *r, *p; + struct ifnet *ifp = &sp->pp_if; + int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG; + u_long hisaddr, desiredaddr; + + len -= 4; + origlen = len; + /* + * Make sure to allocate a buf that can at least hold a + * conf-nak with an `address' option. We might need it below. + */ + buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT); + if (! buf) + return (0); + + /* pass 1: see if we can recognize them */ + if (debug) + log(LOG_DEBUG, SPP_FMT "ipcp parse opts: ", + SPP_ARGS(ifp)); + p = (void*) (h+1); + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog("%s ", sppp_ipcp_opt_name(*p)); + switch (*p) { +#ifdef notyet + case IPCP_OPT_COMPRESSION: + if (len >= 6 && p[1] >= 6) { + /* correctly formed compress option */ + continue; + } + if (debug) + addlog("[invalid] "); + break; +#endif + case IPCP_OPT_ADDRESS: + if (len >= 6 && p[1] == 6) { + /* correctly formed address option */ + continue; + } + if (debug) + addlog("[invalid] "); + break; + default: + /* Others not supported. */ + if (debug) + addlog("[rej] "); + break; + } + /* Add the option to rejected list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + if (rlen) { + if (debug) + addlog(" send conf-rej\n"); + sppp_cp_send (sp, PPP_IPCP, CONF_REJ, h->ident, rlen, buf); + return 0; + } else if (debug) + addlog("\n"); + + /* pass 2: parse option values */ + sppp_get_ip_addrs(sp, 0, &hisaddr, 0); + if (debug) + log(LOG_DEBUG, SPP_FMT "ipcp parse opt values: ", + SPP_ARGS(ifp)); + p = (void*) (h+1); + len = origlen; + for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { + if (debug) + addlog(" %s ", sppp_ipcp_opt_name(*p)); + switch (*p) { +#ifdef notyet + case IPCP_OPT_COMPRESSION: + continue; +#endif + case IPCP_OPT_ADDRESS: + desiredaddr = p[2] << 24 | p[3] << 16 | + p[4] << 8 | p[5]; + if (desiredaddr == hisaddr || + (hisaddr == 1 && desiredaddr != 0)) { + /* + * Peer's address is same as our value, + * or we have set it to 0.0.0.1 to + * indicate that we do not really care, + * this is agreeable. Gonna conf-ack + * it. + */ + if (debug) + addlog("%s [ack] ", + sppp_dotted_quad(desiredaddr)); + /* record that we've seen it already */ + sp->ipcp.flags |= IPCP_HISADDR_SEEN; + continue; + } + /* + * The address wasn't agreeable. This is either + * he sent us 0.0.0.0, asking to assign him an + * address, or he send us another address not + * matching our value. Either case, we gonna + * conf-nak it with our value. + */ + if (debug) { + if (desiredaddr == 0) + addlog("[addr requested] "); + else + addlog("%s [not agreed] ", + sppp_dotted_quad(desiredaddr)); + + p[2] = hisaddr >> 24; + p[3] = hisaddr >> 16; + p[4] = hisaddr >> 8; + p[5] = hisaddr; + } + break; + } + /* Add the option to nak'ed list. */ + bcopy (p, r, p[1]); + r += p[1]; + rlen += p[1]; + } + + /* + * If we are about to conf-ack the request, but haven't seen + * his address so far, gonna conf-nak it instead, with the + * `address' option present and our idea of his address being + * filled in there, to request negotiation of both addresses. + * + * XXX This can result in an endless req - nak loop if peer + * doesn't want to send us his address. Q: What should we do + * about it? XXX A: implement the max-failure counter. + */ + if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN)) { + buf[0] = IPCP_OPT_ADDRESS; + buf[1] = 6; + buf[2] = hisaddr >> 24; + buf[3] = hisaddr >> 16; + buf[4] = hisaddr >> 8; + buf[5] = hisaddr; + rlen = 6; + if (debug) + addlog("still need hisaddr "); + } + + if (rlen) { + if (debug) + addlog(" send conf-nak\n"); + sppp_cp_send (sp, PPP_IPCP, CONF_NAK, h->ident, rlen, buf); + } else { + if (debug) + addlog(" send conf-ack\n"); + sppp_cp_send (sp, PPP_IPCP, CONF_ACK, + h->ident, origlen, h+1); + } + + free (buf, M_TEMP); + return (rlen == 0); +} + +/* + * Analyze the IPCP Configure-Reject option list, and adjust our + * negotiation. + */ +static void +sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *p; + struct ifnet *ifp = &sp->pp_if; + int debug = ifp->if_flags & IFF_DEBUG; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, SPP_FMT "ipcp rej opts: ", + SPP_ARGS(ifp)); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog("%s ", sppp_ipcp_opt_name(*p)); + switch (*p) { + case IPCP_OPT_ADDRESS: + /* + * Peer doesn't grok address option. This is + * bad. XXX Should we better give up here? + */ + sp->ipcp.opts &= ~(1 << IPCP_OPT_ADDRESS); + break; +#ifdef notyet + case IPCP_OPT_COMPRESS: + sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESS); + break; +#endif + } + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +/* + * Analyze the IPCP Configure-NAK option list, and adjust our + * negotiation. + */ +static void +sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) +{ + u_char *buf, *p; + struct ifnet *ifp = &sp->pp_if; + int debug = ifp->if_flags & IFF_DEBUG; + u_long wantaddr; + + len -= 4; + buf = malloc (len, M_TEMP, M_NOWAIT); + if (!buf) + return; + + if (debug) + log(LOG_DEBUG, SPP_FMT "ipcp nak opts: ", + SPP_ARGS(ifp)); + + p = (void*) (h+1); + for (; len > 1 && p[1]; len -= p[1], p += p[1]) { + if (debug) + addlog("%s ", sppp_ipcp_opt_name(*p)); + switch (*p) { + case IPCP_OPT_ADDRESS: + /* + * Peer doesn't like our local IP address. See + * if we can do something for him. We'll drop + * him our address then. + */ + if (len >= 6 && p[1] == 6) { + wantaddr = p[2] << 24 | p[3] << 16 | + p[4] << 8 | p[5]; + sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS); + if (debug) + addlog("[wantaddr %s] ", + sppp_dotted_quad(wantaddr)); + /* + * When doing dynamic address assignment, + * we accept his offer. Otherwise, we + * ignore it and thus continue to negotiate + * our already existing value. + */ + if (sp->ipcp.flags & IPCP_MYADDR_DYN) { + sppp_set_ip_addr(sp, wantaddr); + if (debug) + addlog("[agree] "); + } + } + break; +#ifdef notyet + case IPCP_OPT_COMPRESS: + /* + * Peer wants different compression parameters. + */ + break; +#endif + } + } + if (debug) + addlog("\n"); + free (buf, M_TEMP); + return; +} + +static void +sppp_ipcp_tlu(struct sppp *sp) +{ +} + +static void +sppp_ipcp_tld(struct sppp *sp) +{ +} + +static void +sppp_ipcp_tls(struct sppp *sp) +{ + /* indicate to LCP that it must stay alive */ + sp->lcp.protos |= (1 << IDX_IPCP); +} + +static void +sppp_ipcp_tlf(struct sppp *sp) +{ + /* we no longer need LCP */ + sp->lcp.protos &= ~(1 << IDX_IPCP); + sppp_lcp_check_and_close(sp); +} + +static void +sppp_ipcp_scr(struct sppp *sp) +{ + char opt[6 /* compression */ + 6 /* address */]; + u_long ouraddr; + int i = 0; + +#ifdef notyet + if (sp->ipcp.opts & (1 << IPCP_OPT_COMPRESSION)) { + opt[i++] = IPCP_OPT_COMPRESSION; + opt[i++] = 6; + opt[i++] = 0; /* VJ header compression */ + opt[i++] = 0x2d; /* VJ header compression */ + opt[i++] = max_slot_id; + opt[i++] = comp_slot_id; + } +#endif + + if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) { + sppp_get_ip_addrs(sp, &ouraddr, 0, 0); + opt[i++] = IPCP_OPT_ADDRESS; + opt[i++] = 6; + opt[i++] = ouraddr >> 24; + opt[i++] = ouraddr >> 16; + opt[i++] = ouraddr >> 8; + opt[i++] = ouraddr; + } + + sp->confid[IDX_IPCP] = ++sp->pp_seq; + sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt); +} + + +/* + *--------------------------------------------------------------------------* + * * + * The CHAP implementation. * + * * + *--------------------------------------------------------------------------* + */ + +/* + * The authentication protocols don't employ a full-fledged state machine as + * the control protocols do, since they do have Open and Close events, but + * not Up and Down, nor are they explicitly terminated. Also, use of the + * authentication protocols may be different in both directions (this makes + * sense, think of a machine that never accepts incoming calls but only + * calls out, it doesn't require the called party to authenticate itself). + * + * Our state machine for the local authentication protocol (we are requesting + * the peer to authenticate) looks like: + * + * RCA- + * +--------------------------------------------+ + * V scn,tld| + * +--------+ Close +---------+ RCA+ + * | |<----------------------------------| |------+ + * +--->| Closed | TO* | Opened | sca | + * | | |-----+ +-------| |<-----+ + * | +--------+ irc | | +---------+ + * | ^ | | ^ + * | | | | | + * | | | | | + * | TO-| | | | + * | |tld TO+ V | | + * | | +------->+ | | + * | | | | | | + * | +--------+ V | | + * | | |<----+<--------------------+ | + * | | Req- | scr | + * | | Sent | | + * | | | | + * | +--------+ | + * | RCA- | | RCA+ | + * +------+ +------------------------------------------+ + * scn,tld sca,irc,ict,tlu + * + * + * with: + * + * Open: LCP reached authentication phase + * Close: LCP reached terminate phase + * + * RCA+: received reply (pap-req, chap-response), acceptable + * RCN: received reply (pap-req, chap-response), not acceptable + * TO+: timeout with restart counter >= 0 + * TO-: timeout with restart counter < 0 + * TO*: reschedule timeout for CHAP + * + * scr: send request packet (none for PAP, chap-challenge) + * sca: send ack packet (pap-ack, chap-success) + * scn: send nak packet (pap-nak, chap-failure) + * ict: initialize re-challenge timer (CHAP only) + * + * tlu: this-layer-up, LCP reaches network phase + * tld: this-layer-down, LCP enters terminate phase + * + * Note that in CHAP mode, after sending a new challenge, while the state + * automaton falls back into Req-Sent state, it doesn't signal a tld + * event to LCP, so LCP remains in network phase. Only after not getting + * any response (or after getting an unacceptable response), CHAP closes, + * causing LCP to enter terminate phase. + * + * With PAP, there is no initial request that can be sent. The peer is + * expected to send one based on the successful negotiation of PAP as + * the authentication protocol during the LCP option negotiation. + * + * Incoming authentication protocol requests (remote requests + * authentication, we are peer) don't employ a state machine at all, + * they are simply answered. Some peers [Ascend P50 firmware rev + * 4.50] react allergically when sending IPCP requests while they are + * still in authentication phase (thereby violating the standard that + * demands that these NCP packets are to be discarded), so we keep + * track of the peer demanding us to authenticate, and only proceed to + * phase network once we've seen a positive acknowledge for the + * authentication. + */ + +/* + * Handle incoming CHAP packets. + */ +void +sppp_chap_input(struct sppp *sp, struct mbuf *m) +{ + STDDCL; + struct lcp_header *h; + int len, x; + u_char *value, *name, digest[AUTHKEYLEN], dsize; + int value_len, name_len; + MD5_CTX ctx; + + len = m->m_pkthdr.len; + if (len < 4) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "chap invalid packet length: %d bytes\n", + SPP_ARGS(ifp), len); + return; + } + h = mtod (m, struct lcp_header*); + if (len > ntohs (h->len)) + len = ntohs (h->len); + + switch (h->type) { + /* challenge, failure and success are his authproto */ + case CHAP_CHALLENGE: + value = 1 + (u_char*)(h+1); + value_len = value[-1]; + name = value + value_len; + name_len = len - value_len - 5; + if (name_len < 0) { + if (debug) { + log(LOG_DEBUG, + SPP_FMT "chap corrupted challenge " + "<%s id=0x%x len=%d", + SPP_ARGS(ifp), + sppp_auth_type_name(PPP_CHAP, h->type), + h->ident, ntohs(h->len)); + if (len > 4) + sppp_print_bytes((u_char*) (h+1), len-4); + addlog(">\n"); + } + break; + } + + if (debug) { + log(LOG_DEBUG, + SPP_FMT "chap input <%s id=0x%x len=%d name=", + SPP_ARGS(ifp), + sppp_auth_type_name(PPP_CHAP, h->type), h->ident, + ntohs(h->len)); + sppp_print_string((char*) name, name_len); + addlog(" value-size=%d value=", value_len); + sppp_print_bytes(value, value_len); + addlog(">\n"); + } + + /* Compute reply value. */ + MD5Init(&ctx); + MD5Update(&ctx, &h->ident, 1); + MD5Update(&ctx, sp->myauth.secret, + sppp_strnlen(sp->myauth.secret, AUTHKEYLEN)); + MD5Update(&ctx, value, value_len); + MD5Final(digest, &ctx); + dsize = sizeof digest; + + sppp_auth_send(&chap, sp, CHAP_RESPONSE, h->ident, + sizeof dsize, (const char *)&dsize, + sizeof digest, digest, + (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN), + sp->myauth.name, + 0); + break; + + case CHAP_SUCCESS: + if (debug) { + log(LOG_DEBUG, SPP_FMT "chap success", + SPP_ARGS(ifp)); + if (len > 4) { + addlog(": "); + sppp_print_string((char*)(h + 1), len - 4); + } + addlog("\n"); + } + x = splimp(); + sp->pp_flags &= ~PP_NEEDAUTH; + if (sp->myauth.proto == PPP_CHAP && + (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) && + (sp->lcp.protos & (1 << IDX_CHAP)) == 0) { + /* + * We are authenticator for CHAP but didn't + * complete yet. Leave it to tlu to proceed + * to network phase. + */ + splx(x); + break; + } + splx(x); + sppp_phase_network(sp); + break; + + case CHAP_FAILURE: + if (debug) { + log(LOG_INFO, SPP_FMT "chap failure", + SPP_ARGS(ifp)); + if (len > 4) { + addlog(": "); + sppp_print_string((char*)(h + 1), len - 4); + } + addlog("\n"); + } else + log(LOG_INFO, SPP_FMT "chap failure\n", + SPP_ARGS(ifp)); + /* await LCP shutdown by authenticator */ + break; + + /* response is my authproto */ + case CHAP_RESPONSE: + value = 1 + (u_char*)(h+1); + value_len = value[-1]; + name = value + value_len; + name_len = len - value_len - 5; + if (name_len < 0) { + if (debug) { + log(LOG_DEBUG, + SPP_FMT "chap corrupted response " + "<%s id=0x%x len=%d", + SPP_ARGS(ifp), + sppp_auth_type_name(PPP_CHAP, h->type), + h->ident, ntohs(h->len)); + if (len > 4) + sppp_print_bytes((u_char*)(h+1), len-4); + addlog(">\n"); + } + break; + } + if (h->ident != sp->confid[IDX_CHAP]) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "chap dropping response for old ID " + "(got %d, expected %d)\n", + SPP_ARGS(ifp), + h->ident, sp->confid[IDX_CHAP]); + break; + } + if (name_len != sppp_strnlen(sp->hisauth.name, AUTHNAMELEN) + || bcmp(name, sp->hisauth.name, name_len) != 0) { + log(LOG_INFO, SPP_FMT "chap response, his name ", + SPP_ARGS(ifp)); + sppp_print_string(name, name_len); + addlog(" != expected "); + sppp_print_string(sp->hisauth.name, + sppp_strnlen(sp->hisauth.name, AUTHNAMELEN)); + addlog("\n"); + } + if (debug) { + log(LOG_DEBUG, SPP_FMT "chap input(%s) " + "<%s id=0x%x len=%d name=", + SPP_ARGS(ifp), + sppp_state_name(sp->state[IDX_CHAP]), + sppp_auth_type_name(PPP_CHAP, h->type), + h->ident, ntohs (h->len)); + sppp_print_string((char*)name, name_len); + addlog(" value-size=%d value=", value_len); + sppp_print_bytes(value, value_len); + addlog(">\n"); + } + if (value_len != AUTHKEYLEN) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "chap bad hash value length: " + "%d bytes, should be %d\n", + SPP_ARGS(ifp), value_len, + AUTHKEYLEN); + break; + } + + MD5Init(&ctx); + MD5Update(&ctx, &h->ident, 1); + MD5Update(&ctx, sp->hisauth.secret, + sppp_strnlen(sp->hisauth.secret, AUTHKEYLEN)); + MD5Update(&ctx, sp->myauth.challenge, AUTHKEYLEN); + MD5Final(digest, &ctx); + +#define FAILMSG "Failed..." +#define SUCCMSG "Welcome!" + + if (value_len != sizeof digest || + bcmp(digest, value, value_len) != 0) { + /* action scn, tld */ + sppp_auth_send(&chap, sp, CHAP_FAILURE, h->ident, + sizeof(FAILMSG) - 1, (u_char *)FAILMSG, + 0); + chap.tld(sp); + break; + } + /* action sca, perhaps tlu */ + if (sp->state[IDX_CHAP] == STATE_REQ_SENT || + sp->state[IDX_CHAP] == STATE_OPENED) + sppp_auth_send(&chap, sp, CHAP_SUCCESS, h->ident, + sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG, + 0); + if (sp->state[IDX_CHAP] == STATE_REQ_SENT) { + sppp_cp_change_state(&chap, sp, STATE_OPENED); + chap.tlu(sp); + } + break; + + default: + /* Unknown CHAP packet type -- ignore. */ + if (debug) { + log(LOG_DEBUG, SPP_FMT "chap unknown input(%s) " + "<0x%x id=0x%xh len=%d", + SPP_ARGS(ifp), + sppp_state_name(sp->state[IDX_CHAP]), + h->type, h->ident, ntohs(h->len)); + if (len > 4) + sppp_print_bytes((u_char*)(h+1), len-4); + addlog(">\n"); + } + break; + + } +} + +static void +sppp_chap_init(struct sppp *sp) +{ + /* Chap doesn't have STATE_INITIAL at all. */ + sp->state[IDX_CHAP] = STATE_CLOSED; + sp->fail_counter[IDX_CHAP] = 0; +#if defined (__FreeBSD__) + callout_handle_init(&sp->ch[IDX_CHAP]); +#endif +} + +static void +sppp_chap_open(struct sppp *sp) +{ + if (sp->myauth.proto == PPP_CHAP && + (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) { + /* we are authenticator for CHAP, start it */ + chap.scr(sp); + sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure; + sppp_cp_change_state(&chap, sp, STATE_REQ_SENT); + } + /* nothing to be done if we are peer, await a challenge */ +} + +static void +sppp_chap_close(struct sppp *sp) +{ + if (sp->state[IDX_CHAP] != STATE_CLOSED) + sppp_cp_change_state(&chap, sp, STATE_CLOSED); +} + +static void +sppp_chap_TO(void *cookie) +{ + struct sppp *sp = (struct sppp *)cookie; + STDDCL; + int s; + + s = splimp(); + if (debug) + log(LOG_DEBUG, SPP_FMT "chap TO(%s) rst_counter = %d\n", + SPP_ARGS(ifp), + sppp_state_name(sp->state[IDX_CHAP]), + sp->rst_counter[IDX_CHAP]); + + if (--sp->rst_counter[IDX_CHAP] < 0) + /* TO- event */ + switch (sp->state[IDX_CHAP]) { + case STATE_REQ_SENT: + chap.tld(sp); + sppp_cp_change_state(&chap, sp, STATE_CLOSED); + break; + } + else + /* TO+ (or TO*) event */ + switch (sp->state[IDX_CHAP]) { + case STATE_OPENED: + /* TO* event */ + sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure; + /* fall through */ + case STATE_REQ_SENT: + chap.scr(sp); + /* sppp_cp_change_state() will restart the timer */ + sppp_cp_change_state(&chap, sp, STATE_REQ_SENT); + break; + } + + splx(s); +} + +static void +sppp_chap_tlu(struct sppp *sp) +{ + STDDCL; + int i = 0, x; + + i = 0; + sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure; + + /* + * Some broken CHAP implementations (Conware CoNet, firmware + * 4.0.?) don't want to re-authenticate their CHAP once the + * initial challenge-response exchange has taken place. + * Provide for an option to avoid rechallenges. + */ + if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0) { + /* + * Compute the re-challenge timeout. This will yield + * a number between 300 and 810 seconds. + */ + i = 300 + ((unsigned)(random() & 0xff00) >> 7); + +#if defined (__FreeBSD__) + sp->ch[IDX_CHAP] = +#endif + timeout(chap.TO, (void *)sp, i * hz); + } + + if (debug) { + log(LOG_DEBUG, + SPP_FMT "chap %s, ", + SPP_ARGS(ifp), + sp->pp_phase == PHASE_NETWORK? "reconfirmed": "tlu"); + if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0) + addlog("next re-challenge in %d seconds\n", i); + else + addlog("re-challenging supressed\n"); + } + + x = splimp(); + /* indicate to LCP that we need to be closed down */ + sp->lcp.protos |= (1 << IDX_CHAP); + + if (sp->pp_flags & PP_NEEDAUTH) { + /* + * Remote is authenticator, but his auth proto didn't + * complete yet. Defer the transition to network + * phase. + */ + splx(x); + return; + } + splx(x); + + /* + * If we are already in phase network, we are done here. This + * is the case if this is a dummy tlu event after a re-challenge. + */ + if (sp->pp_phase != PHASE_NETWORK) + sppp_phase_network(sp); +} + +static void +sppp_chap_tld(struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "chap tld\n", SPP_ARGS(ifp)); + UNTIMEOUT(chap.TO, (void *)sp, sp->ch[IDX_CHAP]); + sp->lcp.protos &= ~(1 << IDX_CHAP); + + lcp.Close(sp); +} + +static void +sppp_chap_scr(struct sppp *sp) +{ + u_long *ch, seed; + u_char clen; +#if defined (__NetBSD__) || defined (__OpenBSD__) + struct timeval tv; +#endif + + /* Compute random challenge. */ + ch = (u_long *)sp->myauth.challenge; +#if defined (__FreeBSD__) + read_random(&seed, sizeof seed); +#else + microtime(&tv); + seed = tv.tv_sec ^ tv.tv_usec; +#endif + ch[0] = seed ^ random(); + ch[1] = seed ^ random(); + ch[2] = seed ^ random(); + ch[3] = seed ^ random(); + clen = AUTHKEYLEN; + + sp->confid[IDX_CHAP] = ++sp->pp_seq; + + sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->confid[IDX_CHAP], + sizeof clen, (const char *)&clen, + (size_t)AUTHKEYLEN, sp->myauth.challenge, + (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN), + sp->myauth.name, + 0); +} +/* + *--------------------------------------------------------------------------* + * * + * The PAP implementation. * + * * + *--------------------------------------------------------------------------* + */ +/* + * For PAP, we need to keep a little state also if we are the peer, not the + * authenticator. This is since we don't get a request to authenticate, but + * have to repeatedly authenticate ourself until we got a response (or the + * retry counter is expired). + */ + +/* + * Handle incoming PAP packets. */ +static void +sppp_pap_input(struct sppp *sp, struct mbuf *m) +{ + STDDCL; + struct lcp_header *h; + int len, x; + u_char *name, *passwd, mlen; + int name_len, passwd_len; + + len = m->m_pkthdr.len; + if (len < 5) { + if (debug) + log(LOG_DEBUG, + SPP_FMT "pap invalid packet length: %d bytes\n", + SPP_ARGS(ifp), len); + return; + } + h = mtod (m, struct lcp_header*); + if (len > ntohs (h->len)) + len = ntohs (h->len); + switch (h->type) { + /* PAP request is my authproto */ + case PAP_REQ: + name = 1 + (u_char*)(h+1); + name_len = name[-1]; + passwd = name + name_len + 1; + if (name_len > len - 6 || + (passwd_len = passwd[-1]) > len - 6 - name_len) { + if (debug) { + log(LOG_DEBUG, SPP_FMT "pap corrupted input " + "<%s id=0x%x len=%d", + SPP_ARGS(ifp), + sppp_auth_type_name(PPP_PAP, h->type), + h->ident, ntohs(h->len)); + if (len > 4) + sppp_print_bytes((u_char*)(h+1), len-4); + addlog(">\n"); + } + break; + } + if (debug) { + log(LOG_DEBUG, SPP_FMT "pap input(%s) " + "<%s id=0x%x len=%d name=", + SPP_ARGS(ifp), + sppp_state_name(sp->state[IDX_PAP]), + sppp_auth_type_name(PPP_PAP, h->type), + h->ident, ntohs(h->len)); + sppp_print_string((char*)name, name_len); + addlog(" passwd="); + sppp_print_string((char*)passwd, passwd_len); + addlog(">\n"); + } + if (name_len > AUTHNAMELEN || + passwd_len > AUTHKEYLEN || + bcmp(name, sp->hisauth.name, name_len) != 0 || + bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) { + /* action scn, tld */ + mlen = sizeof(FAILMSG) - 1; + sppp_auth_send(&pap, sp, PAP_NAK, h->ident, + sizeof mlen, (const char *)&mlen, + sizeof(FAILMSG) - 1, (u_char *)FAILMSG, + 0); + pap.tld(sp); + break; + } + /* action sca, perhaps tlu */ + if (sp->state[IDX_PAP] == STATE_REQ_SENT || + sp->state[IDX_PAP] == STATE_OPENED) { + mlen = sizeof(SUCCMSG) - 1; + sppp_auth_send(&pap, sp, PAP_ACK, h->ident, + sizeof mlen, (const char *)&mlen, + sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG, + 0); + } + if (sp->state[IDX_PAP] == STATE_REQ_SENT) { + sppp_cp_change_state(&pap, sp, STATE_OPENED); + pap.tlu(sp); + } + break; + + /* ack and nak are his authproto */ + case PAP_ACK: + UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); + if (debug) { + log(LOG_DEBUG, SPP_FMT "pap success", + SPP_ARGS(ifp)); + name_len = *((char *)h); + if (len > 5 && name_len) { + addlog(": "); + sppp_print_string((char*)(h+1), name_len); + } + addlog("\n"); + } + x = splimp(); + sp->pp_flags &= ~PP_NEEDAUTH; + if (sp->myauth.proto == PPP_PAP && + (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) && + (sp->lcp.protos & (1 << IDX_PAP)) == 0) { + /* + * We are authenticator for PAP but didn't + * complete yet. Leave it to tlu to proceed + * to network phase. + */ + splx(x); + break; + } + splx(x); + sppp_phase_network(sp); + break; + + case PAP_NAK: + UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); + if (debug) { + log(LOG_INFO, SPP_FMT "pap failure", + SPP_ARGS(ifp)); + name_len = *((char *)h); + if (len > 5 && name_len) { + addlog(": "); + sppp_print_string((char*)(h+1), name_len); + } + addlog("\n"); + } else + log(LOG_INFO, SPP_FMT "pap failure\n", + SPP_ARGS(ifp)); + /* await LCP shutdown by authenticator */ + break; + + default: + /* Unknown PAP packet type -- ignore. */ + if (debug) { + log(LOG_DEBUG, SPP_FMT "pap corrupted input " + "<0x%x id=0x%x len=%d", + SPP_ARGS(ifp), + h->type, h->ident, ntohs(h->len)); + if (len > 4) + sppp_print_bytes((u_char*)(h+1), len-4); + addlog(">\n"); + } + break; + + } +} + +static void +sppp_pap_init(struct sppp *sp) +{ + /* PAP doesn't have STATE_INITIAL at all. */ + sp->state[IDX_PAP] = STATE_CLOSED; + sp->fail_counter[IDX_PAP] = 0; +#if defined (__FreeBSD__) + callout_handle_init(&sp->ch[IDX_PAP]); + callout_handle_init(&sp->pap_my_to_ch); +#endif +} + +static void +sppp_pap_open(struct sppp *sp) +{ + if (sp->hisauth.proto == PPP_PAP && + (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) { + /* we are authenticator for PAP, start our timer */ + sp->rst_counter[IDX_PAP] = sp->lcp.max_configure; + sppp_cp_change_state(&pap, sp, STATE_REQ_SENT); + } + if (sp->myauth.proto == PPP_PAP) { + /* we are peer, send a request, and start a timer */ + pap.scr(sp); +#if defined (__FreeBSD__) + sp->pap_my_to_ch = +#endif + timeout(sppp_pap_my_TO, (void *)sp, sp->lcp.timeout); + } +} + +static void +sppp_pap_close(struct sppp *sp) +{ + if (sp->state[IDX_PAP] != STATE_CLOSED) + sppp_cp_change_state(&pap, sp, STATE_CLOSED); +} + +/* + * That's the timeout routine if we are authenticator. Since the + * authenticator is basically passive in PAP, we can't do much here. + */ +static void +sppp_pap_TO(void *cookie) +{ + struct sppp *sp = (struct sppp *)cookie; + STDDCL; + int s; + + s = splimp(); + if (debug) + log(LOG_DEBUG, SPP_FMT "pap TO(%s) rst_counter = %d\n", + SPP_ARGS(ifp), + sppp_state_name(sp->state[IDX_PAP]), + sp->rst_counter[IDX_PAP]); + + if (--sp->rst_counter[IDX_PAP] < 0) + /* TO- event */ + switch (sp->state[IDX_PAP]) { + case STATE_REQ_SENT: + pap.tld(sp); + sppp_cp_change_state(&pap, sp, STATE_CLOSED); + break; + } + else + /* TO+ event, not very much we could do */ + switch (sp->state[IDX_PAP]) { + case STATE_REQ_SENT: + /* sppp_cp_change_state() will restart the timer */ + sppp_cp_change_state(&pap, sp, STATE_REQ_SENT); + break; + } + + splx(s); +} + +/* + * That's the timeout handler if we are peer. Since the peer is active, + * we need to retransmit our PAP request since it is apparently lost. + * XXX We should impose a max counter. + */ +static void +sppp_pap_my_TO(void *cookie) +{ + struct sppp *sp = (struct sppp *)cookie; + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "pap peer TO\n", + SPP_ARGS(ifp)); + + pap.scr(sp); +} + +static void +sppp_pap_tlu(struct sppp *sp) +{ + STDDCL; + int x; + + sp->rst_counter[IDX_PAP] = sp->lcp.max_configure; + + if (debug) + log(LOG_DEBUG, SPP_FMT "%s tlu\n", + SPP_ARGS(ifp), pap.name); + + x = splimp(); + /* indicate to LCP that we need to be closed down */ + sp->lcp.protos |= (1 << IDX_PAP); + + if (sp->pp_flags & PP_NEEDAUTH) { + /* + * Remote is authenticator, but his auth proto didn't + * complete yet. Defer the transition to network + * phase. + */ + splx(x); + return; + } + splx(x); + sppp_phase_network(sp); +} + +static void +sppp_pap_tld(struct sppp *sp) +{ + STDDCL; + + if (debug) + log(LOG_DEBUG, SPP_FMT "pap tld\n", SPP_ARGS(ifp)); + UNTIMEOUT(pap.TO, (void *)sp, sp->ch[IDX_PAP]); + UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); + sp->lcp.protos &= ~(1 << IDX_PAP); + + lcp.Close(sp); +} + +static void +sppp_pap_scr(struct sppp *sp) +{ + u_char idlen, pwdlen; + + sp->confid[IDX_PAP] = ++sp->pp_seq; + pwdlen = sppp_strnlen(sp->myauth.secret, AUTHKEYLEN); + idlen = sppp_strnlen(sp->myauth.name, AUTHNAMELEN); + + sppp_auth_send(&pap, sp, PAP_REQ, sp->confid[IDX_PAP], + sizeof idlen, (const char *)&idlen, + (size_t)idlen, sp->myauth.name, + sizeof pwdlen, (const char *)&pwdlen, + (size_t)pwdlen, sp->myauth.secret, + 0); +} +/* + * Random miscellaneous functions. + */ + +/* + * Send a PAP or CHAP proto packet. + * + * Varadic function, each of the elements for the ellipsis is of type + * ``size_t mlen, const u_char *msg''. Processing will stop iff + * mlen == 0. + */ + +static void +sppp_auth_send(const struct cp *cp, struct sppp *sp, u_char type, u_char id, + ...) +{ + STDDCL; + struct ppp_header *h; + struct lcp_header *lh; + struct mbuf *m; + u_char *p; + int len; + size_t mlen; + const char *msg; + va_list ap; + + MGETHDR (m, M_DONTWAIT, MT_DATA); + if (! m) + return; + m->m_pkthdr.rcvif = 0; + + h = mtod (m, struct ppp_header*); + h->address = PPP_ALLSTATIONS; /* broadcast address */ + h->control = PPP_UI; /* Unnumbered Info */ + h->protocol = htons(cp->proto); + + lh = (struct lcp_header*)(h + 1); + lh->type = type; + lh->ident = id; + p = (u_char*) (lh+1); + + va_start(ap, id); + len = 0; + + while ((mlen = va_arg(ap, size_t)) != 0) { + msg = va_arg(ap, const char *); + len += mlen; + if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) { + va_end(ap); + m_freem(m); + return; + } + + bcopy(msg, p, mlen); + p += mlen; + } + va_end(ap); + + m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len; + lh->len = htons (LCP_HEADER_LEN + len); + + if (debug) { + log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d", + SPP_ARGS(ifp), cp->name, + sppp_auth_type_name(cp->proto, lh->type), + lh->ident, ntohs(lh->len)); + if (len) + sppp_print_bytes((u_char*) (lh+1), len); + addlog(">\n"); + } + if (IF_QFULL (&sp->pp_cpq)) { + IF_DROP (&sp->pp_fastq); + IF_DROP (&ifp->if_snd); + m_freem (m); + ++ifp->if_oerrors; + } else + IF_ENQUEUE (&sp->pp_cpq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + ifp->if_obytes += m->m_pkthdr.len + 3; +} + +/* + * Flush interface queue. + */ +static void +sppp_qflush(struct ifqueue *ifq) +{ + struct mbuf *m, *n; + + n = ifq->ifq_head; + while ((m = n)) { + n = m->m_act; + m_freem (m); + } + ifq->ifq_head = 0; + ifq->ifq_tail = 0; + ifq->ifq_len = 0; +} + +/* + * Send keepalive packets, every 10 seconds. + */ +static void +sppp_keepalive(void *dummy) +{ + struct sppp *sp; + int s; + + s = splimp(); + for (sp=spppq; sp; sp=sp->pp_next) { + struct ifnet *ifp = &sp->pp_if; + + /* Keepalive mode disabled or channel down? */ + if (! (sp->pp_flags & PP_KEEPALIVE) || + ! (ifp->if_flags & IFF_RUNNING)) + continue; + + /* No keepalive in PPP mode if LCP not opened yet. */ + if (! (sp->pp_flags & PP_CISCO) && + sp->pp_phase < PHASE_AUTHENTICATE) + continue; + + if (sp->pp_alivecnt == MAXALIVECNT) { + /* No keepalive packets got. Stop the interface. */ + printf (SPP_FMT "down\n", SPP_ARGS(ifp)); + if (sp->pp_flags & PP_CISCO) { + if_down (ifp); + sppp_qflush (&sp->pp_cpq); + } else { + /* Shut down the PPP link. */ + lcp.Close(sp); + } + } + if (sp->pp_alivecnt <= MAXALIVECNT) + ++sp->pp_alivecnt; + if (sp->pp_flags & PP_CISCO) + sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq, + sp->pp_rseq); + else if (sp->pp_phase >= PHASE_AUTHENTICATE) { + long nmagic = htonl (sp->lcp.magic); + sp->lcp.echoid = ++sp->pp_seq; + sppp_cp_send (sp, PPP_LCP, ECHO_REQ, + sp->lcp.echoid, 4, &nmagic); + } + } + splx(s); +#if defined (__FreeBSD__) + keepalive_ch = +#endif + timeout(sppp_keepalive, 0, hz * 10); +} + +/* + * Get both IP addresses. + */ +static void +sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, u_long *srcmask) +{ + struct ifnet *ifp = &sp->pp_if; + struct ifaddr *ifa; + struct sockaddr_in *si, *sm = 0; + u_long ssrc, ddst; + + sm = NULL; + ssrc = ddst = 0L; + /* + * Pick the first AF_INET address from the list, + * aliases don't make any sense on a p2p link anyway. + */ +#if defined (__FreeBSD__) + for (ifa = ifp->if_addrhead.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_link.tqe_next) +#else + for (ifa = ifp->if_addrlist.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_list.tqe_next) +#endif + if (ifa->ifa_addr->sa_family == AF_INET) { + si = (struct sockaddr_in *)ifa->ifa_addr; + sm = (struct sockaddr_in *)ifa->ifa_netmask; + if (si) + break; + } + if (ifa) { + if (si && si->sin_addr.s_addr) { + ssrc = si->sin_addr.s_addr; + if (srcmask) + *srcmask = ntohl(sm->sin_addr.s_addr); + } + + si = (struct sockaddr_in *)ifa->ifa_dstaddr; + if (si && si->sin_addr.s_addr) + ddst = si->sin_addr.s_addr; + } + + if (dst) *dst = ntohl(ddst); + if (src) *src = ntohl(ssrc); +} + +/* + * Set my IP address. Must be called at splimp. + */ +static void +sppp_set_ip_addr(struct sppp *sp, u_long src) +{ + struct ifnet *ifp = &sp->pp_if; + struct ifaddr *ifa; + struct sockaddr_in *si; + + /* + * Pick the first AF_INET address from the list, + * aliases don't make any sense on a p2p link anyway. + */ + +#if defined (__FreeBSD__) + for (ifa = ifp->if_addrhead.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_link.tqe_next) +#else + for (ifa = ifp->if_addrlist.tqh_first, si = 0; + ifa; + ifa = ifa->ifa_list.tqe_next) +#endif + { + if (ifa->ifa_addr->sa_family == AF_INET) + { + si = (struct sockaddr_in *)ifa->ifa_addr; + if (si) + break; + } + } + + if (ifa && si) + si->sin_addr.s_addr = htonl(src); +} + +static int +sppp_params(struct sppp *sp, u_long cmd, void *data) +{ + u_long subcmd; + struct ifreq *ifr = (struct ifreq *)data; + struct spppreq spr; + + /* + * ifr->ifr_data is supposed to point to a struct spppreq. + * Check the cmd word first before attempting to fetch all the + * data. + */ + if ((subcmd = fuword(ifr->ifr_data)) == -1) + return EFAULT; + + if (copyin((caddr_t)ifr->ifr_data, &spr, sizeof spr) != 0) + return EFAULT; + + switch (subcmd) { + case SPPPIOGDEFS: + if (cmd != SIOCGIFGENERIC) + return EINVAL; + /* + * We copy over the entire current state, but clean + * out some of the stuff we don't wanna pass up. + * Remember, SIOCGIFGENERIC is unprotected, and can be + * called by any user. No need to ever get PAP or + * CHAP secrets back to userland anyway. + */ + bcopy(sp, &spr.defs, sizeof(struct sppp)); + bzero(spr.defs.myauth.secret, AUTHKEYLEN); + bzero(spr.defs.myauth.challenge, AUTHKEYLEN); + bzero(spr.defs.hisauth.secret, AUTHKEYLEN); + bzero(spr.defs.hisauth.challenge, AUTHKEYLEN); + return copyout(&spr, (caddr_t)ifr->ifr_data, sizeof spr); + + case SPPPIOSDEFS: + if (cmd != SIOCSIFGENERIC) + return EINVAL; + /* + * We have a very specific idea of which fields we allow + * being passed back from userland, so to not clobber our + * current state. For one, we only allow setting + * anything if LCP is in dead phase. Once the LCP + * negotiations started, the authentication settings must + * not be changed again. (The administrator can force an + * ifconfig down in order to get LCP back into dead + * phase.) + * + * Also, we only allow for authentication parameters to be + * specified. + * + * XXX Should allow to set or clear pp_flags. + * + * Finally, if the respective authentication protocol to + * be used is set differently than 0, but the secret is + * passed as all zeros, we don't trash the existing secret. + * This allows an administrator to change the system name + * only without clobbering the secret (which he didn't get + * back in a previous SPPPIOGDEFS call). However, the + * secrets are cleared if the authentication protocol is + * reset to 0. + */ + if (sp->pp_phase != PHASE_DEAD) + return EBUSY; + + if ((spr.defs.myauth.proto != 0 && spr.defs.myauth.proto != PPP_PAP && + spr.defs.myauth.proto != PPP_CHAP) || + (spr.defs.hisauth.proto != 0 && spr.defs.hisauth.proto != PPP_PAP && + spr.defs.hisauth.proto != PPP_CHAP)) + return EINVAL; + + if (spr.defs.myauth.proto == 0) + /* resetting myauth */ + bzero(&sp->myauth, sizeof sp->myauth); + else { + /* setting/changing myauth */ + sp->myauth.proto = spr.defs.myauth.proto; + bcopy(spr.defs.myauth.name, sp->myauth.name, AUTHNAMELEN); + if (spr.defs.myauth.secret[0] != '\0') + bcopy(spr.defs.myauth.secret, sp->myauth.secret, + AUTHKEYLEN); + } + if (spr.defs.hisauth.proto == 0) + /* resetting hisauth */ + bzero(&sp->hisauth, sizeof sp->hisauth); + else { + /* setting/changing hisauth */ + sp->hisauth.proto = spr.defs.hisauth.proto; + sp->hisauth.flags = spr.defs.hisauth.flags; + bcopy(spr.defs.hisauth.name, sp->hisauth.name, AUTHNAMELEN); + if (spr.defs.hisauth.secret[0] != '\0') + bcopy(spr.defs.hisauth.secret, sp->hisauth.secret, + AUTHKEYLEN); + } + break; + + default: + return EINVAL; + } + + return 0; +} + +static void +sppp_phase_network(struct sppp *sp) +{ + struct ifnet *ifp = &sp->pp_if; + int i; + u_long mask; + + sp->pp_phase = PHASE_NETWORK; + + log(LOG_INFO, SPP_FMT "phase %s\n", SPP_ARGS(ifp), + sppp_phase_name(sp->pp_phase)); + + /* Notify NCPs now. */ + for (i = 0; i < IDX_COUNT; i++) + if ((cps[i])->flags & CP_NCP) + (cps[i])->Open(sp); + + /* Send Up events to all NCPs. */ + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + if (sp->lcp.protos & mask && ((cps[i])->flags & CP_NCP)) + (cps[i])->Up(sp); + + /* if no NCP is starting, all this was in vain, close down */ + sppp_lcp_check_and_close(sp); +} + + +static const char * +sppp_cp_type_name(u_char type) +{ + static char buf[12]; + switch (type) { + case CONF_REQ: return "conf-req"; + case CONF_ACK: return "conf-ack"; + case CONF_NAK: return "conf-nak"; + case CONF_REJ: return "conf-rej"; + case TERM_REQ: return "term-req"; + case TERM_ACK: return "term-ack"; + case CODE_REJ: return "code-rej"; + case PROTO_REJ: return "proto-rej"; + case ECHO_REQ: return "echo-req"; + case ECHO_REPLY: return "echo-reply"; + case DISC_REQ: return "discard-req"; + } + sprintf (buf, "0x%x", type); + return buf; +} + +static const char * +sppp_auth_type_name(u_short proto, u_char type) +{ + static char buf[12]; + switch (proto) { + case PPP_CHAP: + switch (type) { + case CHAP_CHALLENGE: return "challenge"; + case CHAP_RESPONSE: return "response"; + case CHAP_SUCCESS: return "success"; + case CHAP_FAILURE: return "failure"; + } + case PPP_PAP: + switch (type) { + case PAP_REQ: return "req"; + case PAP_ACK: return "ack"; + case PAP_NAK: return "nak"; + } + } + sprintf (buf, "0x%x", type); + return buf; +} + +static const char * +sppp_lcp_opt_name(u_char opt) +{ + static char buf[12]; + switch (opt) { + case LCP_OPT_MRU: return "mru"; + case LCP_OPT_ASYNC_MAP: return "async-map"; + case LCP_OPT_AUTH_PROTO: return "auth-proto"; + case LCP_OPT_QUAL_PROTO: return "qual-proto"; + case LCP_OPT_MAGIC: return "magic"; + case LCP_OPT_PROTO_COMP: return "proto-comp"; + case LCP_OPT_ADDR_COMP: return "addr-comp"; + } + sprintf (buf, "0x%x", opt); + return buf; +} + +static const char * +sppp_ipcp_opt_name(u_char opt) +{ + static char buf[12]; + switch (opt) { + case IPCP_OPT_ADDRESSES: return "addresses"; + case IPCP_OPT_COMPRESSION: return "compression"; + case IPCP_OPT_ADDRESS: return "address"; + } + sprintf (buf, "0x%x", opt); + return buf; +} + +static const char * +sppp_state_name(int state) +{ + switch (state) { + case STATE_INITIAL: return "initial"; + case STATE_STARTING: return "starting"; + case STATE_CLOSED: return "closed"; + case STATE_STOPPED: return "stopped"; + case STATE_CLOSING: return "closing"; + case STATE_STOPPING: return "stopping"; + case STATE_REQ_SENT: return "req-sent"; + case STATE_ACK_RCVD: return "ack-rcvd"; + case STATE_ACK_SENT: return "ack-sent"; + case STATE_OPENED: return "opened"; + } + return "illegal"; +} + +static const char * +sppp_phase_name(enum ppp_phase phase) +{ + switch (phase) { + case PHASE_DEAD: return "dead"; + case PHASE_ESTABLISH: return "establish"; + case PHASE_TERMINATE: return "terminate"; + case PHASE_AUTHENTICATE: return "authenticate"; + case PHASE_NETWORK: return "network"; + } + return "illegal"; +} + +static const char * +sppp_proto_name(u_short proto) +{ + static char buf[12]; + switch (proto) { + case PPP_LCP: return "lcp"; + case PPP_IPCP: return "ipcp"; + case PPP_PAP: return "pap"; + case PPP_CHAP: return "chap"; + } + sprintf(buf, "0x%x", (unsigned)proto); + return buf; +} + +static void +sppp_print_bytes(const u_char *p, u_short len) +{ + addlog(" %02x", *p++); + while (--len > 0) + addlog("-%02x", *p++); +} + +static void +sppp_print_string(const char *p, u_short len) +{ + u_char c; + + while (len-- > 0) { + c = *p++; + /* + * Print only ASCII chars directly. RFC 1994 recommends + * using only them, but we don't rely on it. */ + if (c < ' ' || c > '~') + addlog("\\x%x", c); + else + addlog("%c", c); + } +} + +static const char * +sppp_dotted_quad(u_long addr) +{ + static char s[16]; + sprintf(s, "%d.%d.%d.%d", + (int)((addr >> 24) & 0xff), + (int)((addr >> 16) & 0xff), + (int)((addr >> 8) & 0xff), + (int)(addr & 0xff)); + return s; +} + +static int +sppp_strnlen(u_char *p, int max) +{ + int len; + + for (len = 0; len < max && *p; ++p) + ++len; + return len; +} + +/* a dummy, used to drop uninteresting events */ +static void +sppp_null(struct sppp *unused) +{ + /* do just nothing */ +} +/* + * This file is large. Tell emacs to highlight it nevertheless. + * + * Local Variables: + * hilit-auto-highlight-maxout: 120000 + * End: + */ |