diff options
author | scottl <scottl@FreeBSD.org> | 2005-10-03 07:05:34 +0000 |
---|---|---|
committer | scottl <scottl@FreeBSD.org> | 2005-10-03 07:05:34 +0000 |
commit | d39a762fb7f3b6ea3df528e0ffc3259123d225cc (patch) | |
tree | b27cd7ba5a306d23510b8cb37c4036f2efc7405a /sys | |
parent | 68576cc8135a9a0be69c7ef6cb184d1143fdd483 (diff) | |
download | FreeBSD-src-d39a762fb7f3b6ea3df528e0ffc3259123d225cc.zip FreeBSD-src-d39a762fb7f3b6ea3df528e0ffc3259123d225cc.tar.gz |
Reintroduce the lmc T1/E1/T3 WAN driver. This version is locked, supports
interface polling, compiles on 64-bit platforms, and compiles on NetBSD,
OpenBSD, BSD/OS, and Linux. Woo! Thanks to David Boggs for providing this
driver.
Altq, sppp, netgraph, and bpf are required for this driver to operate.
Userland tools and man pages will be committed next.
Submitted by: David Boggs
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/NOTES | 4 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/dev/lmc/if_lmc.c | 7026 | ||||
-rw-r--r-- | sys/dev/lmc/if_lmc.h | 1687 | ||||
-rw-r--r-- | sys/modules/Makefile | 1 | ||||
-rw-r--r-- | sys/modules/lmc/Makefile | 25 |
6 files changed, 8744 insertions, 0 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 1bde93f..85ff0e9 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1720,6 +1720,7 @@ device miibus # lge: Support for PCI gigabit ethernet adapters based on the Level 1 # LXT1001 NetCellerator chipset. This includes the D-Link DGE-500SX, # SMC TigerCard 1000 (SMC9462SX), and some Addtron cards. +# lmc: Support for the LMC/SBE wide-area network interface cards. # my: Myson Fast Ethernet (MTD80X, MTD89X) # nge: Support for PCI gigabit ethernet adapters based on the National # Semiconductor DP83820 and DP83821 chipset. This includes the @@ -1845,6 +1846,9 @@ device sk device ti device fpa +# PCI WAN adapters. +device lmc + # Use "private" jumbo buffers allocated exclusively for the ti(4) driver. # This option is incompatible with the TI_JUMBO_HDRSPLIT option below. #options TI_PRIVATE_JUMBOS diff --git a/sys/conf/files b/sys/conf/files index 1af4da6..1c70b98 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -689,6 +689,7 @@ dev/joy/joy_pccard.c optional joy pccard dev/kbdmux/kbdmux.c optional kbdmux dev/led/led.c standard dev/lge/if_lge.c optional lge +dev/lmc/if_lmc.c optional lmc dev/lnc/if_lnc.c optional lnc dev/lnc/if_lnc_pci.c optional lnc pci dev/mc146818/mc146818.c optional mc146818 diff --git a/sys/dev/lmc/if_lmc.c b/sys/dev/lmc/if_lmc.c new file mode 100644 index 0000000..d475bab --- /dev/null +++ b/sys/dev/lmc/if_lmc.c @@ -0,0 +1,7026 @@ +/* + * $FreeBSD$ + * + * Copyright (c) 2002-2004 David Boggs. <boggs@boggs.palo-alto.ca.us> + * All rights reserved. + * + * BSD License: + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * GNU General Public License: + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Description: + * + * This is an open-source Unix device driver for PCI-bus WAN interface cards. + * It sends and receives packets in HDLC frames over synchronous links. + * A generic PC plus Unix plus some SBE/LMC cards makes an OPEN router. + * This driver works with FreeBSD, NetBSD, OpenBSD, BSD/OS and Linux. + * It has been tested on i386 (32-bit little-end), Sparc (64-bit big-end), + * and Alpha (64-bit little-end) architectures. + * + * History and Authors: + * + * Ron Crane had the neat idea to use a Fast Ethernet chip as a PCI + * interface and add an Ethernet-to-HDLC gate array to make a WAN card. + * David Boggs designed the Ethernet-to-HDLC gate arrays and PC cards. + * We did this at our company, LAN Media Corporation (LMC). + * SBE Corp aquired LMC and continues to make the cards. + * + * Since the cards use Tulip Ethernet chips, we started with Matt Thomas' + * ubiquitous "de" driver. Michael Graff stripped out the Ethernet stuff + * and added HSSI stuff. Basil Gunn ported it to Solaris (lost) and + * Rob Braun ported it to Linux. Andrew Stanley-Jones added support + * for three more cards and wrote the first version of lmcconfig. + * During 2002-5 David Boggs rewrote it and now feels responsible for it. + * + * Responsible Individual: + * + * Send bug reports and improvements to <boggs@boggs.palo-alto.ca.us>. + */ +#if __FreeBSD__ +# include <sys/param.h> /* OS version */ +# define IFNET 1 +# include "opt_inet.h" /* INET */ +# include "opt_inet6.h" /* INET6 */ +# include "opt_netgraph.h" /* NETGRAPH */ +# ifndef NETGRAPH +# define NETGRAPH 0 +# endif +# define P2P 0 /* not in FreeBSD */ +# include "opt_global.h"/* ALTQ, DEVICE_POLLING */ +# if (__FreeBSD_version >= 500000) +# define NSPPP 1 /* No count devices in FreeBSD 5 */ +# include "opt_bpf.h" /* DEV_BPF */ +# define NBPFILTER DEV_BPF +# else /* FreeBSD-4 */ +# include "sppp.h" /* NSPPP */ +# include "bpf.h" /* NBPF */ +# define NBPFILTER NBPF +# endif +# define GEN_HDLC 0 /* not in FreeBSD */ +# +# include <sys/systm.h> +# include <sys/kernel.h> +# include <sys/malloc.h> +# include <sys/mbuf.h> +# include <sys/socket.h> +# include <sys/sockio.h> +# include <sys/module.h> +# include <sys/bus.h> +# include <sys/lock.h> +# include <net/if.h> +# include <net/if_types.h> +# include <net/if_media.h> +# include <net/netisr.h> +# include <machine/bus.h> +# include <machine/resource.h> +# include <machine/clock.h> +# include <sys/rman.h> +# include <vm/vm.h> +# include <vm/pmap.h> +# if (__FreeBSD_version >= 500000) +# include <sys/mutex.h> +# include <dev/pci/pcivar.h> +# else /* FreeBSD-4 */ +# include <sys/proc.h> +# include <pci/pcivar.h> +# endif +# if NETGRAPH +# include <netgraph/ng_message.h> +# include <netgraph/netgraph.h> +# endif +# if (INET || INET6) +# include <netinet/in.h> +# include <netinet/in_var.h> +# endif +# if NSPPP +# include <net/if_sppp.h> +# endif +# if NBPFILTER +# include <net/bpf.h> +# endif +/* and finally... */ +# include <dev/lmc/if_lmc.h> +#endif /*__FreeBSD__*/ + +#if __NetBSD__ +# include <sys/param.h> /* OS version */ +# define IFNET 1 +# include "opt_inet.h" /* INET6, INET */ +# define NETGRAPH 0 /* not in NetBSD */ +# include "sppp.h" /* NSPPP */ +# define P2P 0 /* not in NetBSD */ +# include "opt_altq_enabled.h" /* ALTQ */ +# include "bpfilter.h" /* NBPFILTER */ +# define GEN_HDLC 0 /* not in NetBSD */ +# +# include <sys/systm.h> +# include <sys/kernel.h> +# include <sys/lkm.h> +# include <sys/mbuf.h> +# include <sys/socket.h> +# include <sys/sockio.h> +# include <sys/device.h> +# include <sys/lock.h> +# include <net/if.h> +# include <net/if_types.h> +# include <net/if_media.h> +# include <net/netisr.h> +# include <machine/bus.h> +# include <machine/intr.h> +# include <dev/pci/pcivar.h> +# if (__NetBSD_Version__ >= 106000000) +# include <uvm/uvm_extern.h> +# else +# include <vm/vm.h> +# endif +# if (INET || INET6) +# include <netinet/in.h> +# include <netinet/in_var.h> +# endif +# if NSPPP +# if (__NetBSD_Version__ >= 106000000) +# include <net/if_spppvar.h> +# else +# include <net/if_sppp.h> +# endif +# endif +# if NBPFILTER +# include <net/bpf.h> +# endif +/* and finally... */ +# include "if_lmc.h" +#endif /*__NetBSD__*/ + +#if __OpenBSD__ +# include <sys/param.h> /* OS version */ +# define IFNET 1 +/* -DINET is passed on the compiler command line */ +/* -DINET6 is passed on the compiler command line */ +# define NETGRAPH 0 /* not in OpenBSD */ +# include "sppp.h" /* NSPPP */ +# define P2P 0 /* not in OpenBSD */ +/* -DALTQ is passed on the compiler command line */ +# include "bpfilter.h" /* NBPFILTER */ +# define GEN_HDLC 0 /* not in OpenBSD */ +# +# include <sys/systm.h> +# include <sys/kernel.h> +# include <sys/conf.h> +# include <sys/exec.h> +# include <sys/lkm.h> +# include <sys/mbuf.h> +# include <sys/socket.h> +# include <sys/sockio.h> +# include <sys/device.h> +# include <sys/lock.h> +# include <net/if.h> +# include <net/if_types.h> +# include <net/if_media.h> +# include <net/netisr.h> +# include <machine/bus.h> +# include <machine/intr.h> +# include <dev/pci/pcivar.h> +# if (OpenBSD >= 200206) +# include <uvm/uvm_extern.h> +# else +# include <vm/vm.h> +# endif +# if (INET || INET6) +# include <netinet/in.h> +# include <netinet/in_var.h> +# endif +# if NSPPP +# include <net/if_sppp.h> +# endif +# if NBPFILTER +# include <net/bpf.h> +# endif +/* and finally... */ +# include "if_lmc.h" +#endif /*__OpenBSD__*/ + +#if __bsdi__ +# include <sys/param.h> /* OS version */ +# define IFNET 1 +/* -DINET is passed on the compiler command line */ +/* -DINET6 is passed on the compiler command line */ +# define NETGRAPH 0 /* not in BSD/OS */ +# define NSPPP 0 /* not in BSD/OS */ +/* -DPPP is passed on the compiler command line */ +/* -DCISCO_HDLC is passed on the compiler command line */ +/* -DFR is passed on the compiler command line */ +# if (PPP || CISCO_HDLC || FR) +# define P2P 1 +# else +# define P2P 0 +# endif +# define ALTQ 0 /* not in BSD/OS */ +# include "bpfilter.h" /* NBPFILTER */ +# define GEN_HDLC 0 /* not in BSD/OS */ +# +# include <sys/kernel.h> +# include <sys/malloc.h> +# include <sys/mbuf.h> +# include <sys/socket.h> +# include <sys/sockio.h> +# include <sys/device.h> +# include <sys/lock.h> +# include <net/if.h> +# include <net/if_types.h> +# include <net/if_media.h> +# include <net/netisr.h> +# include <vm/vm.h> +# include <i386/isa/dma.h> +# include <i386/isa/isavar.h> +# include <i386/include/cpu.h> +# include <i386/pci/pci.h> +# if (INET || INET6) +# include <netinet/in.h> +# include <netinet/in_var.h> +# endif +# if P2P +# include <net/if_p2p.h> +# include <sys/ttycom.h> +# endif +# if NBPFILTER +# include <net/bpf.h> +# endif +/* and finally... */ +# include "if_lmc.h" +#endif /*__bsdi__*/ + +#if __linux__ +# include <linux/config.h> +# if (CONFIG_HDLC || CONFIG_HDLC_MODULE) +# define GEN_HDLC 1 +# else +# define GEN_HDLC 0 +# endif +# define IFNET 0 /* different in Linux */ +# define NETGRAPH 0 /* not in Linux */ +# define NSPPP 0 /* different in Linux */ +# define P2P 0 /* not in Linux */ +# define ALTQ 0 /* different in Linux */ +# define NBPFILTER 0 /* different in Linux */ +# +# include <linux/pci.h> +# include <linux/delay.h> +# include <linux/netdevice.h> +# include <linux/if_arp.h> +# if GEN_HDLC +# include <linux/hdlc.h> +# endif +/* and finally... */ +# include "if_lmc.h" +#endif /* __linux__ */ + +/* The SROM is a generic 93C46 serial EEPROM (64 words by 16 bits). */ +/* Data is set up before the RISING edge of CLK; CLK is parked low. */ +static void +shift_srom_bits(softc_t *sc, u_int32_t data, u_int32_t len) + { + u_int32_t csr = READ_CSR(TLP_SROM_MII); + for (; len>0; len--) + { /* MSB first */ + if (data & (1<<(len-1))) + csr |= TLP_SROM_DIN; /* DIN setup */ + else + csr &= ~TLP_SROM_DIN; /* DIN setup */ + WRITE_CSR(TLP_SROM_MII, csr); + csr |= TLP_SROM_CLK; /* CLK rising edge */ + WRITE_CSR(TLP_SROM_MII, csr); + csr &= ~TLP_SROM_CLK; /* CLK falling edge */ + WRITE_CSR(TLP_SROM_MII, csr); + } + } + +/* Data is sampled on the RISING edge of CLK; CLK is parked low. */ +static u_int16_t +read_srom(softc_t *sc, u_int8_t addr) + { + int i; + u_int32_t csr; + u_int16_t data; + + /* Enable SROM access. */ + csr = (TLP_SROM_SEL | TLP_SROM_RD | TLP_MII_MDOE); + WRITE_CSR(TLP_SROM_MII, csr); + /* CS rising edge prepares SROM for a new cycle. */ + csr |= TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* assert CS */ + shift_srom_bits(sc, 6, 4); /* issue read cmd */ + shift_srom_bits(sc, addr, 6); /* issue address */ + for (data=0, i=16; i>=0; i--) /* read ->17<- bits of data */ + { /* MSB first */ + csr = READ_CSR(TLP_SROM_MII); /* DOUT sampled */ + data = (data<<1) | ((csr & TLP_SROM_DOUT) ? 1:0); + csr |= TLP_SROM_CLK; /* CLK rising edge */ + WRITE_CSR(TLP_SROM_MII, csr); + csr &= ~TLP_SROM_CLK; /* CLK falling edge */ + WRITE_CSR(TLP_SROM_MII, csr); + } + /* Disable SROM access. */ + WRITE_CSR(TLP_SROM_MII, TLP_MII_MDOE); + + return data; + } + +/* The SROM is formatted by the mfgr and should NOT be written! */ +/* But lmcconfig can rewrite it in case it gets overwritten somehow. */ +/* IOCTL SYSCALL: can sleep. */ +static void +write_srom(softc_t *sc, u_int8_t addr, u_int16_t data) + { + u_int32_t csr; + int i; + + /* Enable SROM access. */ + csr = (TLP_SROM_SEL | TLP_SROM_RD | TLP_MII_MDOE); + WRITE_CSR(TLP_SROM_MII, csr); + + /* Issue write-enable command. */ + csr |= TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* assert CS */ + shift_srom_bits(sc, 4, 4); /* issue write enable cmd */ + shift_srom_bits(sc, 63, 6); /* issue address */ + csr &= ~TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* deassert CS */ + + /* Issue erase command. */ + csr |= TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* assert CS */ + shift_srom_bits(sc, 7, 4); /* issue erase cmd */ + shift_srom_bits(sc, addr, 6); /* issue address */ + csr &= ~TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* deassert CS */ + + /* Issue write command. */ + csr |= TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* assert CS */ + for (i=0; i<10; i++) /* 100 ms max wait */ + if ((READ_CSR(TLP_SROM_MII) & TLP_SROM_DOUT)==0) SLEEP(10000); + shift_srom_bits(sc, 5, 4); /* issue write cmd */ + shift_srom_bits(sc, addr, 6); /* issue address */ + shift_srom_bits(sc, data, 16); /* issue data */ + csr &= ~TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* deassert CS */ + + /* Issue write-disable command. */ + csr |= TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* assert CS */ + for (i=0; i<10; i++) /* 100 ms max wait */ + if ((READ_CSR(TLP_SROM_MII) & TLP_SROM_DOUT)==0) SLEEP(10000); + shift_srom_bits(sc, 4, 4); /* issue write disable cmd */ + shift_srom_bits(sc, 0, 6); /* issue address */ + csr &= ~TLP_SROM_CS; + WRITE_CSR(TLP_SROM_MII, csr); /* deassert CS */ + + /* Disable SROM access. */ + WRITE_CSR(TLP_SROM_MII, TLP_MII_MDOE); + } + +/* Not all boards have BIOS roms. */ +/* The BIOS ROM is an AMD 29F010 1Mbit (128K by 8) EEPROM. */ +static u_int8_t +read_bios(softc_t *sc, u_int32_t addr) + { + u_int32_t srom_mii; + + /* Load the BIOS rom address register. */ + WRITE_CSR(TLP_BIOS_ROM, addr); + + /* Enable the BIOS rom. */ + srom_mii = TLP_BIOS_SEL | TLP_BIOS_RD | TLP_MII_MDOE; + WRITE_CSR(TLP_SROM_MII, srom_mii); + + /* Wait at least 20 PCI cycles. */ + DELAY(20); + + /* Read the BIOS rom data. */ + srom_mii = READ_CSR(TLP_SROM_MII); + + /* Disable the BIOS rom. */ + WRITE_CSR(TLP_SROM_MII, TLP_MII_MDOE); + + return (u_int8_t)srom_mii & 0xFF; + } + +static void +write_bios_phys(softc_t *sc, u_int32_t addr, u_int8_t data) + { + u_int32_t srom_mii; + + /* Load the BIOS rom address register. */ + WRITE_CSR(TLP_BIOS_ROM, addr); + + /* Enable the BIOS rom. */ + srom_mii = TLP_BIOS_SEL | TLP_BIOS_WR | TLP_MII_MDOE; + + /* Load the data into the data register. */ + srom_mii = (srom_mii & 0xFFFFFF00) | (data & 0xFF); + WRITE_CSR(TLP_SROM_MII, srom_mii); + + /* Wait at least 20 PCI cycles. */ + DELAY(20); + + /* Disable the BIOS rom. */ + WRITE_CSR(TLP_SROM_MII, TLP_MII_MDOE); + } + +/* IOCTL SYSCALL: can sleep. */ +static void +write_bios(softc_t *sc, u_int32_t addr, u_int8_t data) + { + u_int8_t read_data; + + /* this sequence enables writing */ + write_bios_phys(sc, 0x5555, 0xAA); + write_bios_phys(sc, 0x2AAA, 0x55); + write_bios_phys(sc, 0x5555, 0xA0); + write_bios_phys(sc, addr, data); + + /* Wait for the write operation to complete. */ + for (;;) /* interruptable syscall */ + { + for (;;) + { + read_data = read_bios(sc, addr); + if ((read_data & 0x80) == (data & 0x80)) break; + if (read_data & 0x20) + { /* Data sheet says read it again. */ + read_data = read_bios(sc, addr); + if ((read_data & 0x80) == (data & 0x80)) break; + if (DRIVER_DEBUG) + printf("%s: write_bios() failed; rom addr=0x%x\n", + NAME_UNIT, addr); + return; + } + } + read_data = read_bios(sc, addr); + if (read_data == data) break; + } + } + +/* IOCTL SYSCALL: can sleep. */ +static void +erase_bios(softc_t *sc) + { + unsigned char read_data; + + /* This sequence enables erasing: */ + write_bios_phys(sc, 0x5555, 0xAA); + write_bios_phys(sc, 0x2AAA, 0x55); + write_bios_phys(sc, 0x5555, 0x80); + write_bios_phys(sc, 0x5555, 0xAA); + write_bios_phys(sc, 0x2AAA, 0x55); + write_bios_phys(sc, 0x5555, 0x10); + + /* Wait for the erase operation to complete. */ + for (;;) /* interruptable syscall */ + { + for (;;) + { + read_data = read_bios(sc, 0); + if (read_data & 0x80) break; + if (read_data & 0x20) + { /* Data sheet says read it again. */ + read_data = read_bios(sc, 0); + if (read_data & 0x80) break; + if (DRIVER_DEBUG) + printf("%s: erase_bios() failed\n", NAME_UNIT); + return; + } + } + read_data = read_bios(sc, 0); + if (read_data == 0xFF) break; + } + } + +/* MDIO is 3-stated between tranactions. */ +/* MDIO is set up before the RISING edge of MDC; MDC is parked low. */ +static void +shift_mii_bits(softc_t *sc, u_int32_t data, u_int32_t len) + { + u_int32_t csr = READ_CSR(TLP_SROM_MII); + for (; len>0; len--) + { /* MSB first */ + if (data & (1<<(len-1))) + csr |= TLP_MII_MDOUT; /* MDOUT setup */ + else + csr &= ~TLP_MII_MDOUT; /* MDOUT setup */ + WRITE_CSR(TLP_SROM_MII, csr); + csr |= TLP_MII_MDC; /* MDC rising edge */ + WRITE_CSR(TLP_SROM_MII, csr); + csr &= ~TLP_MII_MDC; /* MDC falling edge */ + WRITE_CSR(TLP_SROM_MII, csr); + } + } + +/* The specification for the MII is IEEE Std 802.3 clause 22. */ +/* MDIO is sampled on the RISING edge of MDC; MDC is parked low. */ +static u_int16_t +read_mii(softc_t *sc, u_int8_t regad) + { + int i; + u_int32_t csr; + u_int16_t data = 0; + + WRITE_CSR(TLP_SROM_MII, TLP_MII_MDOUT); + + shift_mii_bits(sc, 0xFFFFF, 20); /* preamble */ + shift_mii_bits(sc, 0xFFFFF, 20); /* preamble */ + shift_mii_bits(sc, 1, 2); /* start symbol */ + shift_mii_bits(sc, 2, 2); /* read op */ + shift_mii_bits(sc, 0, 5); /* phyad=0 */ + shift_mii_bits(sc, regad, 5); /* regad */ + csr = READ_CSR(TLP_SROM_MII); + csr |= TLP_MII_MDOE; + WRITE_CSR(TLP_SROM_MII, csr); + shift_mii_bits(sc, 0, 2); /* turn-around */ + for (i=15; i>=0; i--) /* data */ + { /* MSB first */ + csr = READ_CSR(TLP_SROM_MII); /* MDIN sampled */ + data = (data<<1) | ((csr & TLP_MII_MDIN) ? 1:0); + csr |= TLP_MII_MDC; /* MDC rising edge */ + WRITE_CSR(TLP_SROM_MII, csr); + csr &= ~TLP_MII_MDC; /* MDC falling edge */ + WRITE_CSR(TLP_SROM_MII, csr); + } + return data; + } + +static void +write_mii(softc_t *sc, u_int8_t regad, u_int16_t data) + { + WRITE_CSR(TLP_SROM_MII, TLP_MII_MDOUT); + shift_mii_bits(sc, 0xFFFFF, 20); /* preamble */ + shift_mii_bits(sc, 0xFFFFF, 20); /* preamble */ + shift_mii_bits(sc, 1, 2); /* start symbol */ + shift_mii_bits(sc, 1, 2); /* write op */ + shift_mii_bits(sc, 0, 5); /* phyad=0 */ + shift_mii_bits(sc, regad, 5); /* regad */ + shift_mii_bits(sc, 2, 2); /* turn-around */ + shift_mii_bits(sc, data, 16); /* data */ + WRITE_CSR(TLP_SROM_MII, TLP_MII_MDOE); + if (regad == 16) sc->led_state = data; /* a small optimization */ + } + +static void +set_mii16_bits(softc_t *sc, u_int16_t bits) + { + u_int16_t mii16 = read_mii(sc, 16); + mii16 |= bits; + write_mii(sc, 16, mii16); + } + +static void +clr_mii16_bits(softc_t *sc, u_int16_t bits) + { + u_int16_t mii16 = read_mii(sc, 16); + mii16 &= ~bits; + write_mii(sc, 16, mii16); + } + +static void +set_mii17_bits(softc_t *sc, u_int16_t bits) + { + u_int16_t mii17 = read_mii(sc, 17); + mii17 |= bits; + write_mii(sc, 17, mii17); + } + +static void +clr_mii17_bits(softc_t *sc, u_int16_t bits) + { + u_int16_t mii17 = read_mii(sc, 17); + mii17 &= ~bits; + write_mii(sc, 17, mii17); + } + +/* + * Watchdog code is more readable if it refreshes LEDs + * once a second whether they need it or not. + * But MII refs take 150 uSecs each, so remember the last value + * written to MII16 and avoid LED writes that do nothing. + */ + +static void +led_off(softc_t *sc, u_int16_t led) + { + if ((led & sc->led_state) == led) return; + set_mii16_bits(sc, led); + } + +static void +led_on(softc_t *sc, u_int16_t led) + { + if ((led & sc->led_state) == 0) return; + clr_mii16_bits(sc, led); + } + +static void +led_inv(softc_t *sc, u_int16_t led) + { + u_int16_t mii16 = read_mii(sc, 16); + mii16 ^= led; + write_mii(sc, 16, mii16); + } + +/* + * T1 & T3 framer registers are accessed through MII regs 17 & 18. + * Write the address to MII reg 17 then R/W data through MII reg 18. + * The hardware interface is an Intel-style 8-bit muxed A/D bus. + */ +static void +write_framer(softc_t *sc, u_int16_t addr, u_int8_t data) + { + write_mii(sc, 17, addr); + write_mii(sc, 18, data); + } + +static u_int8_t +read_framer(softc_t *sc, u_int16_t addr) + { + write_mii(sc, 17, addr); + return (u_int8_t)read_mii(sc, 18); + } + +/* Tulip's hardware implementation of General Purpose IO + * (GPIO) pins makes life difficult for software. + * Bits 7-0 in the Tulip GPIO CSR are used for two purposes + * depending on the state of bit 8. + * If bit 8 is 0 then bits 7-0 are "data" bits. + * If bit 8 is 1 then bits 7-0 are "direction" bits. + * If a direction bit is one, the data bit is an output. + * The problem is that the direction bits are WRITE-ONLY. + * Software must remember the direction bits in a shadow copy. + * (sc->gpio_dir) in order to change some but not all of the bits. + * All accesses to the Tulip GPIO register use these five procedures. + */ + +static void +make_gpio_input(softc_t *sc, u_int32_t bits) + { + sc->gpio_dir &= ~bits; + WRITE_CSR(TLP_GPIO, TLP_GPIO_DIR | (sc->gpio_dir)); + } + +static void +make_gpio_output(softc_t *sc, u_int32_t bits) + { + sc->gpio_dir |= bits; + WRITE_CSR(TLP_GPIO, TLP_GPIO_DIR | (sc->gpio_dir)); + } + +static u_int32_t +read_gpio(softc_t *sc) + { + return READ_CSR(TLP_GPIO); + } + +static void +set_gpio_bits(softc_t *sc, u_int32_t bits) + { + WRITE_CSR(TLP_GPIO, (read_gpio(sc) | bits) & 0xFF); + } + +static void +clr_gpio_bits(softc_t *sc, u_int32_t bits) + { + WRITE_CSR(TLP_GPIO, (read_gpio(sc) & ~bits) & 0xFF); + } + +/* Reset ALL of the flip-flops in the gate array to zero. */ +/* This does NOT change the gate array programming. */ +/* Called during initialization so it must not sleep. */ +static void +reset_xilinx(softc_t *sc) + { + /* Drive RESET low to force initialization. */ + clr_gpio_bits(sc, GPIO_RESET); + make_gpio_output(sc, GPIO_RESET); + + /* Hold RESET low for more than 10 uSec. */ + DELAY(50); + + /* Done with RESET; make it an input. */ + make_gpio_input(sc, GPIO_RESET); + } + +/* Load Xilinx gate array program from on-board rom. */ +/* This changes the gate array programming. */ +/* IOCTL SYSCALL: can sleep. */ +static void +load_xilinx_from_rom(softc_t *sc) + { + int i; + + /* Drive MODE low to load from ROM rather than GPIO. */ + clr_gpio_bits(sc, GPIO_MODE); + make_gpio_output(sc, GPIO_MODE); + + /* Drive DP & RESET low to force configuration. */ + clr_gpio_bits(sc, GPIO_RESET | GPIO_DP); + make_gpio_output(sc, GPIO_RESET | GPIO_DP); + + /* Hold RESET & DP low for more than 10 uSec. */ + DELAY(50); + + /* Done with RESET & DP; make them inputs. */ + make_gpio_input(sc, GPIO_DP | GPIO_RESET); + + /* BUSY-WAIT for Xilinx chip to configure itself from ROM bits. */ + for (i=0; i<100; i++) /* 1 sec max delay */ + if ((read_gpio(sc) & GPIO_DP) == 0) SLEEP(10000); + + /* Done with MODE; make it an input. */ + make_gpio_input(sc, GPIO_MODE); + } + +/* Load the Xilinx gate array program from userland bits. */ +/* This changes the gate array programming. */ +/* IOCTL SYSCALL: can sleep. */ +static int +load_xilinx_from_file(softc_t *sc, char *addr, u_int32_t len) + { + char *data; + int i, j, error; + + /* Get some pages to hold the Xilinx bits; biggest file is < 6 KB. */ + if (len > 8192) return EFBIG; /* too big */ + data = malloc(len, M_DEVBUF, M_WAITOK); + if (data == NULL) return ENOMEM; + + /* Copy the Xilinx bits from userland. */ + if ((error = copyin(addr, data, len))) + { + free(data, M_DEVBUF); + return error; + } + + /* Drive MODE high to load from GPIO rather than ROM. */ + set_gpio_bits(sc, GPIO_MODE); + make_gpio_output(sc, GPIO_MODE); + + /* Drive DP & RESET low to force configuration. */ + clr_gpio_bits(sc, GPIO_RESET | GPIO_DP); + make_gpio_output(sc, GPIO_RESET | GPIO_DP); + + /* Hold RESET & DP low for more than 10 uSec. */ + DELAY(50); + + /* Done with RESET & DP; make them inputs. */ + make_gpio_input(sc, GPIO_RESET | GPIO_DP); + + /* BUSY-WAIT for Xilinx chip to clear its config memory. */ + make_gpio_input(sc, GPIO_INIT); + for (i=0; i<10000; i++) /* 1 sec max delay */ + if ((read_gpio(sc) & GPIO_INIT)==0) SLEEP(10000); + + /* Configure CLK and DATA as outputs. */ + set_gpio_bits(sc, GPIO_CLK); /* park CLK high */ + make_gpio_output(sc, GPIO_CLK | GPIO_DATA); + + /* Write bits to Xilinx; CLK is parked HIGH. */ + /* DATA is set up before the RISING edge of CLK. */ + for (i=0; i<len; i++) + for (j=0; j<8; j++) + { /* LSB first */ + if ((data[i] & (1<<j)) != 0) + set_gpio_bits(sc, GPIO_DATA); /* DATA setup */ + else + clr_gpio_bits(sc, GPIO_DATA); /* DATA setup */ + clr_gpio_bits(sc, GPIO_CLK); /* CLK falling edge */ + set_gpio_bits(sc, GPIO_CLK); /* CLK rising edge */ + } + + /* Stop driving all Xilinx-related signals. */ + /* Pullup and pulldown resistors take over. */ + make_gpio_input(sc, GPIO_CLK | GPIO_DATA | GPIO_MODE); + + free(data, M_DEVBUF); + return 0; + } + +/* Write fragments of a command into the synthesized oscillator. */ +/* DATA is set up before the RISING edge of CLK. CLK is parked low. */ +static void +shift_synth_bits(softc_t *sc, u_int32_t data, u_int32_t len) + { + int i; + + for (i=0; i<len; i++) + { /* LSB first */ + if ((data & (1<<i)) != 0) + set_gpio_bits(sc, GPIO_DATA); /* DATA setup */ + else + clr_gpio_bits(sc, GPIO_DATA); /* DATA setup */ + set_gpio_bits(sc, GPIO_CLK); /* CLK rising edge */ + clr_gpio_bits(sc, GPIO_CLK); /* CLK falling edge */ + } + } + +/* Write a command to the synthesized oscillator on SSI and HSSIc. */ +static void +write_synth(softc_t *sc, struct synth *synth) + { + /* SSI cards have a programmable prescaler */ + if (sc->status.card_type == TLP_CSID_SSI) + { + if (synth->prescale == 9) /* divide by 512 */ + set_mii17_bits(sc, MII17_SSI_PRESCALE); + else /* divide by 32 */ + clr_mii17_bits(sc, MII17_SSI_PRESCALE); + } + + clr_gpio_bits(sc, GPIO_DATA | GPIO_CLK); + make_gpio_output(sc, GPIO_DATA | GPIO_CLK); + + /* SYNTH is a low-true chip enable for the AV9110 chip. */ + set_gpio_bits(sc, GPIO_SSI_SYNTH); + make_gpio_output(sc, GPIO_SSI_SYNTH); + clr_gpio_bits(sc, GPIO_SSI_SYNTH); + + /* Serially shift the command into the AV9110 chip. */ + shift_synth_bits(sc, synth->n, 7); + shift_synth_bits(sc, synth->m, 7); + shift_synth_bits(sc, synth->v, 1); + shift_synth_bits(sc, synth->x, 2); + shift_synth_bits(sc, synth->r, 2); + shift_synth_bits(sc, 0x16, 5); /* enable clk/x output */ + + /* SYNTH (chip enable) going high ends the command. */ + set_gpio_bits(sc, GPIO_SSI_SYNTH); + make_gpio_input(sc, GPIO_SSI_SYNTH); + + /* Stop driving serial-related signals; pullups/pulldowns take over. */ + make_gpio_input(sc, GPIO_DATA | GPIO_CLK); + + /* remember the new synthesizer parameters */ + if (&sc->config.synth != synth) sc->config.synth = *synth; + } + +/* Write a command to the DAC controlling the VCXO on some T3 adapters. */ +/* The DAC is a TI-TLV5636: 12-bit resolution and a serial interface. */ +/* DATA is set up before the FALLING edge of CLK. CLK is parked HIGH. */ +static void +write_dac(softc_t *sc, u_int16_t data) + { + int i; + + /* Prepare to use DATA and CLK. */ + set_gpio_bits(sc, GPIO_DATA | GPIO_CLK); + make_gpio_output(sc, GPIO_DATA | GPIO_CLK); + + /* High-to-low transition prepares DAC for new value. */ + set_gpio_bits(sc, GPIO_T3_DAC); + make_gpio_output(sc, GPIO_T3_DAC); + clr_gpio_bits(sc, GPIO_T3_DAC); + + /* Serially shift command bits into DAC. */ + for (i=0; i<16; i++) + { /* MSB first */ + if ((data & (1<<(15-i))) != 0) + set_gpio_bits(sc, GPIO_DATA); /* DATA setup */ + else + clr_gpio_bits(sc, GPIO_DATA); /* DATA setup */ + clr_gpio_bits(sc, GPIO_CLK); /* CLK falling edge */ + set_gpio_bits(sc, GPIO_CLK); /* CLK rising edge */ + } + + /* Done with DAC; make it an input; loads new value into DAC. */ + set_gpio_bits(sc, GPIO_T3_DAC); + make_gpio_input(sc, GPIO_T3_DAC); + + /* Stop driving serial-related signals; pullups/pulldowns take over. */ + make_gpio_input(sc, GPIO_DATA | GPIO_CLK); + } + +/* begin HSSI card code */ + +/* Must not sleep. */ +static void +hssi_config(softc_t *sc) + { + if (sc->status.card_type == 0) + { /* defaults */ + sc->status.card_type = READ_PCI_CFG(sc, TLP_CSID); + sc->config.crc_len = CFG_CRC_16; + sc->config.loop_back = CFG_LOOP_NONE; + sc->config.tx_clk_src = CFG_CLKMUX_ST; + sc->config.dte_dce = CFG_DTE; + sc->config.synth.n = 52; /* 52.000 Mbs */ + sc->config.synth.m = 5; + sc->config.synth.v = 0; + sc->config.synth.x = 0; + sc->config.synth.r = 0; + sc->config.synth.prescale = 2; + } + + /* set CRC length */ + if (sc->config.crc_len == CFG_CRC_32) + set_mii16_bits(sc, MII16_HSSI_CRC32); + else + clr_mii16_bits(sc, MII16_HSSI_CRC32); + + /* Assert pin LA in HSSI conn: ask modem for local loop. */ + if (sc->config.loop_back == CFG_LOOP_LL) + set_mii16_bits(sc, MII16_HSSI_LA); + else + clr_mii16_bits(sc, MII16_HSSI_LA); + + /* Assert pin LB in HSSI conn: ask modem for remote loop. */ + if (sc->config.loop_back == CFG_LOOP_RL) + set_mii16_bits(sc, MII16_HSSI_LB); + else + clr_mii16_bits(sc, MII16_HSSI_LB); + + if (sc->status.card_type == TLP_CSID_HSSI) + { + /* set TXCLK src */ + if (sc->config.tx_clk_src == CFG_CLKMUX_ST) + set_gpio_bits(sc, GPIO_HSSI_TXCLK); + else + clr_gpio_bits(sc, GPIO_HSSI_TXCLK); + make_gpio_output(sc, GPIO_HSSI_TXCLK); + } + else if (sc->status.card_type == TLP_CSID_HSSIc) + { /* cPCI HSSI rev C has extra features */ + /* Set TXCLK source. */ + u_int16_t mii16 = read_mii(sc, 16); + mii16 &= ~MII16_HSSI_CLKMUX; + mii16 |= (sc->config.tx_clk_src&3)<<13; + write_mii(sc, 16, mii16); + + /* cPCI HSSI implements loopback towards the net. */ + if (sc->config.loop_back == CFG_LOOP_LINE) + set_mii16_bits(sc, MII16_HSSI_LOOP); + else + clr_mii16_bits(sc, MII16_HSSI_LOOP); + + /* Set DTE/DCE mode. */ + if (sc->config.dte_dce == CFG_DCE) + set_gpio_bits(sc, GPIO_HSSI_DCE); + else + clr_gpio_bits(sc, GPIO_HSSI_DCE); + make_gpio_output(sc, GPIO_HSSI_DCE); + + /* Program the synthesized oscillator. */ + write_synth(sc, &sc->config.synth); + } + } + +static void +hssi_ident(softc_t *sc) + { + } + +/* Called once a second; must not sleep. */ +static int +hssi_watchdog(softc_t *sc) + { + u_int16_t mii16 = read_mii(sc, 16) & MII16_HSSI_MODEM; + int link_status = STATUS_UP; + + led_inv(sc, MII16_HSSI_LED_UL); /* Software is alive. */ + led_on(sc, MII16_HSSI_LED_LL); /* always on (SSI cable) */ + + /* Check the transmit clock. */ + if (sc->status.tx_speed == 0) + { + led_on(sc, MII16_HSSI_LED_UR); + link_status = STATUS_DOWN; + } + else + led_off(sc, MII16_HSSI_LED_UR); + + /* Is the modem ready? */ + if ((mii16 & MII16_HSSI_CA) == 0) + { + led_off(sc, MII16_HSSI_LED_LR); + link_status = STATUS_DOWN; + } + else + led_on(sc, MII16_HSSI_LED_LR); + + /* Print the modem control signals if they changed. */ + if ((DRIVER_DEBUG) && (mii16 != sc->last_mii16)) + { + char *on = "ON ", *off = "OFF"; + printf("%s: TA=%s CA=%s LA=%s LB=%s LC=%s TM=%s\n", NAME_UNIT, + (mii16 & MII16_HSSI_TA) ? on : off, + (mii16 & MII16_HSSI_CA) ? on : off, + (mii16 & MII16_HSSI_LA) ? on : off, + (mii16 & MII16_HSSI_LB) ? on : off, + (mii16 & MII16_HSSI_LC) ? on : off, + (mii16 & MII16_HSSI_TM) ? on : off); + } + + /* SNMP one-second-report */ + sc->status.snmp.hssi.sigs = mii16 & MII16_HSSI_MODEM; + + /* Remember this state until next time. */ + sc->last_mii16 = mii16; + + /* If a loop back is in effect, link status is UP */ + if (sc->config.loop_back != CFG_LOOP_NONE) + link_status = STATUS_UP; + + return link_status; + } + +/* IOCTL SYSCALL: can sleep (but doesn't). */ +static int +hssi_ioctl(softc_t *sc, struct ioctl *ioctl) + { + int error = 0; + + if (ioctl->cmd == IOCTL_SNMP_SIGS) + { + u_int16_t mii16 = read_mii(sc, 16); + mii16 &= ~MII16_HSSI_MODEM; + mii16 |= (MII16_HSSI_MODEM & ioctl->data); + write_mii(sc, 16, mii16); + } + else if (ioctl->cmd == IOCTL_SET_STATUS) + { + if (ioctl->data != 0) + set_mii16_bits(sc, MII16_HSSI_TA); + else + clr_mii16_bits(sc, MII16_HSSI_TA); + } + else + error = EINVAL; + + return error; + } + +/* begin DS3 card code */ + +/* Must not sleep. */ +static void +t3_config(softc_t *sc) + { + int i; + u_int8_t ctl1; + + if (sc->status.card_type == 0) + { /* defaults */ + sc->status.card_type = TLP_CSID_T3; + sc->config.crc_len = CFG_CRC_16; + sc->config.loop_back = CFG_LOOP_NONE; + sc->config.format = CFG_FORMAT_T3CPAR; + sc->config.cable_len = 10; /* meters */ + sc->config.scrambler = CFG_SCRAM_DL_KEN; + sc->config.tx_clk_src = CFG_CLKMUX_INT; + + /* Center the VCXO -- get within 20 PPM of 44736000. */ + write_dac(sc, 0x9002); /* set Vref = 2.048 volts */ + write_dac(sc, 2048); /* range is 0..4095 */ + } + + /* Set cable length. */ + if (sc->config.cable_len > 30) + clr_mii16_bits(sc, MII16_DS3_ZERO); + else + set_mii16_bits(sc, MII16_DS3_ZERO); + + /* Set payload scrambler polynomial. */ + if (sc->config.scrambler == CFG_SCRAM_LARS) + set_mii16_bits(sc, MII16_DS3_POLY); + else + clr_mii16_bits(sc, MII16_DS3_POLY); + + /* Set payload scrambler on/off. */ + if (sc->config.scrambler == CFG_SCRAM_OFF) + clr_mii16_bits(sc, MII16_DS3_SCRAM); + else + set_mii16_bits(sc, MII16_DS3_SCRAM); + + /* Set CRC length. */ + if (sc->config.crc_len == CFG_CRC_32) + set_mii16_bits(sc, MII16_DS3_CRC32); + else + clr_mii16_bits(sc, MII16_DS3_CRC32); + + /* Loopback towards host thru the line interface. */ + if (sc->config.loop_back == CFG_LOOP_OTHER) + set_mii16_bits(sc, MII16_DS3_TRLBK); + else + clr_mii16_bits(sc, MII16_DS3_TRLBK); + + /* Loopback towards network thru the line interface. */ + if (sc->config.loop_back == CFG_LOOP_LINE) + set_mii16_bits(sc, MII16_DS3_LNLBK); + else if (sc->config.loop_back == CFG_LOOP_DUAL) + set_mii16_bits(sc, MII16_DS3_LNLBK); + else + clr_mii16_bits(sc, MII16_DS3_LNLBK); + + /* Configure T3 framer chip; write EVERY writeable register. */ + ctl1 = CTL1_SER | CTL1_XTX; + if (sc->config.loop_back == CFG_LOOP_INWARD) ctl1 |= CTL1_3LOOP; + if (sc->config.loop_back == CFG_LOOP_DUAL) ctl1 |= CTL1_3LOOP; + if (sc->config.format == CFG_FORMAT_T3M13) ctl1 |= CTL1_M13MODE; + write_framer(sc, T3CSR_CTL1, ctl1); + write_framer(sc, T3CSR_TX_FEAC, CTL5_EMODE); + write_framer(sc, T3CSR_CTL8, CTL8_FBEC); + write_framer(sc, T3CSR_CTL12, CTL12_DLCB1 | CTL12_C21 | CTL12_MCB1); + write_framer(sc, T3CSR_DBL_FEAC, 0); + write_framer(sc, T3CSR_CTL14, CTL14_RGCEN | CTL14_TGCEN); + write_framer(sc, T3CSR_INTEN, 0); + write_framer(sc, T3CSR_CTL20, CTL20_CVEN); + + /* Clear error counters and latched error bits */ + /* that may have happened while initializing. */ + for (i=0; i<21; i++) read_framer(sc, i); + } + +static void +t3_ident(softc_t *sc) + { + printf(", TXC03401 rev B"); + } + +/* Called once a second; must not sleep. */ +static int +t3_watchdog(softc_t *sc) + { + u_int16_t CV; + u_int8_t CERR, PERR, MERR, FERR, FEBE; + u_int8_t ctl1, stat16, feac; + int link_status = STATUS_UP; + u_int16_t mii16; + + /* Read the alarm registers. */ + ctl1 = read_framer(sc, T3CSR_CTL1); + stat16 = read_framer(sc, T3CSR_STAT16); + mii16 = read_mii(sc, 16); + + /* Always ignore the RTLOC alarm bit. */ + stat16 &= ~STAT16_RTLOC; + + /* Software is alive. */ + led_inv(sc, MII16_DS3_LED_GRN); + + /* Receiving Alarm Indication Signal (AIS). */ + if ((stat16 & STAT16_RAIS) != 0) /* receiving ais */ + led_on(sc, MII16_DS3_LED_BLU); + else if (ctl1 & CTL1_TXAIS) /* sending ais */ + led_inv(sc, MII16_DS3_LED_BLU); + else + led_off(sc, MII16_DS3_LED_BLU); + + /* Receiving Remote Alarm Indication (RAI). */ + if ((stat16 & STAT16_XERR) != 0) /* receiving rai */ + led_on(sc, MII16_DS3_LED_YEL); + else if ((ctl1 & CTL1_XTX) == 0) /* sending rai */ + led_inv(sc, MII16_DS3_LED_YEL); + else + led_off(sc, MII16_DS3_LED_YEL); + + /* If certain status bits are set then the link is 'down'. */ + /* The bad bits are: rxlos rxoof rxais rxidl xerr. */ + if ((stat16 & ~(STAT16_FEAC | STAT16_SEF)) != 0) + link_status = STATUS_DOWN; + + /* Declare local Red Alarm if the link is down. */ + if (link_status == STATUS_DOWN) + led_on(sc, MII16_DS3_LED_RED); + else if (sc->loop_timer != 0) /* loopback is active */ + led_inv(sc, MII16_DS3_LED_RED); + else + led_off(sc, MII16_DS3_LED_RED); + + /* Print latched error bits if they changed. */ + if ((DRIVER_DEBUG) && ((stat16 & ~STAT16_FEAC) != sc->last_stat16)) + { + char *on = "ON ", *off = "OFF"; + printf("%s: RLOS=%s ROOF=%s RAIS=%s RIDL=%s SEF=%s XERR=%s\n", + NAME_UNIT, + (stat16 & STAT16_RLOS) ? on : off, + (stat16 & STAT16_ROOF) ? on : off, + (stat16 & STAT16_RAIS) ? on : off, + (stat16 & STAT16_RIDL) ? on : off, + (stat16 & STAT16_SEF) ? on : off, + (stat16 & STAT16_XERR) ? on : off); + } + + /* Check and print error counters if non-zero. */ + CV = read_framer(sc, T3CSR_CVHI)<<8; + CV += read_framer(sc, T3CSR_CVLO); + PERR = read_framer(sc, T3CSR_PERR); + CERR = read_framer(sc, T3CSR_CERR); + FERR = read_framer(sc, T3CSR_FERR); + MERR = read_framer(sc, T3CSR_MERR); + FEBE = read_framer(sc, T3CSR_FEBE); + + /* CV is invalid during LOS. */ + if ((stat16 & STAT16_RLOS)!=0) CV = 0; + /* CERR & FEBE are invalid in M13 mode */ + if (sc->config.format == CFG_FORMAT_T3M13) CERR = FEBE = 0; + /* FEBE is invalid during AIS. */ + if ((stat16 & STAT16_RAIS)!=0) FEBE = 0; + if (DRIVER_DEBUG && (CV || PERR || CERR || FERR || MERR || FEBE)) + printf("%s: CV=%u PERR=%u CERR=%u FERR=%u MERR=%u FEBE=%u\n", + NAME_UNIT, CV, PERR, CERR, FERR, MERR, FEBE); + + /* Driver keeps crude link-level error counters (SNMP is better). */ + sc->status.cntrs.lcv_errs += CV; + sc->status.cntrs.par_errs += PERR; + sc->status.cntrs.cpar_errs += CERR; + sc->status.cntrs.frm_errs += FERR; + sc->status.cntrs.mfrm_errs += MERR; + sc->status.cntrs.febe_errs += FEBE; + + /* Check for FEAC messages (FEAC not defined in M13 mode). */ + if (FORMAT_T3CPAR && (stat16 & STAT16_FEAC)) do + { + feac = read_framer(sc, T3CSR_FEAC_STK); + if ((feac & FEAC_STK_VALID)==0) break; + /* Ignore RxFEACs while a far end loopback has been requested. */ + if ((sc->status.snmp.t3.line & TLOOP_FAR_LINE)!=0) continue; + switch (feac & FEAC_STK_FEAC) + { + case T3BOP_LINE_UP: break; + case T3BOP_LINE_DOWN: break; + case T3BOP_LOOP_DS3: + { + if (sc->last_FEAC == T3BOP_LINE_DOWN) + { + if (DRIVER_DEBUG) + printf("%s: Received a 'line loopback deactivate' FEAC msg\n", NAME_UNIT); + clr_mii16_bits(sc, MII16_DS3_LNLBK); + sc->loop_timer = 0; + } + if (sc->last_FEAC == T3BOP_LINE_UP) + { + if (DRIVER_DEBUG) + printf("%s: Received a 'line loopback activate' FEAC msg\n", NAME_UNIT); + set_mii16_bits(sc, MII16_DS3_LNLBK); + sc->loop_timer = 300; + } + break; + } + case T3BOP_OOF: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'far end LOF' FEAC msg\n", NAME_UNIT); + break; + } + case T3BOP_IDLE: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'far end IDL' FEAC msg\n", NAME_UNIT); + break; + } + case T3BOP_AIS: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'far end AIS' FEAC msg\n", NAME_UNIT); + break; + } + case T3BOP_LOS: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'far end LOS' FEAC msg\n", NAME_UNIT); + break; + } + default: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'type 0x%02X' FEAC msg\n", NAME_UNIT, feac & FEAC_STK_FEAC); + break; + } + } + sc->last_FEAC = feac & FEAC_STK_FEAC; + } while ((feac & FEAC_STK_MORE) != 0); + stat16 &= ~STAT16_FEAC; + + /* Send Service-Affecting priority FEAC messages */ + if (((sc->last_stat16 ^ stat16) & 0xF0) && (FORMAT_T3CPAR)) + { + /* Transmit continuous FEACs */ + write_framer(sc, T3CSR_CTL14, + read_framer(sc, T3CSR_CTL14) & ~CTL14_FEAC10); + if ((stat16 & STAT16_RLOS)!=0) + write_framer(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_LOS); + else if ((stat16 & STAT16_ROOF)!=0) + write_framer(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_OOF); + else if ((stat16 & STAT16_RAIS)!=0) + write_framer(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_AIS); + else if ((stat16 & STAT16_RIDL)!=0) + write_framer(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_IDLE); + else + write_framer(sc, T3CSR_TX_FEAC, CTL5_EMODE); + } + + /* Start sending RAI, Remote Alarm Indication. */ + if (((stat16 & STAT16_ROOF)!=0) && ((stat16 & STAT16_RLOS)==0) && + ((sc->last_stat16 & STAT16_ROOF)==0)) + write_framer(sc, T3CSR_CTL1, ctl1 &= ~CTL1_XTX); + /* Stop sending RAI, Remote Alarm Indication. */ + else if (((stat16 & STAT16_ROOF)==0) && ((sc->last_stat16 & STAT16_ROOF)!=0)) + write_framer(sc, T3CSR_CTL1, ctl1 |= CTL1_XTX); + + /* Start sending AIS, Alarm Indication Signal */ + if (((stat16 & STAT16_RLOS)!=0) && ((sc->last_stat16 & STAT16_RLOS)==0)) + { + set_mii16_bits(sc, MII16_DS3_FRAME); + write_framer(sc, T3CSR_CTL1, ctl1 | CTL1_TXAIS); + } + /* Stop sending AIS, Alarm Indication Signal */ + else if (((stat16 & STAT16_RLOS)==0) && ((sc->last_stat16 & STAT16_RLOS)!=0)) + { + clr_mii16_bits(sc, MII16_DS3_FRAME); + write_framer(sc, T3CSR_CTL1, ctl1 & ~CTL1_TXAIS); + } + + /* Time out loopback requests. */ + if (sc->loop_timer != 0) + if (--sc->loop_timer == 0) + if ((mii16 & MII16_DS3_LNLBK)!=0) + { + if (DRIVER_DEBUG) + printf("%s: Timeout: Loop Down after 300 seconds\n", NAME_UNIT); + clr_mii16_bits(sc, MII16_DS3_LNLBK); /* line loopback off */ + } + + /* SNMP error counters */ + sc->status.snmp.t3.lcv = CV; + sc->status.snmp.t3.pcv = PERR; + sc->status.snmp.t3.ccv = CERR; + sc->status.snmp.t3.febe = FEBE; + + /* SNMP Line Status */ + sc->status.snmp.t3.line = 0; + if ((ctl1 & CTL1_XTX)==0) sc->status.snmp.t3.line |= TLINE_TX_RAI; + if (stat16 & STAT16_XERR) sc->status.snmp.t3.line |= TLINE_RX_RAI; + if (ctl1 & CTL1_TXAIS) sc->status.snmp.t3.line |= TLINE_TX_AIS; + if (stat16 & STAT16_RAIS) sc->status.snmp.t3.line |= TLINE_RX_AIS; + if (stat16 & STAT16_ROOF) sc->status.snmp.t3.line |= TLINE_LOF; + if (stat16 & STAT16_RLOS) sc->status.snmp.t3.line |= TLINE_LOS; + if (stat16 & STAT16_SEF) sc->status.snmp.t3.line |= T3LINE_SEF; + + /* SNMP Loopback Status */ + sc->status.snmp.t3.loop &= ~TLOOP_FAR_LINE; + if (sc->config.loop_back == CFG_LOOP_TULIP) + sc->status.snmp.t3.loop |= TLOOP_NEAR_OTHER; + if (ctl1 & CTL1_3LOOP) sc->status.snmp.t3.loop |= TLOOP_NEAR_INWARD; + if (mii16 & MII16_DS3_TRLBK) sc->status.snmp.t3.loop |= TLOOP_NEAR_OTHER; + if (mii16 & MII16_DS3_LNLBK) sc->status.snmp.t3.loop |= TLOOP_NEAR_LINE; +/*if (ctl12 & CTL12_RTPLOOP) sc->status.snmp.t3.loop |= TLOOP_NEAR_PAYLOAD; */ + + /* Remember this state until next time. */ + sc->last_stat16 = stat16; + + /* If an INWARD loopback is in effect, link status is UP */ + if (sc->config.loop_back != CFG_LOOP_NONE) /* XXX INWARD ONLY */ + link_status = STATUS_UP; + + return link_status; + } + +/* IOCTL SYSCALL: can sleep. */ +static void +t3_send_dbl_feac(softc_t *sc, int feac1, int feac2) + { + u_int8_t tx_feac; + int i; + + /* The FEAC transmitter could be sending a continuous */ + /* FEAC msg when told to send a double FEAC message. */ + /* So save the current state of the FEAC transmitter. */ + tx_feac = read_framer(sc, T3CSR_TX_FEAC); + /* Load second FEAC code and stop FEAC transmitter. */ + write_framer(sc, T3CSR_TX_FEAC, CTL5_EMODE + feac2); + /* FEAC transmitter sends 10 more FEACs and then stops. */ + SLEEP(20000); /* sending one FEAC takes 1700 uSecs */ + /* Load first FEAC code and start FEAC transmitter. */ + write_framer(sc, T3CSR_DBL_FEAC, CTL13_DFEXEC + feac1); + /* Wait for double FEAC sequence to complete -- about 70 ms. */ + for (i=0; i<10; i++) /* max delay 100 ms */ + if (read_framer(sc, T3CSR_DBL_FEAC) & CTL13_DFEXEC) SLEEP(10000); + /* Flush received FEACS; don't respond to our own loop cmd! */ + while (read_framer(sc, T3CSR_FEAC_STK) & FEAC_STK_VALID) DELAY(1); /* XXX HANG */ + /* Restore previous state of the FEAC transmitter. */ + /* If it was sending a continous FEAC, it will resume. */ + write_framer(sc, T3CSR_TX_FEAC, tx_feac); + } + +/* IOCTL SYSCALL: can sleep. */ +static int +t3_ioctl(softc_t *sc, struct ioctl *ioctl) + { + int error = 0; + + switch (ioctl->cmd) + { + case IOCTL_SNMP_SEND: /* set opstatus? */ + { + if (sc->config.format != CFG_FORMAT_T3CPAR) + error = EINVAL; + else if (ioctl->data == TSEND_LINE) + { + sc->status.snmp.t3.loop |= TLOOP_FAR_LINE; + t3_send_dbl_feac(sc, T3BOP_LINE_UP, T3BOP_LOOP_DS3); + } + else if (ioctl->data == TSEND_RESET) + { + t3_send_dbl_feac(sc, T3BOP_LINE_DOWN, T3BOP_LOOP_DS3); + sc->status.snmp.t3.loop &= ~TLOOP_FAR_LINE; + } + else + error = EINVAL; + break; + } + case IOCTL_SNMP_LOOP: /* set opstatus = test? */ + { + if (ioctl->data == CFG_LOOP_NONE) + { + clr_mii16_bits(sc, MII16_DS3_FRAME); + clr_mii16_bits(sc, MII16_DS3_TRLBK); + clr_mii16_bits(sc, MII16_DS3_LNLBK); + write_framer(sc, T3CSR_CTL1, + read_framer(sc, T3CSR_CTL1) & ~CTL1_3LOOP); + write_framer(sc, T3CSR_CTL12, + read_framer(sc, T3CSR_CTL12) & ~(CTL12_RTPLOOP | CTL12_RTPLLEN)); + } + else if (ioctl->data == CFG_LOOP_LINE) + set_mii16_bits(sc, MII16_DS3_LNLBK); + else if (ioctl->data == CFG_LOOP_OTHER) + set_mii16_bits(sc, MII16_DS3_TRLBK); + else if (ioctl->data == CFG_LOOP_INWARD) + write_framer(sc, T3CSR_CTL1, + read_framer(sc, T3CSR_CTL1) | CTL1_3LOOP); + else if (ioctl->data == CFG_LOOP_DUAL) + { + set_mii16_bits(sc, MII16_DS3_LNLBK); + write_framer(sc, T3CSR_CTL1, + read_framer(sc, T3CSR_CTL1) | CTL1_3LOOP); + } + else if (ioctl->data == CFG_LOOP_PAYLOAD) + { + set_mii16_bits(sc, MII16_DS3_FRAME); + write_framer(sc, T3CSR_CTL12, + read_framer(sc, T3CSR_CTL12) | CTL12_RTPLOOP); + write_framer(sc, T3CSR_CTL12, + read_framer(sc, T3CSR_CTL12) | CTL12_RTPLLEN); + DELAY(25); /* at least two frames (22 uS) */ + write_framer(sc, T3CSR_CTL12, + read_framer(sc, T3CSR_CTL12) & ~CTL12_RTPLLEN); + } + else + error = EINVAL; + break; + } + default: + error = EINVAL; + break; + } + + return error; + } + +/* begin SSI card code */ + +/* Must not sleep. */ +static void +ssi_config(softc_t *sc) + { + if (sc->status.card_type == 0) + { /* defaults */ + sc->status.card_type = TLP_CSID_SSI; + sc->config.crc_len = CFG_CRC_16; + sc->config.loop_back = CFG_LOOP_NONE; + sc->config.tx_clk_src = CFG_CLKMUX_ST; + sc->config.dte_dce = CFG_DTE; + sc->config.synth.n = 51; /* 1.536 MHz */ + sc->config.synth.m = 83; + sc->config.synth.v = 1; + sc->config.synth.x = 1; + sc->config.synth.r = 1; + sc->config.synth.prescale = 4; + } + + /* Disable the TX clock driver while programming the oscillator. */ + clr_gpio_bits(sc, GPIO_SSI_DCE); + make_gpio_output(sc, GPIO_SSI_DCE); + + /* Program the synthesized oscillator. */ + write_synth(sc, &sc->config.synth); + + /* Set DTE/DCE mode. */ + /* If DTE mode then DCD & TXC are received. */ + /* If DCE mode then DCD & TXC are driven. */ + /* Boards with MII rev=4.0 don't drive DCD. */ + if (sc->config.dte_dce == CFG_DCE) + set_gpio_bits(sc, GPIO_SSI_DCE); + else + clr_gpio_bits(sc, GPIO_SSI_DCE); + make_gpio_output(sc, GPIO_SSI_DCE); + + /* Set CRC length. */ + if (sc->config.crc_len == CFG_CRC_32) + set_mii16_bits(sc, MII16_SSI_CRC32); + else + clr_mii16_bits(sc, MII16_SSI_CRC32); + + /* Loop towards host thru cable drivers and receivers. */ + /* Asserts DCD at the far end of a null modem cable. */ + if (sc->config.loop_back == CFG_LOOP_PINS) + set_mii16_bits(sc, MII16_SSI_LOOP); + else + clr_mii16_bits(sc, MII16_SSI_LOOP); + + /* Assert pin LL in modem conn: ask modem for local loop. */ + /* Asserts TM at the far end of a null modem cable. */ + if (sc->config.loop_back == CFG_LOOP_LL) + set_mii16_bits(sc, MII16_SSI_LL); + else + clr_mii16_bits(sc, MII16_SSI_LL); + + /* Assert pin RL in modem conn: ask modem for remote loop. */ + if (sc->config.loop_back == CFG_LOOP_RL) + set_mii16_bits(sc, MII16_SSI_RL); + else + clr_mii16_bits(sc, MII16_SSI_RL); + } + +static void +ssi_ident(softc_t *sc) + { + printf(", LTC1343/44"); + } + +/* Called once a second; must not sleep. */ +static int +ssi_watchdog(softc_t *sc) + { + u_int16_t cable; + u_int16_t mii16 = read_mii(sc, 16) & MII16_SSI_MODEM; + int link_status = STATUS_UP; + + /* Software is alive. */ + led_inv(sc, MII16_SSI_LED_UL); + + /* Check the transmit clock. */ + if (sc->status.tx_speed == 0) + { + led_on(sc, MII16_SSI_LED_UR); + link_status = STATUS_DOWN; + } + else + led_off(sc, MII16_SSI_LED_UR); + + /* Check the external cable. */ + cable = read_mii(sc, 17); + cable = cable & MII17_SSI_CABLE_MASK; + cable = cable >> MII17_SSI_CABLE_SHIFT; + if (cable == 7) + { + led_off(sc, MII16_SSI_LED_LL); /* no cable */ + link_status = STATUS_DOWN; + } + else + led_on(sc, MII16_SSI_LED_LL); + + /* The unit at the other end of the cable is ready if: */ + /* DTE mode and DCD pin is asserted */ + /* DCE mode and DSR pin is asserted */ + if (((sc->config.dte_dce == CFG_DTE) && ((mii16 & MII16_SSI_DCD)==0)) || + ((sc->config.dte_dce == CFG_DCE) && ((mii16 & MII16_SSI_DSR)==0))) + { + led_off(sc, MII16_SSI_LED_LR); + link_status = STATUS_DOWN; + } + else + led_on(sc, MII16_SSI_LED_LR); + + if (DRIVER_DEBUG && (cable != sc->status.cable_type)) + printf("%s: SSI cable type changed to '%s'\n", + NAME_UNIT, ssi_cables[cable]); + sc->status.cable_type = cable; + + /* Print the modem control signals if they changed. */ + if ((DRIVER_DEBUG) && (mii16 != sc->last_mii16)) + { + char *on = "ON ", *off = "OFF"; + printf("%s: DTR=%s DSR=%s RTS=%s CTS=%s DCD=%s RI=%s LL=%s RL=%s TM=%s\n", + NAME_UNIT, + (mii16 & MII16_SSI_DTR) ? on : off, + (mii16 & MII16_SSI_DSR) ? on : off, + (mii16 & MII16_SSI_RTS) ? on : off, + (mii16 & MII16_SSI_CTS) ? on : off, + (mii16 & MII16_SSI_DCD) ? on : off, + (mii16 & MII16_SSI_RI) ? on : off, + (mii16 & MII16_SSI_LL) ? on : off, + (mii16 & MII16_SSI_RL) ? on : off, + (mii16 & MII16_SSI_TM) ? on : off); + } + + /* SNMP one-second report */ + sc->status.snmp.ssi.sigs = mii16 & MII16_SSI_MODEM; + + /* Remember this state until next time. */ + sc->last_mii16 = mii16; + + /* If a loop back is in effect, link status is UP */ + if (sc->config.loop_back != CFG_LOOP_NONE) + link_status = STATUS_UP; + + return link_status; + } + +/* IOCTL SYSCALL: can sleep (but doesn't). */ +static int +ssi_ioctl(softc_t *sc, struct ioctl *ioctl) + { + int error = 0; + + if (ioctl->cmd == IOCTL_SNMP_SIGS) + { + u_int16_t mii16 = read_mii(sc, 16); + mii16 &= ~MII16_SSI_MODEM; + mii16 |= (MII16_SSI_MODEM & ioctl->data); + write_mii(sc, 16, mii16); + } + else if (ioctl->cmd == IOCTL_SET_STATUS) + { + if (ioctl->data != 0) + set_mii16_bits(sc, (MII16_SSI_DTR | MII16_SSI_RTS | MII16_SSI_DCD)); + else + clr_mii16_bits(sc, (MII16_SSI_DTR | MII16_SSI_RTS | MII16_SSI_DCD)); + } + else + error = EINVAL; + + return error; + } + +/* begin T1E1 card code */ + +/* Must not sleep. */ +static void +t1_config(softc_t *sc) + { + int i; + u_int8_t pulse, lbo, gain; + + if (sc->status.card_type == 0) + { /* defaults */ + sc->status.card_type = TLP_CSID_T1E1; + sc->config.crc_len = CFG_CRC_16; + sc->config.loop_back = CFG_LOOP_NONE; + sc->config.tx_clk_src = CFG_CLKMUX_INT; + sc->config.format = CFG_FORMAT_T1ESF; + sc->config.cable_len = 10; + sc->config.time_slots = 0x01FFFFFE; + sc->config.tx_pulse = CFG_PULSE_AUTO; + sc->config.rx_gain = CFG_GAIN_AUTO; + sc->config.tx_lbo = CFG_LBO_AUTO; + + /* Bt8370 occasionally powers up in a loopback mode. */ + /* Data sheet says zero LOOP reg and do a s/w reset. */ + write_framer(sc, Bt8370_LOOP, 0x00); /* no loopback */ + write_framer(sc, Bt8370_CR0, 0x80); /* s/w reset */ + for (i=0; i<10; i++) /* max delay 10 ms */ + if (read_framer(sc, Bt8370_CR0) & 0x80) DELAY(1000); + } + + /* Set CRC length. */ + if (sc->config.crc_len == CFG_CRC_32) + set_mii16_bits(sc, MII16_T1_CRC32); + else + clr_mii16_bits(sc, MII16_T1_CRC32); + + /* Invert HDLC payload data in SF/AMI mode. */ + /* HDLC stuff bits satisfy T1 pulse density. */ + if (FORMAT_T1SF) + set_mii16_bits(sc, MII16_T1_INVERT); + else + clr_mii16_bits(sc, MII16_T1_INVERT); + + /* Set the transmitter output impedance. */ + if (FORMAT_E1ANY) set_mii16_bits(sc, MII16_T1_Z); + + /* 001:CR0 -- Control Register 0 - T1/E1 and frame format */ + write_framer(sc, Bt8370_CR0, sc->config.format); + + /* 002:JAT_CR -- Jitter Attenuator Control Register */ + if (sc->config.tx_clk_src == CFG_CLKMUX_RT) /* loop timing */ + write_framer(sc, Bt8370_JAT_CR, 0xA3); /* JAT in RX path */ + else + { /* 64-bit elastic store; free-running JCLK and CLADO */ + write_framer(sc, Bt8370_JAT_CR, 0x4B); /* assert jcenter */ + write_framer(sc, Bt8370_JAT_CR, 0x43); /* release jcenter */ + } + + /* 00C-013:IERn -- Interrupt Enable Registers */ + for (i=Bt8370_IER7; i<=Bt8370_IER0; i++) + write_framer(sc, i, 0); /* no interrupts; polled */ + + /* 014:LOOP -- loopbacks */ + if (sc->config.loop_back == CFG_LOOP_PAYLOAD) + write_framer(sc, Bt8370_LOOP, LOOP_PAYLOAD); + else if (sc->config.loop_back == CFG_LOOP_LINE) + write_framer(sc, Bt8370_LOOP, LOOP_LINE); + else if (sc->config.loop_back == CFG_LOOP_OTHER) + write_framer(sc, Bt8370_LOOP, LOOP_ANALOG); + else if (sc->config.loop_back == CFG_LOOP_INWARD) + write_framer(sc, Bt8370_LOOP, LOOP_FRAMER); + else if (sc->config.loop_back == CFG_LOOP_DUAL) + write_framer(sc, Bt8370_LOOP, LOOP_DUAL); + else + write_framer(sc, Bt8370_LOOP, 0x00); /* no loopback */ + + /* 015:DL3_TS -- Data Link 3 */ + write_framer(sc, Bt8370_DL3_TS, 0x00); /* disabled */ + + /* 018:PIO -- Programmable I/O */ + write_framer(sc, Bt8370_PIO, 0xFF); /* all pins are outputs */ + + /* 019:POE -- Programmable Output Enable */ + write_framer(sc, Bt8370_POE, 0x00); /* all outputs are enabled */ + + /* 01A;CMUX -- Clock Input Mux */ + if (sc->config.tx_clk_src == CFG_CLKMUX_EXT) + write_framer(sc, Bt8370_CMUX, 0x0C); /* external timing */ + else + write_framer(sc, Bt8370_CMUX, 0x0F); /* internal timing */ + + /* 020:LIU_CR -- Line Interface Unit Config Register */ + write_framer(sc, Bt8370_LIU_CR, 0xC1); /* reset LIU, squelch */ + + /* 022:RLIU_CR -- RX Line Interface Unit Config Reg */ + /* Errata sheet says don't use freeze-short, but we do anyway! */ + write_framer(sc, Bt8370_RLIU_CR, 0xB1); /* AGC=2048, Long Eye */ + + /* Select Rx sensitivity based on cable length. */ + if ((gain = sc->config.rx_gain) == CFG_GAIN_AUTO) + { + if (sc->config.cable_len > 2000) + gain = CFG_GAIN_EXTEND; + else if (sc->config.cable_len > 1000) + gain = CFG_GAIN_LONG; + else if (sc->config.cable_len > 100) + gain = CFG_GAIN_MEDIUM; + else + gain = CFG_GAIN_SHORT; + } + + /* 024:VGA_MAX -- Variable Gain Amplifier Max gain */ + write_framer(sc, Bt8370_VGA_MAX, gain); + + /* 028:PRE_EQ -- Pre Equalizer */ + if (gain == CFG_GAIN_EXTEND) + write_framer(sc, Bt8370_PRE_EQ, 0xE6); /* ON; thresh 6 */ + else + write_framer(sc, Bt8370_PRE_EQ, 0xA6); /* OFF; thresh 6 */ + + /* 038-03C:GAINn -- RX Equalizer gain thresholds */ + write_framer(sc, Bt8370_GAIN0, 0x24); + write_framer(sc, Bt8370_GAIN1, 0x28); + write_framer(sc, Bt8370_GAIN2, 0x2C); + write_framer(sc, Bt8370_GAIN3, 0x30); + write_framer(sc, Bt8370_GAIN4, 0x34); + + /* 040:RCR0 -- Receiver Control Register 0 */ + if (FORMAT_T1ESF) + write_framer(sc, Bt8370_RCR0, 0x05); /* B8ZS, 2/5 FErrs */ + else if (FORMAT_T1SF) + write_framer(sc, Bt8370_RCR0, 0x84); /* AMI, 2/5 FErrs */ + else if (FORMAT_E1NONE) + write_framer(sc, Bt8370_RCR0, 0x41); /* HDB3, rabort */ + else if (FORMAT_E1CRC) + write_framer(sc, Bt8370_RCR0, 0x09); /* HDB3, 3 FErrs or 915 CErrs */ + else /* E1 no CRC */ + write_framer(sc, Bt8370_RCR0, 0x19); /* HDB3, 3 FErrs */ + + /* 041:RPATT -- Receive Test Pattern configuration */ + write_framer(sc, Bt8370_RPATT, 0x3E); /* looking for framed QRSS */ + + /* 042:RLB -- Receive Loop Back code detector config */ + write_framer(sc, Bt8370_RLB, 0x09); /* 6 bits down; 5 bits up */ + + /* 043:LBA -- Loop Back Activate code */ + write_framer(sc, Bt8370_LBA, 0x08); /* 10000 10000 10000 ... */ + + /* 044:LBD -- Loop Back Deactivate code */ + write_framer(sc, Bt8370_LBD, 0x24); /* 100100 100100 100100 ... */ + + /* 045:RALM -- Receive Alarm signal configuration */ + write_framer(sc, Bt8370_RALM, 0x0C); /* yel_intg rlof_intg */ + + /* 046:LATCH -- Alarm/Error/Counter Latch register */ + write_framer(sc, Bt8370_LATCH, 0x1F); /* stop_cnt latch_{cnt,err,alm} */ + + /* Select Pulse Shape based on cable length (T1 only). */ + if ((pulse = sc->config.tx_pulse) == CFG_PULSE_AUTO) + { + if (FORMAT_T1ANY) + { + if (sc->config.cable_len > 200) + pulse = CFG_PULSE_T1CSU; + else if (sc->config.cable_len > 160) + pulse = CFG_PULSE_T1DSX4; + else if (sc->config.cable_len > 120) + pulse = CFG_PULSE_T1DSX3; + else if (sc->config.cable_len > 80) + pulse = CFG_PULSE_T1DSX2; + else if (sc->config.cable_len > 40) + pulse = CFG_PULSE_T1DSX1; + else + pulse = CFG_PULSE_T1DSX0; + } + else + pulse = CFG_PULSE_E1TWIST; + } + + /* Select Line Build Out based on cable length (T1CSU only). */ + if ((lbo = sc->config.tx_lbo) == CFG_LBO_AUTO) + { + if (pulse == CFG_PULSE_T1CSU) + { + if (sc->config.cable_len > 1500) + lbo = CFG_LBO_0DB; + else if (sc->config.cable_len > 1000) + lbo = CFG_LBO_7DB; + else if (sc->config.cable_len > 500) + lbo = CFG_LBO_15DB; + else + lbo = CFG_LBO_22DB; + } + else + lbo = 0; + } + + /* 068:TLIU_CR -- Transmit LIU Control Register */ + write_framer(sc, Bt8370_TLIU_CR, (0x40 | (lbo & 0x30) | (pulse & 0x0E))); + + /* 070:TCR0 -- Transmit Framer Configuration */ + write_framer(sc, Bt8370_TCR0, sc->config.format>>1); + + /* 071:TCR1 -- Transmitter Configuration */ + if (FORMAT_T1SF) + write_framer(sc, Bt8370_TCR1, 0x43); /* tabort, AMI PDV enforced */ + else + write_framer(sc, Bt8370_TCR1, 0x41); /* tabort, B8ZS or HDB3 */ + + /* 072:TFRM -- Transmit Frame format MYEL YEL MF FE CRC FBIT */ + if (sc->config.format == CFG_FORMAT_T1ESF) + write_framer(sc, Bt8370_TFRM, 0x0B); /* - YEL MF - CRC FBIT */ + else if (sc->config.format == CFG_FORMAT_T1SF) + write_framer(sc, Bt8370_TFRM, 0x19); /* - YEL MF - - FBIT */ + else if (sc->config.format == CFG_FORMAT_E1FAS) + write_framer(sc, Bt8370_TFRM, 0x11); /* - YEL - - - FBIT */ + else if (sc->config.format == CFG_FORMAT_E1FASCRC) + write_framer(sc, Bt8370_TFRM, 0x1F); /* - YEL MF FE CRC FBIT */ + else if (sc->config.format == CFG_FORMAT_E1FASCAS) + write_framer(sc, Bt8370_TFRM, 0x31); /* MYEL YEL - - - FBIT */ + else if (sc->config.format == CFG_FORMAT_E1FASCRCCAS) + write_framer(sc, Bt8370_TFRM, 0x3F); /* MYEL YEL MF FE CRC FBIT */ + else if (sc->config.format == CFG_FORMAT_E1NONE) + write_framer(sc, Bt8370_TFRM, 0x00); /* NO FRAMING BITS AT ALL! */ + + /* 073:TERROR -- Transmit Error Insert */ + write_framer(sc, Bt8370_TERROR, 0x00); /* no errors, please! */ + + /* 074:TMAN -- Transmit Manual Sa-byte/FEBE configuration */ + write_framer(sc, Bt8370_TMAN, 0x00); /* none */ + + /* 075:TALM -- Transmit Alarm Signal Configuration */ + if (FORMAT_E1ANY) + write_framer(sc, Bt8370_TALM, 0x38); /* auto_myel auto_yel auto_ais */ + else if (FORMAT_T1ANY) + write_framer(sc, Bt8370_TALM, 0x18); /* auto_yel auto_ais */ + + /* 076:TPATT -- Transmit Test Pattern Configuration */ + write_framer(sc, Bt8370_TPATT, 0x00); /* disabled */ + + /* 077:TLB -- Transmit Inband Loopback Code Configuration */ + write_framer(sc, Bt8370_TLB, 0x00); /* disabled */ + + /* 090:CLAD_CR -- Clack Rate Adapter Configuration */ + if (FORMAT_T1ANY) + write_framer(sc, Bt8370_CLAD_CR, 0x06); /* loop filter gain 1/2^6 */ + else + write_framer(sc, Bt8370_CLAD_CR, 0x08); /* loop filter gain 1/2^8 */ + + /* 091:CSEL -- CLAD frequency Select */ + if (FORMAT_T1ANY) + write_framer(sc, Bt8370_CSEL, 0x55); /* 1544 kHz */ + else + write_framer(sc, Bt8370_CSEL, 0x11); /* 2048 kHz */ + + /* 092:CPHASE -- CLAD Phase detector */ + if (FORMAT_T1ANY) + write_framer(sc, Bt8370_CPHASE, 0x22); /* phase compare @ 386 kHz */ + else + write_framer(sc, Bt8370_CPHASE, 0x00); /* phase compare @ 2048 kHz */ + + if (FORMAT_T1ESF) /* BOP & PRM are enabled in T1ESF mode only. */ + { + /* 0A0:BOP -- Bit Oriented Protocol messages */ + write_framer(sc, Bt8370_BOP, RBOP_25 | TBOP_OFF); + /* 0A4:DL1_TS -- Data Link 1 Time Slot Enable */ + write_framer(sc, Bt8370_DL1_TS, 0x40); /* FDL bits in odd frames */ + /* 0A6:DL1_CTL -- Data Link 1 Control */ + write_framer(sc, Bt8370_DL1_CTL, 0x03); /* FCS mode, TX on, RX on */ + /* 0A7:RDL1_FFC -- Rx Data Link 1 Fifo Fill Control */ + write_framer(sc, Bt8370_RDL1_FFC, 0x30); /* assert "near full" at 48 */ + /* 0AA:PRM -- Performance Report Messages */ + write_framer(sc, Bt8370_PRM, 0x80); + } + + /* 0D0:SBI_CR -- System Bus Interface Configuration Register */ + if (FORMAT_T1ANY) + write_framer(sc, Bt8370_SBI_CR, 0x47); /* 1.544 with 24 TS +Fbits */ + else + write_framer(sc, Bt8370_SBI_CR, 0x46); /* 2.048 with 32 TS */ + + /* 0D1:RSB_CR -- Receive System Bus Configuration Register */ + /* Change RINDO & RFSYNC on falling edge of RSBCLKI. */ + write_framer(sc, Bt8370_RSB_CR, 0x70); + + /* 0D2,0D3:RSYNC_{TS,BIT} -- Receive frame Sync offset */ + write_framer(sc, Bt8370_RSYNC_BIT, 0x00); + write_framer(sc, Bt8370_RSYNC_TS, 0x00); + + /* 0D4:TSB_CR -- Transmit System Bus Configuration Register */ + /* Change TINDO & TFSYNC on falling edge of TSBCLKI. */ + write_framer(sc, Bt8370_TSB_CR, 0x30); + + /* 0D5,0D6:TSYNC_{TS,BIT} -- Transmit frame Sync offset */ + write_framer(sc, Bt8370_TSYNC_BIT, 0x00); + write_framer(sc, Bt8370_TSYNC_TS, 0x00); + + /* 0D7:RSIG_CR -- Receive SIGnalling Configuratin Register */ + write_framer(sc, Bt8370_RSIG_CR, 0x00); + + /* Assign and configure 64Kb TIME SLOTS. */ + /* TS24..TS1 must be assigned for T1, TS31..TS0 for E1. */ + /* Timeslots with no user data have RINDO and TINDO off. */ + for (i=0; i<32; i++) + { + /* 0E0-0FF:SBCn -- System Bus Per-Channel Control */ + if (FORMAT_T1ANY && (i==0 || i>24)) + write_framer(sc, Bt8370_SBCn +i, 0x00); /* not assigned in T1 mode */ + else if (FORMAT_E1ANY && (i==0) && !FORMAT_E1NONE) + write_framer(sc, Bt8370_SBCn +i, 0x01); /* assigned, TS0 o/h bits */ + else if (FORMAT_E1CAS && (i==16) && !FORMAT_E1NONE) + write_framer(sc, Bt8370_SBCn +i, 0x01); /* assigned, TS16 o/h bits */ + else if ((sc->config.time_slots & (1<<i)) != 0) + write_framer(sc, Bt8370_SBCn +i, 0x0D); /* assigned, RINDO, TINDO */ + else + write_framer(sc, Bt8370_SBCn +i, 0x01); /* assigned, idle */ + + /* 100-11F:TPCn -- Transmit Per-Channel Control */ + if (FORMAT_E1CAS && (i==0)) + write_framer(sc, Bt8370_TPCn +i, 0x30); /* tidle, sig=0000 (MAS) */ + else if (FORMAT_E1CAS && (i==16)) + write_framer(sc, Bt8370_TPCn +i, 0x3B); /* tidle, sig=1011 (XYXX) */ + else if ((sc->config.time_slots & (1<<i)) == 0) + write_framer(sc, Bt8370_TPCn +i, 0x20); /* tidle: use TSLIP_LOn */ + else + write_framer(sc, Bt8370_TPCn +i, 0x00); /* nothing special */ + + /* 140-15F:TSLIP_LOn -- Transmit PCM Slip Buffer */ + write_framer(sc, Bt8370_TSLIP_LOn +i, 0x7F); /* idle chan data */ + /* 180-19F:RPCn -- Receive Per-Channel Control */ + write_framer(sc, Bt8370_RPCn +i, 0x00); /* nothing special */ + } + + /* Enable transmitter output drivers. */ + set_mii16_bits(sc, MII16_T1_XOE); + } + +static void +t1_ident(softc_t *sc) + { + printf(", Bt837%x rev %x", + read_framer(sc, Bt8370_DID)>>4, + read_framer(sc, Bt8370_DID)&0x0F); + } + +/* Called once a second; must not sleep. */ +static int +t1_watchdog(softc_t *sc) + { + u_int16_t LCV = 0, FERR = 0, CRC = 0, FEBE = 0; + u_int8_t alm1, alm3, loop, isr0; + int link_status = STATUS_UP; + int i; + + /* Read the alarm registers */ + alm1 = read_framer(sc, Bt8370_ALM1); + alm3 = read_framer(sc, Bt8370_ALM3); + loop = read_framer(sc, Bt8370_LOOP); + isr0 = read_framer(sc, Bt8370_ISR0); + + /* Always ignore the SIGFRZ alarm bit, */ + alm1 &= ~ALM1_SIGFRZ; + if (FORMAT_T1ANY) /* ignore RYEL in T1 modes */ + alm1 &= ~ALM1_RYEL; + else if (FORMAT_E1NONE) /* ignore all alarms except LOS */ + alm1 &= ALM1_RLOS; + + /* Software is alive. */ + led_inv(sc, MII16_T1_LED_GRN); + + /* Receiving Alarm Indication Signal (AIS). */ + if ((alm1 & ALM1_RAIS)!=0) /* receiving ais */ + led_on(sc, MII16_T1_LED_BLU); + else if ((alm1 & ALM1_RLOS)!=0) /* sending ais */ + led_inv(sc, MII16_T1_LED_BLU); + else + led_off(sc, MII16_T1_LED_BLU); + + /* Receiving Remote Alarm Indication (RAI). */ + if ((alm1 & (ALM1_RMYEL | ALM1_RYEL))!=0) /* receiving rai */ + led_on(sc, MII16_T1_LED_YEL); + else if ((alm1 & ALM1_RLOF)!=0) /* sending rai */ + led_inv(sc, MII16_T1_LED_YEL); + else + led_off(sc, MII16_T1_LED_YEL); + + /* If any alarm bits are set then the link is 'down'. */ + /* The bad bits are: rmyel ryel rais ralos rlos rlof. */ + /* Some alarm bits have been masked by this point. */ + if (alm1 != 0) link_status = STATUS_DOWN; + + /* Declare local Red Alarm if the link is down. */ + if (link_status == STATUS_DOWN) + led_on(sc, MII16_T1_LED_RED); + else if (sc->loop_timer != 0) /* loopback is active */ + led_inv(sc, MII16_T1_LED_RED); + else + led_off(sc, MII16_T1_LED_RED); + + /* Print latched error bits if they changed. */ + if ((DRIVER_DEBUG) && (alm1 != sc->last_alm1)) + { + char *on = "ON ", *off = "OFF"; + printf("%s: RLOF=%s RLOS=%s RALOS=%s RAIS=%s RYEL=%s RMYEL=%s\n", + NAME_UNIT, + (alm1 & ALM1_RLOF) ? on : off, + (alm1 & ALM1_RLOS) ? on : off, + (alm1 & ALM1_RALOS) ? on : off, + (alm1 & ALM1_RAIS) ? on : off, + (alm1 & ALM1_RYEL) ? on : off, + (alm1 & ALM1_RMYEL) ? on : off); + } + + /* Check and print error counters if non-zero. */ + LCV = read_framer(sc, Bt8370_LCV_LO) + + (read_framer(sc, Bt8370_LCV_HI)<<8); + if (!FORMAT_E1NONE) + FERR = read_framer(sc, Bt8370_FERR_LO) + + (read_framer(sc, Bt8370_FERR_HI)<<8); + if (FORMAT_E1CRC || FORMAT_T1ESF) + CRC = read_framer(sc, Bt8370_CRC_LO) + + (read_framer(sc, Bt8370_CRC_HI)<<8); + if (FORMAT_E1CRC) + FEBE = read_framer(sc, Bt8370_FEBE_LO) + + (read_framer(sc, Bt8370_FEBE_HI)<<8); + /* Only LCV is valid if Out-Of-Frame */ + if (FORMAT_E1NONE) FERR = CRC = FEBE = 0; + if ((DRIVER_DEBUG) && (LCV || FERR || CRC || FEBE)) + printf("%s: LCV=%u FERR=%u CRC=%u FEBE=%u\n", + NAME_UNIT, LCV, FERR, CRC, FEBE); + + /* Driver keeps crude link-level error counters (SNMP is better). */ + sc->status.cntrs.lcv_errs += LCV; + sc->status.cntrs.frm_errs += FERR; + sc->status.cntrs.crc_errs += CRC; + sc->status.cntrs.febe_errs += FEBE; + + /* Check for BOP messages in the ESF Facility Data Link. */ + if ((FORMAT_T1ESF) && (read_framer(sc, Bt8370_ISR1) & 0x80)) + { + u_int8_t bop_code = read_framer(sc, Bt8370_RBOP) & 0x3F; + + switch (bop_code) + { + case T1BOP_OOF: + { + if ((DRIVER_DEBUG) && ((sc->last_alm1 & ALM1_RMYEL)==0)) + printf("%s: Receiving a 'yellow alarm' BOP msg\n", NAME_UNIT); + break; + } + case T1BOP_LINE_UP: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'line loopback activate' BOP msg\n", NAME_UNIT); + write_framer(sc, Bt8370_LOOP, LOOP_LINE); + sc->loop_timer = 305; + break; + } + case T1BOP_LINE_DOWN: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'line loopback deactivate' BOP msg\n", NAME_UNIT); + write_framer(sc, Bt8370_LOOP, + read_framer(sc, Bt8370_LOOP) & ~LOOP_LINE); + sc->loop_timer = 0; + break; + } + case T1BOP_PAY_UP: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'payload loopback activate' BOP msg\n", NAME_UNIT); + write_framer(sc, Bt8370_LOOP, LOOP_PAYLOAD); + sc->loop_timer = 305; + break; + } + case T1BOP_PAY_DOWN: + { + if (DRIVER_DEBUG) + printf("%s: Received a 'payload loopback deactivate' BOP msg\n", NAME_UNIT); + write_framer(sc, Bt8370_LOOP, + read_framer(sc, Bt8370_LOOP) & ~LOOP_PAYLOAD); + sc->loop_timer = 0; + break; + } + default: + { + if (DRIVER_DEBUG) + printf("%s: Received a type 0x%02X BOP msg\n", NAME_UNIT, bop_code); + break; + } + } + } + + /* Check for HDLC pkts in the ESF Facility Data Link. */ + if ((FORMAT_T1ESF) && (read_framer(sc, Bt8370_ISR2) & 0x70)) + { + /* while (not fifo-empty && not start-of-msg) flush fifo */ + while ((read_framer(sc, Bt8370_RDL1_STAT) & 0x0C) == 0) + read_framer(sc, Bt8370_RDL1); + /* If (not fifo-empty), then begin processing fifo contents. */ + if ((read_framer(sc, Bt8370_RDL1_STAT) & 0x0C) == 0x08) + { + u_int8_t msg[64]; + u_int8_t stat = read_framer(sc, Bt8370_RDL1); + sc->status.cntrs.fdl_pkts++; + for (i=0; i<(stat & 0x3F); i++) + msg[i] = read_framer(sc, Bt8370_RDL1); + /* Is this FDL message a T1.403 performance report? */ + if (((stat & 0x3F)==11) && + ((msg[0]==0x38) || (msg[0]==0x3A)) && + (msg[1]==1) && (msg[2]==3)) + /* Copy 4 PRs from FDL pkt to SNMP struct. */ + memcpy(sc->status.snmp.t1.prm, msg+3, 8); + } + } + + /* Check for inband loop up/down commands. */ + if (FORMAT_T1ANY) + { + u_int8_t isr6 = read_framer(sc, Bt8370_ISR6); + u_int8_t alarm2 = read_framer(sc, Bt8370_ALM2); + u_int8_t tlb = read_framer(sc, Bt8370_TLB); + + /* Inband Code == Loop Up && On Transition && Inband Tx Inactive */ + if ((isr6 & 0x40) && (alarm2 & 0x40) && ((tlb & 1)==0)) + { /* CSU loop up is 10000 10000 ... */ + if (DRIVER_DEBUG) + printf("%s: Received a 'CSU Loop Up' inband msg\n", NAME_UNIT); + write_framer(sc, Bt8370_LOOP, LOOP_LINE); /* Loop up */ + sc->loop_timer = 305; + } + /* Inband Code == Loop Down && On Transition && Inband Tx Inactive */ + if ((isr6 & 0x80) && (alarm2 & 0x80) && ((tlb & 1)==0)) + { /* CSU loop down is 100 100 100 ... */ + if (DRIVER_DEBUG) + printf("%s: Received a 'CSU Loop Down' inband msg\n", NAME_UNIT); + write_framer(sc, Bt8370_LOOP, + read_framer(sc, Bt8370_LOOP) & ~LOOP_LINE); /* loop down */ + sc->loop_timer = 0; + } + } + + /* Manually send Yellow Alarm BOP msgs. */ + if (FORMAT_T1ESF) + { + u_int8_t isr7 = read_framer(sc, Bt8370_ISR7); + + if ((isr7 & 0x02) && (alm1 & 0x02)) /* RLOF on-transition */ + { /* Start sending continuous Yellow Alarm BOP messages. */ + write_framer(sc, Bt8370_BOP, RBOP_25 | TBOP_CONT); + write_framer(sc, Bt8370_TBOP, 0x00); /* send BOP; order matters */ + } + else if ((isr7 & 0x02) && ((alm1 & 0x02)==0)) /* RLOF off-transition */ + { /* Stop sending continuous Yellow Alarm BOP messages. */ + write_framer(sc, Bt8370_BOP, RBOP_25 | TBOP_OFF); + } + } + + /* Time out loopback requests. */ + if (sc->loop_timer != 0) + if (--sc->loop_timer == 0) + if (loop != 0) + { + if (DRIVER_DEBUG) + printf("%s: Timeout: Loop Down after 300 seconds\n", NAME_UNIT); + write_framer(sc, Bt8370_LOOP, loop & ~(LOOP_PAYLOAD | LOOP_LINE)); + } + + /* RX Test Pattern status */ + if ((DRIVER_DEBUG) && (isr0 & 0x10)) + printf("%s: RX Test Pattern Sync\n", NAME_UNIT); + + /* SNMP Error Counters */ + sc->status.snmp.t1.lcv = LCV; + sc->status.snmp.t1.fe = FERR; + sc->status.snmp.t1.crc = CRC; + sc->status.snmp.t1.febe = FEBE; + + /* SNMP Line Status */ + sc->status.snmp.t1.line = 0; + if (alm1 & ALM1_RMYEL) sc->status.snmp.t1.line |= TLINE_RX_RAI; + if (alm1 & ALM1_RYEL) sc->status.snmp.t1.line |= TLINE_RX_RAI; + if (alm1 & ALM1_RLOF) sc->status.snmp.t1.line |= TLINE_TX_RAI; + if (alm1 & ALM1_RAIS) sc->status.snmp.t1.line |= TLINE_RX_AIS; + if (alm1 & ALM1_RLOS) sc->status.snmp.t1.line |= TLINE_TX_AIS; + if (alm1 & ALM1_RLOF) sc->status.snmp.t1.line |= TLINE_LOF; + if (alm1 & ALM1_RLOS) sc->status.snmp.t1.line |= TLINE_LOS; + if (alm3 & ALM3_RMAIS) sc->status.snmp.t1.line |= T1LINE_RX_TS16_AIS; + if (alm3 & ALM3_SRED) sc->status.snmp.t1.line |= T1LINE_TX_TS16_LOMF; + if (alm3 & ALM3_SEF) sc->status.snmp.t1.line |= T1LINE_SEF; + if (isr0 & 0x10) sc->status.snmp.t1.line |= T1LINE_RX_TEST; + if ((alm1 & ALM1_RMYEL) && (FORMAT_E1CAS)) + sc->status.snmp.t1.line |= T1LINE_RX_TS16_LOMF; + + /* SNMP Loopback Status */ + sc->status.snmp.t1.loop &= ~(TLOOP_FAR_LINE | TLOOP_FAR_PAYLOAD); + if (sc->config.loop_back == CFG_LOOP_TULIP) + sc->status.snmp.t1.loop |= TLOOP_NEAR_OTHER; + if (loop & LOOP_PAYLOAD) sc->status.snmp.t1.loop |= TLOOP_NEAR_PAYLOAD; + if (loop & LOOP_LINE) sc->status.snmp.t1.loop |= TLOOP_NEAR_LINE; + if (loop & LOOP_ANALOG) sc->status.snmp.t1.loop |= TLOOP_NEAR_OTHER; + if (loop & LOOP_FRAMER) sc->status.snmp.t1.loop |= TLOOP_NEAR_INWARD; + + /* Remember this state until next time. */ + sc->last_alm1 = alm1; + + /* If an INWARD loopback is in effect, link status is UP */ + if (sc->config.loop_back != CFG_LOOP_NONE) /* XXX INWARD ONLY */ + link_status = STATUS_UP; + + return link_status; + } + +/* IOCTL SYSCALL: can sleep. */ +static void +t1_send_bop(softc_t *sc, int bop_code) + { + u_int8_t bop; + int i; + + /* The BOP transmitter could be sending a continuous */ + /* BOP msg when told to send this BOP_25 message. */ + /* So save and restore the state of the BOP machine. */ + bop = read_framer(sc, Bt8370_BOP); + write_framer(sc, Bt8370_BOP, RBOP_OFF | TBOP_OFF); + for (i=0; i<40; i++) /* max delay 400 ms. */ + if (read_framer(sc, Bt8370_BOP_STAT) & 0x80) SLEEP(10000); + /* send 25 repetitions of bop_code */ + write_framer(sc, Bt8370_BOP, RBOP_OFF | TBOP_25); + write_framer(sc, Bt8370_TBOP, bop_code); /* order matters */ + /* wait for tx to stop */ + for (i=0; i<40; i++) /* max delay 400 ms. */ + if (read_framer(sc, Bt8370_BOP_STAT) & 0x80) SLEEP(10000); + /* Restore previous state of the BOP machine. */ + write_framer(sc, Bt8370_BOP, bop); + } + +/* IOCTL SYSCALL: can sleep. */ +static int +t1_ioctl(softc_t *sc, struct ioctl *ioctl) + { + int error = 0; + + switch (ioctl->cmd) + { + case IOCTL_SNMP_SEND: /* set opstatus? */ + { + switch (ioctl->data) + { + case TSEND_NORMAL: + { + write_framer(sc, Bt8370_TPATT, 0x00); /* tx pattern generator off */ + write_framer(sc, Bt8370_RPATT, 0x00); /* rx pattern detector off */ + write_framer(sc, Bt8370_TLB, 0x00); /* tx inband generator off */ + break; + } + case TSEND_LINE: + { + if (FORMAT_T1ESF) + t1_send_bop(sc, T1BOP_LINE_UP); + else if (FORMAT_T1SF) + { + write_framer(sc, Bt8370_LBP, 0x08); /* 10000 10000 ... */ + write_framer(sc, Bt8370_TLB, 0x05); /* 5 bits, framed, start */ + } + sc->status.snmp.t1.loop |= TLOOP_FAR_LINE; + break; + } + case TSEND_PAYLOAD: + { + t1_send_bop(sc, T1BOP_PAY_UP); + sc->status.snmp.t1.loop |= TLOOP_FAR_PAYLOAD; + break; + } + case TSEND_RESET: + { + if (sc->status.snmp.t1.loop == TLOOP_FAR_LINE) + { + if (FORMAT_T1ESF) + t1_send_bop(sc, T1BOP_LINE_DOWN); + else if (FORMAT_T1SF) + { + write_framer(sc, Bt8370_LBP, 0x24); /* 100100 100100 ... */ + write_framer(sc, Bt8370_TLB, 0x09); /* 6 bits, framed, start */ + } + sc->status.snmp.t1.loop &= ~TLOOP_FAR_LINE; + } + if (sc->status.snmp.t1.loop == TLOOP_FAR_PAYLOAD) + { + t1_send_bop(sc, T1BOP_PAY_DOWN); + sc->status.snmp.t1.loop &= ~TLOOP_FAR_PAYLOAD; + } + break; + } + case TSEND_QRS: + { + write_framer(sc, Bt8370_TPATT, 0x1E); /* framed QRSS */ + break; + } + default: + { + error = EINVAL; + break; + } + } + break; + } + case IOCTL_SNMP_LOOP: /* set opstatus = test? */ + { + u_int8_t new_loop = 0; + + if (ioctl->data == CFG_LOOP_NONE) + new_loop = 0; + else if (ioctl->data == CFG_LOOP_PAYLOAD) + new_loop = LOOP_PAYLOAD; + else if (ioctl->data == CFG_LOOP_LINE) + new_loop = LOOP_LINE; + else if (ioctl->data == CFG_LOOP_OTHER) + new_loop = LOOP_ANALOG; + else if (ioctl->data == CFG_LOOP_INWARD) + new_loop = LOOP_FRAMER; + else if (ioctl->data == CFG_LOOP_DUAL) + new_loop = LOOP_DUAL; + else + error = EINVAL; + if (error == 0) + { + write_framer(sc, Bt8370_LOOP, new_loop); + sc->config.loop_back = ioctl->data; + } + break; + } + default: + error = EINVAL; + break; + } + + return error; + } + +static +struct card hssi_card = + { + .config = hssi_config, + .ident = hssi_ident, + .watchdog = hssi_watchdog, + .ioctl = hssi_ioctl, + }; + +static +struct card t3_card = + { + .config = t3_config, + .ident = t3_ident, + .watchdog = t3_watchdog, + .ioctl = t3_ioctl, + }; + +static +struct card ssi_card = + { + .config = ssi_config, + .ident = ssi_ident, + .watchdog = ssi_watchdog, + .ioctl = ssi_ioctl, + }; + +static +struct card t1_card = + { + .config = t1_config, + .ident = t1_ident, + .watchdog = t1_watchdog, + .ioctl = t1_ioctl, + }; + +/* RAWIP is raw IP packets (v4 or v6) in HDLC frames with NO HEADERS. */ +/* No HDLC Address/Control fields! No line control protocol at all! */ +/* This code is BSD/ifnet-specific; Linux and Netgraph also do RAWIP. */ + +#if IFNET + +# if ((__FreeBSD__ && (__FreeBSD_version < 500000)) ||\ + __NetBSD__ || __OpenBSD__ || __bsdi__) +static void +netisr_dispatch(int isr, struct mbuf *mbuf) + { + struct ifqueue *intrq = NULL; + int qfull = 0; + +#if INET + if (isr == NETISR_IP) intrq = &ipintrq; +#endif +#if INET6 + if (isr == NETISR_IPV6) intrq = &ip6intrq; +#endif + + if ((intrq != NULL) && ((qfull = IF_QFULL(intrq)) == 0)) + { + /* rxintr_cleanup() ENQUEUES in a hard interrupt. */ + /* networking code DEQUEUES in a soft interrupt. */ + /* Some BSD QUEUE routines are not interrupt-safe. */ + DISABLE_INTR; /* noop in FreeBSD */ + IF_ENQUEUE(intrq, mbuf); + ENABLE_INTR; + schednetisr(isr); /* schedule a soft interrupt */ + } + else + { + m_freem(mbuf); + if ((intrq != NULL) && (qfull != 0)) + IF_DROP(intrq); + } + } +# endif /* ((__FreeBSD__ && (__FreeBSD_version < 500000)) || */ + /* __NetBSD__ || __OpenBSD__ || __bsdi__) */ + +/* rxintr_cleanup calls this to give a newly arrived pkt to higher levels. */ +static void +raw_input(struct ifnet *ifp, struct mbuf *mbuf) + { + softc_t *sc = IFP2SC(ifp); + +# if INET + if (mbuf->m_data[0]>>4 == 4) + netisr_dispatch(NETISR_IP, mbuf); + else +# endif +# if INET6 + if (mbuf->m_data[0]>>4 == 6) + netisr_dispatch(NETISR_IPV6, mbuf); + else +# endif + { + m_freem(mbuf); + sc->status.cntrs.idiscards++; + if (DRIVER_DEBUG) + printf("%s: raw_input: rx pkt discarded: not IPv4 or IPv6\n", NAME_UNIT); + } + } + +#endif /* IFNET */ + +/* There are TWO VERSIONS of interrupt/DMA code: Linux & BSD. + * Handling Linux and the BSDs with CPP directives would + * make the code unreadable, so there are two versions. + * Conceptually, the two versions do the same thing and + * core_interrupt() doesn't know they are different. + * + * We are "standing on the head of a pin" in these routines. + * Tulip CSRs can be accessed, but nothing else is interrupt-safe! + * Do NOT access: MII, GPIO, SROM, BIOSROM, XILINX, SYNTH, or DAC. + */ + +#if BSD /* BSD version of interrupt/DMA code */ + +/* Singly-linked tail-queues hold mbufs with active DMA. + * For RX, single mbuf clusters; for TX, mbuf chains are queued. + * NB: mbufs are linked through their m_nextpkt field. + * Callers must hold sc->bottom_lock; not otherwise locked. + */ + +/* Put an mbuf (chain) on the tail of the descriptor ring queue. */ +static void /* BSD version */ +mbuf_enqueue(struct desc_ring *ring, struct mbuf *m) + { + m->m_nextpkt = NULL; + if (ring->tail == NULL) + ring->head = m; + else + ring->tail->m_nextpkt = m; + ring->tail = m; + } + +/* Get an mbuf (chain) from the head of the descriptor ring queue. */ +static struct mbuf* /* BSD version */ +mbuf_dequeue(struct desc_ring *ring) + { + struct mbuf *m = ring->head; + if (m != NULL) + if ((ring->head = m->m_nextpkt) == NULL) + ring->tail = NULL; + return m; + } + +# if __FreeBSD__ +static void /* *** FreeBSD ONLY *** Callout from bus_dmamap_load() */ +fbsd_dmamap_load(void *arg, bus_dma_segment_t *segs, int nsegs, int error) + { + struct desc_ring *ring = arg; + ring->nsegs = error ? 0 : nsegs; + ring->segs[0] = segs[0]; + ring->segs[1] = segs[1]; + } +# endif + +/* Initialize a DMA descriptor ring. */ +static int /* BSD version */ +create_ring(softc_t *sc, struct desc_ring *ring, int num_descs) + { + struct dma_desc *descs; + int size_descs = sizeof(struct dma_desc)*num_descs; + int i, error = 0; + + /* The DMA descriptor array must not cross a page boundary. */ + if (size_descs > PAGE_SIZE) + { + printf("%s: DMA descriptor array > PAGE_SIZE (%d)\n", NAME_UNIT, PAGE_SIZE); + return EINVAL; + } + +#if __FreeBSD__ + + /* Create a DMA tag for descriptors and buffers. */ + if ((error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 2, PAGE_SIZE, BUS_DMA_ALLOCNOW, +# if (__FreeBSD_version >= 502000) + NULL, NULL, +# endif + &ring->tag))) + { + printf("%s: bus_dma_tag_create() failed: error %d\n", NAME_UNIT, error); + return error; + } + + /* Allocate wired physical memory for DMA descriptor array */ + /* and map physical address to kernel virtual address. */ + if ((error = bus_dmamem_alloc(ring->tag, (void**)&ring->first, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_ZERO, &ring->map))) + { + printf("%s: bus_dmamem_alloc() failed; error %d\n", NAME_UNIT, error); + return error; + } + descs = ring->first; + + /* Map kernel virtual address to PCI address for DMA descriptor array. */ + if ((error = bus_dmamap_load(ring->tag, ring->map, descs, size_descs, + fbsd_dmamap_load, ring, 0))) + { + printf("%s: bus_dmamap_load() failed; error %d\n", NAME_UNIT, error); + return error; + } + ring->dma_addr = ring->segs[0].ds_addr; + + /* Allocate dmamaps for each DMA descriptor. */ + for (i=0; i<num_descs; i++) + if ((error = bus_dmamap_create(ring->tag, 0, &descs[i].map))) + { + printf("%s: bus_dmamap_create() failed; error %d\n", NAME_UNIT, error); + return error; + } + +#elif (__NetBSD__ || __OpenBSD__) + + /* Use the DMA tag passed to attach() for descriptors and buffers. */ + ring->tag = sc->pa_dmat; + + /* Allocate wired physical memory for DMA descriptor array. */ + if ((error = bus_dmamem_alloc(ring->tag, size_descs, PAGE_SIZE, 0, + ring->segs, 1, &ring->nsegs, BUS_DMA_NOWAIT))) + { + printf("%s: bus_dmamem_alloc() failed; error %d\n", NAME_UNIT, error); + return error; + } + + /* Map physical address to kernel virtual address. */ + if ((error = bus_dmamem_map(ring->tag, ring->segs, ring->nsegs, + size_descs, (caddr_t *)&ring->first, BUS_DMA_NOWAIT | BUS_DMA_COHERENT))) + { + printf("%s: bus_dmamem_map() failed; error %d\n", NAME_UNIT, error); + return error; + } + descs = ring->first; /* suppress compiler warning about aliasing */ + memset(descs, 0, size_descs); + + /* Allocate dmamap for PCI access to DMA descriptor array. */ + if ((error = bus_dmamap_create(ring->tag, size_descs, 1, + size_descs, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ring->map))) + { + printf("%s: bus_dmamap_create() failed; error %d\n", NAME_UNIT, error); + return error; + } + + /* Map kernel virtual address to PCI address for DMA descriptor array. */ + if ((error = bus_dmamap_load(ring->tag, ring->map, descs, size_descs, + 0, BUS_DMA_NOWAIT))) + { + printf("%s: bus_dmamap_load() failed; error %d\n", NAME_UNIT, error); + return error; + } + ring->dma_addr = ring->map->dm_segs[0].ds_addr; + + /* Allocate dmamaps for each DMA descriptor. */ + for (i=0; i<num_descs; i++) + if ((error = bus_dmamap_create(ring->tag, MAX_DESC_LEN, 2, + MAX_CHUNK_LEN, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &descs[i].map))) + { + printf("%s: bus_dmamap_create() failed; error %d\n", NAME_UNIT, error); + return error; + } + +#elif __bsdi__ + + /* Allocate wired physical memory for DMA descriptor array. */ + if ((ring->first = malloc(size_descs, M_DEVBUF, M_NOWAIT)) == NULL) + { + printf("%s: malloc() failed for DMA descriptor array\n", NAME_UNIT); + return ENOMEM; + } + descs = ring->first; + memset(descs, 0, size_descs); + + /* Map kernel virtual address to PCI address for DMA descriptor array. */ + ring->dma_addr = vtophys(descs); /* Relax! BSD/OS only. */ + +#endif + + ring->read = descs; + ring->write = descs; + ring->first = descs; + ring->last = descs + num_descs -1; + ring->last->control = TLP_DCTL_END_RING; + ring->num_descs = num_descs; + ring->size_descs = size_descs; + ring->head = NULL; + ring->tail = NULL; + + return 0; + } + +/* Destroy a DMA descriptor ring */ +static void /* BSD version */ +destroy_ring(softc_t *sc, struct desc_ring *ring) + { + struct dma_desc *desc; + struct mbuf *m; + + /* Free queued mbufs. */ + while ((m = mbuf_dequeue(ring)) != NULL) + m_freem(m); + + /* TX may have one pkt that is not on any queue. */ + if (sc->tx_mbuf != NULL) + { + m_freem(sc->tx_mbuf); + sc->tx_mbuf = NULL; + } + + /* Unmap active DMA descriptors. */ + while (ring->read != ring->write) + { + bus_dmamap_unload(ring->tag, ring->read->map); + if (ring->read++ == ring->last) ring->read = ring->first; + } + +#if __FreeBSD__ + + /* Free the dmamaps of all DMA descriptors. */ + for (desc=ring->first; desc!=ring->last+1; desc++) + if (desc->map != NULL) + bus_dmamap_destroy(ring->tag, desc->map); + + /* Unmap PCI address for DMA descriptor array. */ + if (ring->dma_addr != 0) + bus_dmamap_unload(ring->tag, ring->map); + /* Free kernel memory for DMA descriptor array. */ + if (ring->first != NULL) + bus_dmamem_free(ring->tag, ring->first, ring->map); + /* Free the DMA tag created for this ring. */ + if (ring->tag != NULL) + bus_dma_tag_destroy(ring->tag); + +#elif (__NetBSD__ || __OpenBSD__) + + /* Free the dmamaps of all DMA descriptors. */ + for (desc=ring->first; desc!=ring->last+1; desc++) + if (desc->map != NULL) + bus_dmamap_destroy(ring->tag, desc->map); + + /* Unmap PCI address for DMA descriptor array. */ + if (ring->dma_addr != 0) + bus_dmamap_unload(ring->tag, ring->map); + /* Free dmamap for DMA descriptor array. */ + if (ring->map != NULL) + bus_dmamap_destroy(ring->tag, ring->map); + /* Unmap kernel address for DMA descriptor array. */ + if (ring->first != NULL) + bus_dmamem_unmap(ring->tag, (caddr_t)ring->first, ring->size_descs); + /* Free kernel memory for DMA descriptor array. */ + if (ring->segs[0].ds_addr != 0) + bus_dmamem_free(ring->tag, ring->segs, ring->nsegs); + +#elif __bsdi__ + + /* Free kernel memory for DMA descriptor array. */ + if (ring->first != NULL) + free(ring->first, M_DEVBUF); + +#endif + } + +/* Clean up after a packet has been received. */ +static int /* BSD version */ +rxintr_cleanup(softc_t *sc) + { + struct desc_ring *ring = &sc->rxring; + struct dma_desc *first_desc, *last_desc; + struct mbuf *first_mbuf=NULL, *last_mbuf=NULL; + struct mbuf *new_mbuf; + int pkt_len, desc_len; + +#if (__FreeBSD__ && DEVICE_POLLING) + /* Input packet flow control (livelock prevention): */ + /* Give pkts to higher levels only if quota is > 0. */ + if (sc->quota <= 0) return 0; +#endif + + /* This looks complicated, but remember: typically packets up */ + /* to 2048 bytes long fit in one mbuf and use one descriptor. */ + + first_desc = last_desc = ring->read; + + /* ASSERTION: If there is a descriptor in the ring and the hardware has */ + /* finished with it, then that descriptor will have RX_FIRST_DESC set. */ + if ((ring->read != ring->write) && /* descriptor ring not empty */ + ((ring->read->status & TLP_DSTS_OWNER) == 0) && /* hardware done */ + ((ring->read->status & TLP_DSTS_RX_FIRST_DESC) == 0)) /* should be set */ + panic("%s: rxintr_cleanup: rx-first-descriptor not set.\n", NAME_UNIT); + + /* First decide if a complete packet has arrived. */ + /* Run down DMA descriptors looking for one marked "last". */ + /* Bail out if an active descriptor is encountered. */ + /* Accumulate most significant bits of packet length. */ + pkt_len = 0; + for (;;) + { + if (last_desc == ring->write) return 0; /* no more descs */ + if (last_desc->status & TLP_DSTS_OWNER) return 0; /* still active */ + if (last_desc->status & TLP_DSTS_RX_LAST_DESC) break; /* end of packet */ + pkt_len += last_desc->length1 + last_desc->length2; /* entire desc filled */ + if (last_desc++->control & TLP_DCTL_END_RING) last_desc = ring->first; /* ring wrap */ + } + + /* A complete packet has arrived; how long is it? */ + /* H/w ref man shows RX pkt length as a 14-bit field. */ + /* An experiment found that only the 12 LSBs work. */ + if (((last_desc->status>>16)&0xFFF) == 0) pkt_len += 4096; /* carry-bit */ + pkt_len = (pkt_len & 0xF000) + ((last_desc->status>>16) & 0x0FFF); + /* Subtract the CRC length unless doing so would underflow. */ + if (pkt_len >= sc->config.crc_len) pkt_len -= sc->config.crc_len; + + /* Run down DMA descriptors again doing the following: + * 1) put pkt info in pkthdr of first mbuf, + * 2) link mbufs, + * 3) set mbuf lengths. + */ + first_desc = ring->read; + do + { + /* Read a DMA descriptor from the ring. */ + last_desc = ring->read; + /* Advance the ring read pointer. */ + if (ring->read++ == ring->last) ring->read = ring->first; + + /* Dequeue the corresponding cluster mbuf. */ + new_mbuf = mbuf_dequeue(ring); + if (new_mbuf == NULL) + panic("%s: rxintr_cleanup: expected an mbuf\n", NAME_UNIT); + + desc_len = last_desc->length1 + last_desc->length2; + /* If bouncing, copy bounce buf to mbuf. */ + DMA_SYNC(last_desc->map, desc_len, BUS_DMASYNC_POSTREAD); + /* Unmap kernel virtual address to PCI address. */ + bus_dmamap_unload(ring->tag, last_desc->map); + + /* 1) Put pkt info in pkthdr of first mbuf. */ + if (last_desc == first_desc) + { + first_mbuf = new_mbuf; + first_mbuf->m_pkthdr.len = pkt_len; /* total pkt length */ +#if IFNET + first_mbuf->m_pkthdr.rcvif = sc->ifp; /* how it got here */ +#else + first_mbuf->m_pkthdr.rcvif = NULL; +#endif + } + else /* 2) link mbufs. */ + { + last_mbuf->m_next = new_mbuf; + /* M_PKTHDR should be set in the first mbuf only. */ + new_mbuf->m_flags &= ~M_PKTHDR; + } + last_mbuf = new_mbuf; + + /* 3) Set mbuf lengths. */ + new_mbuf->m_len = (pkt_len >= desc_len) ? desc_len : pkt_len; + pkt_len -= new_mbuf->m_len; + } while ((last_desc->status & TLP_DSTS_RX_LAST_DESC) == 0); + + /* Decide whether to accept or to discard this packet. */ + /* RxHDLC sets MIIERR for bad CRC, abort and partial byte at pkt end. */ + if (((last_desc->status & TLP_DSTS_RX_BAD) == 0) && + (sc->status.oper_status == STATUS_UP) && + (first_mbuf->m_pkthdr.len > 0)) + { + /* Optimization: copy a small pkt into a small mbuf. */ + if (first_mbuf->m_pkthdr.len <= COPY_BREAK) + { + MGETHDR(new_mbuf, M_DONTWAIT, MT_DATA); + if (new_mbuf != NULL) + { + new_mbuf->m_pkthdr.rcvif = first_mbuf->m_pkthdr.rcvif; + new_mbuf->m_pkthdr.len = first_mbuf->m_pkthdr.len; + new_mbuf->m_len = first_mbuf->m_len; + memcpy(new_mbuf->m_data, first_mbuf->m_data, + first_mbuf->m_pkthdr.len); + m_freem(first_mbuf); + first_mbuf = new_mbuf; + } + } + /* Include CRC and one flag byte in input byte count. */ + sc->status.cntrs.ibytes += first_mbuf->m_pkthdr.len + sc->config.crc_len +1; + sc->status.cntrs.ipackets++; +#if IFNET + sc->ifp->if_ipackets++; + LMC_BPF_MTAP(first_mbuf); +#endif +#if (__FreeBSD__ && DEVICE_POLLING) + sc->quota--; +#endif + + /* Give this good packet to the network stacks. */ +#if NETGRAPH + if (sc->ng_hook != NULL) /* is hook connected? */ + { +# if (__FreeBSD_version >= 500000) + int error; /* ignore error */ + NG_SEND_DATA_ONLY(error, sc->ng_hook, first_mbuf); +# else /* FreeBSD-4 */ + ng_queue_data(sc->ng_hook, first_mbuf, NULL); +# endif + return 1; /* did something */ + } +#endif /* NETGRAPH */ + if (sc->config.line_pkg == PKG_RAWIP) + raw_input(sc->ifp, first_mbuf); + else + { +#if NSPPP + sppp_input(sc->ifp, first_mbuf); +#elif P2P + new_mbuf = first_mbuf; + while (new_mbuf != NULL) + { + sc->p2p->p2p_hdrinput(sc->p2p, new_mbuf->m_data, new_mbuf->m_len); + new_mbuf = new_mbuf->m_next; + } + sc->p2p->p2p_input(sc->p2p, NULL); + m_freem(first_mbuf); +#else + m_freem(first_mbuf); + sc->status.cntrs.idiscards++; +#endif + } + } + else if (sc->status.oper_status != STATUS_UP) + { + /* If the link is down, this packet is probably noise. */ + m_freem(first_mbuf); + sc->status.cntrs.idiscards++; + if (DRIVER_DEBUG) + printf("%s: rxintr_cleanup: rx pkt discarded: link down\n", NAME_UNIT); + } + else /* Log and discard this bad packet. */ + { + if (DRIVER_DEBUG) + printf("%s: RX bad pkt; len=%d %s%s%s%s\n", + NAME_UNIT, first_mbuf->m_pkthdr.len, + (last_desc->status & TLP_DSTS_RX_MII_ERR) ? " miierr" : "", + (last_desc->status & TLP_DSTS_RX_DRIBBLE) ? " dribble" : "", + (last_desc->status & TLP_DSTS_RX_DESC_ERR) ? " descerr" : "", + (last_desc->status & TLP_DSTS_RX_OVERRUN) ? " overrun" : ""); + if (last_desc->status & TLP_DSTS_RX_OVERRUN) + sc->status.cntrs.fifo_over++; + else + sc->status.cntrs.ierrors++; + m_freem(first_mbuf); + } + + return 1; /* did something */ + } + +/* Setup (prepare) to receive a packet. */ +/* Try to keep the RX descriptor ring full of empty buffers. */ +static int /* BSD version */ +rxintr_setup(softc_t *sc) + { + struct desc_ring *ring = &sc->rxring; + struct dma_desc *desc; + struct mbuf *m; + int desc_len; + int error; + + /* Ring is full if (wrap(write+1)==read) */ + if (((ring->write == ring->last) ? ring->first : ring->write+1) == ring->read) + return 0; /* ring is full; nothing to do */ + + /* Allocate a small mbuf and attach an mbuf cluster. */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + { + sc->status.cntrs.rxdma++; + if (DRIVER_DEBUG) + printf("%s: rxintr_setup: MGETHDR() failed\n", NAME_UNIT); + return 0; + } + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) + { + m_freem(m); + sc->status.cntrs.rxdma++; + if (DRIVER_DEBUG) + printf("%s: rxintr_setup: MCLGET() failed\n", NAME_UNIT); + return 0; + } + + /* Queue the mbuf for later processing by rxintr_cleanup. */ + mbuf_enqueue(ring, m); + + /* Write a DMA descriptor into the ring. */ + /* Hardware won't see it until the OWNER bit is set. */ + desc = ring->write; + /* Advance the ring write pointer. */ + if (ring->write++ == ring->last) ring->write = ring->first; + + desc_len = (MCLBYTES < MAX_DESC_LEN) ? MCLBYTES : MAX_DESC_LEN; + /* Map kernel virtual address to PCI address. */ + if ((error = DMA_LOAD(desc->map, m->m_data, desc_len))) + printf("%s: bus_dmamap_load(rx) failed; error %d\n", NAME_UNIT, error); + /* Invalidate the cache for this mbuf. */ + DMA_SYNC(desc->map, desc_len, BUS_DMASYNC_PREREAD); + + /* Set up the DMA descriptor. */ +#if __FreeBSD__ + desc->address1 = ring->segs[0].ds_addr; +#elif (__NetBSD__ || __OpenBSD__) + desc->address1 = desc->map->dm_segs[0].ds_addr; +#elif __bsdi__ + desc->address1 = vtophys(m->m_data); /* Relax! BSD/OS only. */ +#endif + desc->length1 = desc_len>>1; + desc->address2 = desc->address1 + desc->length1; + desc->length2 = desc_len>>1; + + /* Before setting the OWNER bit, flush the cache (memory barrier). */ + DMA_SYNC(ring->map, ring->size_descs, BUS_DMASYNC_PREWRITE); + + /* Commit the DMA descriptor to the hardware. */ + desc->status = TLP_DSTS_OWNER; + + /* Notify the receiver that there is another buffer available. */ + WRITE_CSR(TLP_RX_POLL, 1); + + return 1; /* did something */ + } + +/* Clean up after a packet has been transmitted. */ +/* Free the mbuf chain and update the DMA descriptor ring. */ +static int /* BSD version */ +txintr_cleanup(softc_t *sc) + { + struct desc_ring *ring = &sc->txring; + struct dma_desc *desc; + + while ((ring->read != ring->write) && /* while ring is not empty */ + ((ring->read->status & TLP_DSTS_OWNER) == 0)) + { + /* Read a DMA descriptor from the ring. */ + desc = ring->read; + /* Advance the ring read pointer. */ + if (ring->read++ == ring->last) ring->read = ring->first; + + /* This is a no-op on most architectures. */ + DMA_SYNC(desc->map, desc->length1 + desc->length2, BUS_DMASYNC_POSTWRITE); + /* Unmap kernel virtual address to PCI address. */ + bus_dmamap_unload(ring->tag, desc->map); + + /* If this descriptor is the last segment of a packet, */ + /* then dequeue and free the corresponding mbuf chain. */ + if ((desc->control & TLP_DCTL_TX_LAST_SEG) != 0) + { + struct mbuf *m; + if ((m = mbuf_dequeue(ring)) == NULL) + panic("%s: txintr_cleanup: expected an mbuf\n", NAME_UNIT); + + /* Include CRC and one flag byte in output byte count. */ + sc->status.cntrs.obytes += m->m_pkthdr.len + sc->config.crc_len +1; + sc->status.cntrs.opackets++; +#if IFNET + sc->ifp->if_opackets++; + LMC_BPF_MTAP(m); +#endif + /* The only bad TX status is fifo underrun. */ + if ((desc->status & TLP_DSTS_TX_UNDERRUN) != 0) + sc->status.cntrs.fifo_under++; + + m_freem(m); + return 1; /* did something */ + } + } + + return 0; + } + +/* Build DMA descriptors for a transmit packet mbuf chain. */ +static int /* 0=success; 1=error */ /* BSD version */ +txintr_setup_mbuf(softc_t *sc, struct mbuf *m) + { + struct desc_ring *ring = &sc->txring; + struct dma_desc *desc; + unsigned int desc_len; + + /* build DMA descriptors for a chain of mbufs. */ + while (m != NULL) + { + char *data = m->m_data; + int length = m->m_len; /* zero length mbufs happen! */ + + /* Build DMA descriptors for one mbuf. */ + while (length > 0) + { + int error; + + /* Ring is full if (wrap(write+1)==read) */ + if (((ring->temp==ring->last) ? ring->first : ring->temp+1) == ring->read) + { /* Not enough DMA descriptors; try later. */ + for (; ring->temp!=ring->write; + ring->temp = (ring->temp==ring->first)? ring->last : ring->temp-1) + bus_dmamap_unload(ring->tag, ring->temp->map); + sc->status.cntrs.txdma++; + return 1; + } + + /* Provisionally, write a descriptor into the ring. */ + /* But don't change the REAL ring write pointer. */ + /* Hardware won't see it until the OWNER bit is set. */ + desc = ring->temp; + /* Advance the temporary ring write pointer. */ + if (ring->temp++ == ring->last) ring->temp = ring->first; + + /* Clear all control bits except the END_RING bit. */ + desc->control &= TLP_DCTL_END_RING; + /* Don't pad short packets up to 64 bytes. */ + desc->control |= TLP_DCTL_TX_NO_PAD; + /* Use Tulip's CRC-32 generator, if appropriate. */ + if (sc->config.crc_len != CFG_CRC_32) + desc->control |= TLP_DCTL_TX_NO_CRC; + /* Set the OWNER bit, except in the first descriptor. */ + if (desc != ring->write) + desc->status = TLP_DSTS_OWNER; + + desc_len = (length > MAX_CHUNK_LEN) ? MAX_CHUNK_LEN : length; + /* Map kernel virtual address to PCI address. */ + if ((error = DMA_LOAD(desc->map, data, desc_len))) + printf("%s: bus_dmamap_load(tx) failed; error %d\n", NAME_UNIT, error); + /* Flush the cache and if bouncing, copy mbuf to bounce buf. */ + DMA_SYNC(desc->map, desc_len, BUS_DMASYNC_PREWRITE); + + /* Prevent wild fetches if mapping fails (nsegs==0). */ + desc->length1 = desc->length2 = 0; + desc->address1 = desc->address2 = 0; +#if (__FreeBSD__ || __NetBSD__ || __OpenBSD__) + { +# if __FreeBSD__ + bus_dma_segment_t *segs = ring->segs; + int nsegs = ring->nsegs; +# elif (__NetBSD__ || __OpenBSD__) + bus_dma_segment_t *segs = desc->map->dm_segs; + int nsegs = desc->map->dm_nsegs; +# endif + if (nsegs >= 1) + { + desc->address1 = segs[0].ds_addr; + desc->length1 = segs[0].ds_len; + } + if (nsegs == 2) + { + desc->address2 = segs[1].ds_addr; + desc->length2 = segs[1].ds_len; + } + } +#elif __bsdi__ + desc->address1 = vtophys(data); /* Relax! BSD/OS only. */ + desc->length1 = desc_len; +#endif + + data += desc_len; + length -= desc_len; + } /* while (length > 0) */ + + m = m->m_next; + } /* while (m != NULL) */ + + return 0; /* success */ + } + +/* Setup (prepare) to transmit a packet. */ +/* Select a packet, build DMA descriptors and give packet to hardware. */ +/* If DMA descriptors run out, abandon the attempt and return 0. */ +static int /* BSD version */ +txintr_setup(softc_t *sc) + { + struct desc_ring *ring = &sc->txring; + struct dma_desc *first_desc, *last_desc; + + /* Protect against half-up links: Don't transmit */ + /* if the receiver can't hear the far end. */ + if (sc->status.oper_status != STATUS_UP) return 0; + + /* Pick a packet to transmit. */ +#if NETGRAPH + if ((sc->ng_hook != NULL) && (sc->tx_mbuf == NULL)) + { + if (!IFQ_IS_EMPTY(&sc->ng_fastq)) + IFQ_DEQUEUE(&sc->ng_fastq, sc->tx_mbuf); + else + IFQ_DEQUEUE(&sc->ng_sndq, sc->tx_mbuf); + } + else +#endif + if (sc->tx_mbuf == NULL) + { + if (sc->config.line_pkg == PKG_RAWIP) + IFQ_DEQUEUE(&sc->ifp->if_snd, sc->tx_mbuf); + else + { +#if NSPPP + sc->tx_mbuf = sppp_dequeue(sc->ifp); +#elif P2P + if (!IFQ_IS_EMPTY(&sc->p2p->p2p_isnd)) + IFQ_DEQUEUE(&sc->p2p->p2p_isnd, sc->tx_mbuf); + else + IFQ_DEQUEUE(&sc->ifp->if_snd, sc->tx_mbuf); +#endif + } + } + if (sc->tx_mbuf == NULL) return 0; /* no pkt to transmit */ + + /* Build DMA descriptors for an outgoing mbuf chain. */ + ring->temp = ring->write; /* temporary ring write pointer */ + if (txintr_setup_mbuf(sc, sc->tx_mbuf) != 0) return 0; + + /* Enqueue the mbuf; txintr_cleanup will free it. */ + mbuf_enqueue(ring, sc->tx_mbuf); + + /* The transmitter has room for another packet. */ + sc->tx_mbuf = NULL; + + /* Set first & last segment bits. */ + /* last_desc is the desc BEFORE the one pointed to by ring->temp. */ + first_desc = ring->write; + first_desc->control |= TLP_DCTL_TX_FIRST_SEG; + last_desc = (ring->temp==ring->first)? ring->last : ring->temp-1; + last_desc->control |= TLP_DCTL_TX_LAST_SEG; + /* Interrupt at end-of-transmission? Why bother the poor computer! */ +/* last_desc->control |= TLP_DCTL_TX_INTERRUPT; */ + + /* Make sure the OWNER bit is not set in the next descriptor. */ + /* The OWNER bit may have been set if a previous call aborted. */ + ring->temp->status = 0; + + /* Commit the DMA descriptors to the software. */ + ring->write = ring->temp; + + /* Before setting the OWNER bit, flush the cache (memory barrier). */ + DMA_SYNC(ring->map, ring->size_descs, BUS_DMASYNC_PREWRITE); + + /* Commit the DMA descriptors to the hardware. */ + first_desc->status = TLP_DSTS_OWNER; + + /* Notify the transmitter that there is another packet to send. */ + WRITE_CSR(TLP_TX_POLL, 1); + + return 1; /* did something */ + } + +#endif /* BSD */ + +#if __linux__ +/* NOTE: this is the LINUX version of the interrupt/DMA code, */ + +/* Singly-linked tail-queues hold sk_buffs with active DMA. + * skbuffs are linked through their sk_buff.next field. + * Callers must hold sc->bottom_lock; not otherwise locked. + */ + +/* Put an skbuff on the tail of the descriptor ring queue. */ +static void /* Linux version */ +skbuff_enqueue(struct desc_ring *ring, struct sk_buff *skb) + { + skb->next = NULL; + if (ring->tail == NULL) + ring->head = skb; + else + ring->tail->next = skb; + ring->tail = skb; + } + +/* Get an skbuff from the head of the descriptor ring queue. */ +static struct sk_buff* /* Linux version */ +skbuff_dequeue(struct desc_ring *ring) + { + struct sk_buff *skb = ring->head; + if (skb != NULL) + if ((ring->head = skb->next) == NULL) + ring->tail = NULL; + return skb; + } + +/* Initialize a DMA descriptor ring. */ +static int /* Linux version */ +create_ring(softc_t *sc, struct desc_ring *ring, int num_descs) + { + struct dma_desc *descs; + int size_descs = sizeof(struct dma_desc)*num_descs; + + /* Allocate and map memory for DMA descriptor array. */ + if ((descs = pci_alloc_consistent(sc->pci_dev, size_descs, + &ring->dma_addr)) == NULL) + { + printk("%s: pci_alloc_consistent() failed\n", NAME_UNIT); + return ENOMEM; + } + memset(descs, 0, size_descs); + + ring->read = descs; + ring->write = descs; + ring->first = descs; + ring->last = descs + num_descs -1; + ring->last->control = TLP_DCTL_END_RING; + ring->num_descs = num_descs; + ring->size_descs = size_descs; + ring->head = NULL; + ring->tail = NULL; + + return 0; + } + +/* Destroy a DMA descriptor ring */ +static void /* Linux version */ +destroy_ring(softc_t *sc, struct desc_ring *ring) + { + struct sk_buff *skb; + + /* Free queued skbuffs. */ + while ((skb = skbuff_dequeue(ring)) != NULL) + dev_kfree_skb(skb); + + /* TX may have one pkt that is not on any queue. */ + if (sc->tx_skb != NULL) + { + dev_kfree_skb(sc->tx_skb); + sc->tx_skb = NULL; + } + + if (ring->first != NULL) + { + /* Unmap active DMA descriptors. */ + while (ring->read != ring->write) + { + pci_unmap_single(sc->pci_dev, ring->read->address1, + ring->read->length1 + ring->read->length2, PCI_DMA_BIDIRECTIONAL); + if (ring->read++ == ring->last) ring->read = ring->first; + } + + /* Unmap and free memory for DMA descriptor array. */ + pci_free_consistent(sc->pci_dev, ring->size_descs, ring->first, + ring->dma_addr); + } + } + +static int /* Linux version */ +rxintr_cleanup(softc_t *sc) + { + struct desc_ring *ring = &sc->rxring; + struct dma_desc *first_desc, *last_desc; + struct sk_buff *first_skb=NULL, *last_skb=NULL; + struct sk_buff *new_skb; + int pkt_len, desc_len; + + /* Input packet flow control (livelock prevention): */ + /* Give pkts to higher levels only if quota is > 0. */ + if (sc->quota <= 0) return 0; + + /* This looks complicated, but remember: packets up to 4032 */ + /* bytes long fit in one skbuff and use one DMA descriptor. */ + + first_desc = last_desc = ring->read; + + /* ASSERTION: If there is a descriptor in the ring and the hardware has */ + /* finished with it, then that descriptor will have RX_FIRST_DESC set. */ + if ((ring->read != ring->write) && /* descriptor ring not empty */ + ((ring->read->status & TLP_DSTS_OWNER) == 0) && /* hardware done */ + ((ring->read->status & TLP_DSTS_RX_FIRST_DESC) == 0)) /* should be set */ + panic("%s: rxintr_cleanup: rx-first-descriptor not set.\n", NAME_UNIT); + + /* First decide if a complete packet has arrived. */ + /* Run down DMA descriptors looking for one marked "last". */ + /* Bail out if an active descriptor is encountered. */ + /* Accumulate most significant bits of packet length. */ + pkt_len = 0; + for (;;) + { + if (last_desc == ring->write) return 0; /* no more descs */ + if (last_desc->status & TLP_DSTS_OWNER) return 0; /* still active */ + if (last_desc->status & TLP_DSTS_RX_LAST_DESC) break; /* end of packet */ + pkt_len += last_desc->length1 + last_desc->length2; /* entire desc filled */ + if (last_desc++->control & TLP_DCTL_END_RING) last_desc = ring->first; /* ring wrap */ + } + + /* A complete packet has arrived; how long is it? */ + /* H/w ref man shows RX pkt length as a 14-bit field. */ + /* An experiment found that only the 12 LSBs work. */ + if (((last_desc->status>>16)&0xFFF) == 0) pkt_len += 4096; /* carry-bit */ + pkt_len = (pkt_len & 0xF000) + ((last_desc->status>>16) & 0x0FFF); + /* Subtract the CRC length unless doing so would underflow. */ + if (pkt_len >= sc->config.crc_len) pkt_len -= sc->config.crc_len; + + /* Run down DMA descriptors again doing the following: + * 1) put pkt info in hdr of first skbuff. + * 2) put additional skbuffs on frag_list. + * 3) set skbuff lengths. + */ + first_desc = ring->read; + do + { + /* Read a DMA descriptor from the ring. */ + last_desc = ring->read; + /* Advance the ring read pointer. */ + if (ring->read++ == ring->last) ring->read = ring->first; + + /* Dequeue the corresponding skbuff. */ + new_skb = skbuff_dequeue(ring); + if (new_skb == NULL) + panic("%s: rxintr_cleanup: expected an skbuff\n", NAME_UNIT); + + desc_len = last_desc->length1 + last_desc->length2; + /* Unmap kernel virtual addresss to PCI address. */ + pci_unmap_single(sc->pci_dev, last_desc->address1, + desc_len, PCI_DMA_FROMDEVICE); + + /* Set skbuff length. */ + skb_put(new_skb, (pkt_len >= desc_len) ? desc_len : pkt_len); + pkt_len -= new_skb->len; + + /* 1) Put pkt info in hdr of first skbuff. */ + if (last_desc == first_desc) + { + first_skb = new_skb; + if (sc->config.line_pkg == PKG_RAWIP) + { + if (first_skb->data[0]>>4 == 4) + first_skb->protocol = htons(ETH_P_IP); + else if (first_skb->data[0]>>4 == 6) + first_skb->protocol = htons(ETH_P_IPV6); + } + else +#if GEN_HDLC + first_skb->protocol = hdlc_type_trans(first_skb, sc->net_dev); +#else + first_skb->protocol = htons(ETH_P_HDLC); +#endif + first_skb->mac.raw = first_skb->data; + first_skb->dev = sc->net_dev; + do_gettimeofday(&first_skb->stamp); + sc->net_dev->last_rx = jiffies; + } + else /* 2) link skbuffs. */ + { + /* Put this skbuff on the frag_list of the first skbuff. */ + new_skb->next = NULL; + if (skb_shinfo(first_skb)->frag_list == NULL) + skb_shinfo(first_skb)->frag_list = new_skb; + else + last_skb->next = new_skb; + /* 3) set skbuff lengths. */ + first_skb->len += new_skb->len; + first_skb->data_len += new_skb->len; + } + last_skb = new_skb; + } while ((last_desc->status & TLP_DSTS_RX_LAST_DESC) == 0); + + /* Decide whether to accept or to discard this packet. */ + /* RxHDLC sets MIIERR for bad CRC, abort and partial byte at pkt end. */ + if (((last_desc->status & TLP_DSTS_RX_BAD) == 0) && + (sc->status.oper_status == STATUS_UP) && + (first_skb->len > 0)) + { + /* Optimization: copy a small pkt into a small skbuff. */ + if (first_skb->len <= COPY_BREAK) + if ((new_skb = skb_copy(first_skb, GFP_ATOMIC)) != NULL) + { + dev_kfree_skb_any(first_skb); + first_skb = new_skb; + } + + /* Include CRC and one flag byte in input byte count. */ + sc->status.cntrs.ibytes += first_skb->len + sc->config.crc_len +1; + sc->status.cntrs.ipackets++; + + /* Give this good packet to the network stacks. */ + netif_receive_skb(first_skb); /* NAPI */ + sc->quota--; + } + else if (sc->status.oper_status != STATUS_UP) + { + /* If the link is down, this packet is probably noise. */ + sc->status.cntrs.idiscards++; + dev_kfree_skb_any(first_skb); + if (DRIVER_DEBUG) + printk("%s: rxintr_cleanup: rx pkt discarded: link down\n", NAME_UNIT); + } + else /* Log and discard this bad packet. */ + { + if (DRIVER_DEBUG) + printk("%s: RX bad pkt; len=%d %s%s%s%s\n", + NAME_UNIT, first_skb->len, + (last_desc->status & TLP_DSTS_RX_MII_ERR) ? " miierr" : "", + (last_desc->status & TLP_DSTS_RX_DRIBBLE) ? " dribble" : "", + (last_desc->status & TLP_DSTS_RX_DESC_ERR) ? " descerr" : "", + (last_desc->status & TLP_DSTS_RX_OVERRUN) ? " overrun" : ""); + if (last_desc->status & TLP_DSTS_RX_OVERRUN) + sc->status.cntrs.fifo_over++; + else + sc->status.cntrs.ierrors++; + dev_kfree_skb_any(first_skb); + } + + return 1; /* did something */ + } + +/* Setup (prepare) to receive a packet. */ +/* Try to keep the RX descriptor ring full of empty buffers. */ +static int /* Linux version */ +rxintr_setup(softc_t *sc) + { + struct desc_ring *ring = &sc->rxring; + struct dma_desc *desc; + struct sk_buff *skb; + u_int32_t dma_addr; + + /* Ring is full if (wrap(write+1)==read) */ + if (((ring->write == ring->last) ? ring->first : ring->write+1) == ring->read) + return 0; /* ring is full; nothing to do */ + + /* Allocate an skbuff. */ + if ((skb = dev_alloc_skb(MAX_DESC_LEN)) == NULL) + { + sc->status.cntrs.rxdma++; + if (DRIVER_DEBUG) + printk("%s: rxintr_setup: dev_alloc_skb() failed\n", NAME_UNIT); + return 0; + } + skb->dev = sc->net_dev; + + /* Queue the skbuff for later processing by rxintr_cleanup. */ + skbuff_enqueue(ring, skb); + + /* Write a DMA descriptor into the ring. */ + /* Hardware won't see it until the OWNER bit is set. */ + desc = ring->write; + /* Advance the ring write pointer. */ + if (ring->write++ == ring->last) ring->write = ring->first; + + /* Map kernel virtual addresses to PCI addresses. */ + dma_addr = pci_map_single(sc->pci_dev, skb->data, + MAX_DESC_LEN, PCI_DMA_FROMDEVICE); + /* Set up the DMA descriptor. */ + desc->address1 = dma_addr; + desc->length1 = MAX_CHUNK_LEN; + desc->address2 = desc->address1 + desc->length1; + desc->length2 = MAX_CHUNK_LEN; + + /* Before setting the OWNER bit, flush the cache (memory barrier). */ + wmb(); /* write memory barrier */ + + /* Commit the DMA descriptor to the hardware. */ + desc->status = TLP_DSTS_OWNER; + + /* Notify the receiver that there is another buffer available. */ + WRITE_CSR(TLP_RX_POLL, 1); + + return 1; /* did something */ + } + +/* Clean up after a packet has been transmitted. */ +/* Free the sk_buff and update the DMA descriptor ring. */ +static int /* Linux version */ +txintr_cleanup(softc_t *sc) + { + struct desc_ring *ring = &sc->txring; + struct dma_desc *desc; + + while ((ring->read != ring->write) && /* ring is not empty */ + ((ring->read->status & TLP_DSTS_OWNER) == 0)) + { + /* Read a DMA descriptor from the ring. */ + desc = ring->read; + /* Advance the ring read pointer. */ + if (ring->read++ == ring->last) ring->read = ring->first; + /* Unmap kernel virtual address to PCI address. */ + pci_unmap_single(sc->pci_dev, desc->address1, + desc->length1 + desc->length2, PCI_DMA_TODEVICE); + + /* If this descriptor is the last segment of a packet, */ + /* then dequeue and free the corresponding skbuff. */ + if ((desc->control & TLP_DCTL_TX_LAST_SEG) != 0) + { + struct sk_buff *skb; + if ((skb = skbuff_dequeue(ring)) == NULL) + panic("%s: txintr_cleanup: expected an sk_buff\n", NAME_UNIT); + + /* Include CRC and one flag byte in output byte count. */ + sc->status.cntrs.obytes += skb->len + sc->config.crc_len +1; + sc->status.cntrs.opackets++; + + /* The only bad TX status is fifo underrun. */ + if ((desc->status & TLP_DSTS_TX_UNDERRUN) != 0) + { + sc->status.cntrs.fifo_under++; /* also increment oerrors? */ + if (DRIVER_DEBUG) + printk("%s: txintr_cleanup: tx fifo underrun\n", NAME_UNIT); + } + + dev_kfree_skb_any(skb); + return 1; /* did something */ + } + } + + return 0; + } + +/* Build DMA descriptors for a tranmit packet fragment, */ +/* Assertion: fragment is contiguous in physical memory. */ +static int /* 0=success; 1=error */ /* linux version */ +txintr_setup_frag(softc_t *sc, char *data, int length) + { + struct desc_ring *ring = &sc->txring; + struct dma_desc *desc; + unsigned int desc_len; + u_int32_t dma_addr; + + while (length > 0) + { + /* Ring is full if (wrap(write+1)==read) */ + if (((ring->temp==ring->last) ? ring->first : ring->temp+1) == ring->read) + { /* Not enough DMA descriptors; try later. */ + for (; ring->temp!=ring->write; + ring->temp = (ring->temp==ring->first)? ring->last : ring->temp-1) + pci_unmap_single(sc->pci_dev, ring->temp->address1, + ring->temp->length1 + ring->temp->length2, PCI_DMA_FROMDEVICE); + sc->status.cntrs.txdma++; + return 1; + } + + /* Provisionally, write a DMA descriptor into the ring. */ + /* But don't change the REAL ring write pointer. */ + /* Hardware won't see it until the OWNER bit is set. */ + desc = ring->temp; + /* Advance the temporary ring write pointer. */ + if (ring->temp++ == ring->last) ring->temp = ring->first; + + /* Clear all control bits except the END_RING bit. */ + desc->control &= TLP_DCTL_END_RING; + /* Don't pad short packets up to 64 bytes */ + desc->control |= TLP_DCTL_TX_NO_PAD; + /* Use Tulip's CRC-32 generator, if appropriate. */ + if (sc->config.crc_len != CFG_CRC_32) + desc->control |= TLP_DCTL_TX_NO_CRC; + /* Set the OWNER bit, except in the first descriptor. */ + if (desc != ring->write) + desc->status = TLP_DSTS_OWNER; + + desc_len = (length >= MAX_DESC_LEN) ? MAX_DESC_LEN : length; + /* Map kernel virtual address to PCI address. */ + dma_addr = pci_map_single(sc->pci_dev, data, desc_len, PCI_DMA_TODEVICE); + /* If it will fit in one chunk, do so, otherwise split it. */ + if (desc_len <= MAX_CHUNK_LEN) + { + desc->address1 = dma_addr; + desc->length1 = desc_len; + desc->address2 = 0; + desc->length2 = 0; + } + else + { + desc->address1 = dma_addr; + desc->length1 = desc_len>>1; + desc->address2 = desc->address1 + desc->length1; + desc->length2 = desc_len>>1; + if (desc_len & 1) desc->length2++; + } + + data += desc_len; + length -= desc_len; + } /* while (length > 0) */ + + return 0; /* success */ + } + +/* NB: this procedure is recursive! */ +static int /* 0=success; 1=error */ +txintr_setup_skb(softc_t *sc, struct sk_buff *skb) + { + struct sk_buff *list; + int i; + + /* First, handle the data in the skbuff itself. */ + if (txintr_setup_frag(sc, skb->data, skb_headlen(skb))) + return 1; + + /* Next, handle the VM pages in the Scatter/Gather list. */ + if (skb_shinfo(skb)->nr_frags != 0) + for (i=0; i<skb_shinfo(skb)->nr_frags; i++) + { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + if (txintr_setup_frag(sc, page_address(frag->page) + + frag->page_offset, frag->size)) + return 1; + } + + /* Finally, handle the skbuffs in the frag_list. */ + if ((list = skb_shinfo(skb)->frag_list) != NULL) + for (; list; list=list->next) + if (txintr_setup_skb(sc, list)) /* recursive! */ + return 1; + + return 0; + } + +/* Setup (prepare) to transmit a packet. */ +/* Select a packet, build DMA descriptors and give packet to hardware. */ +/* If DMA descriptors run out, abandon the attempt and return 0. */ +static int /* Linux version */ +txintr_setup(softc_t *sc) + { + struct desc_ring *ring = &sc->txring; + struct dma_desc *first_desc, *last_desc; + + /* Protect against half-up links: Don't transmit */ + /* if the receiver can't hear the far end. */ + if (sc->status.oper_status != STATUS_UP) return 0; + + /* Pick a packet to transmit. */ + /* linux_start() puts packets in sc->tx_skb. */ + if (sc->tx_skb == NULL) + { + if (netif_queue_stopped(sc->net_dev) != 0) + netif_wake_queue(sc->net_dev); + return 0; /* no pkt to transmit */ + } + + /* Build DMA descriptors for an outgoing skbuff. */ + ring->temp = ring->write; /* temporary ring write pointer */ + if (txintr_setup_skb(sc, sc->tx_skb) != 0) return 0; + + /* Enqueue the skbuff; txintr_cleanup will free it. */ + skbuff_enqueue(ring, sc->tx_skb); + + /* The transmitter has room for another packet. */ + sc->tx_skb = NULL; + + /* Set first & last segment bits. */ + /* last_desc is the desc BEFORE the one pointed to by ring->temp. */ + first_desc = ring->write; + first_desc->control |= TLP_DCTL_TX_FIRST_SEG; + last_desc = (ring->temp==ring->first)? ring->last : ring->temp-1; + last_desc->control |= TLP_DCTL_TX_LAST_SEG; + /* Interrupt at end-of-transmission? Why bother the poor computer! */ +/* last_desc->control |= TLP_DCTL_TX_INTERRUPT; */ + + /* Make sure the OWNER bit is not set in the next descriptor. */ + /* The OWNER bit may have been set if a previous call aborted. */ + ring->temp->status = 0; + + /* Commit the DMA descriptors to the software. */ + ring->write = ring->temp; + + /* Before setting the OWNER bit, flush the cache (memory barrier). */ + wmb(); /* write memory barrier */ + + /* Commit the DMA descriptors to the hardware. */ + first_desc->status = TLP_DSTS_OWNER; + + /* Notify the transmitter that there is another packet to send. */ + WRITE_CSR(TLP_TX_POLL, 1); + + sc->net_dev->trans_start = jiffies; + + return 1; /* did something */ + } + +#endif /* __linux__ */ + +static void +check_intr_status(softc_t *sc) + { + u_int32_t status, cfcs, op_mode; + u_int32_t missed, overruns; + + /* Check for four unusual events: + * 1) fatal PCI bus errors - some are recoverable + * 2) transmitter FIFO underruns - increase fifo threshold + * 3) receiver FIFO overruns - clear potential hangup + * 4) no receive descs or bufs - count missed packets + */ + + /* 1) A fatal bus error causes a Tulip to stop initiating bus cycles. */ + /* Module unload/load or boot are the only fixes for Parity Errors. */ + /* Master and Target Aborts can be cleared and life may continue. */ + status = READ_CSR(TLP_STATUS); + if ((status & TLP_STAT_FATAL_ERROR) != 0) + { + u_int32_t fatal = (status & TLP_STAT_FATAL_BITS)>>TLP_STAT_FATAL_SHIFT; + printf("%s: FATAL PCI BUS ERROR: %s%s%s%s\n", NAME_UNIT, + (fatal == 0) ? "PARITY ERROR" : "", + (fatal == 1) ? "MASTER ABORT" : "", + (fatal == 2) ? "TARGET ABORT" : "", + (fatal >= 3) ? "RESERVED (?)" : ""); + cfcs = READ_PCI_CFG(sc, TLP_CFCS); /* try to clear it */ + cfcs &= ~(TLP_CFCS_MSTR_ABORT | TLP_CFCS_TARG_ABORT); + WRITE_PCI_CFG(sc, TLP_CFCS, cfcs); + } + + /* 2) If the transmitter fifo underruns, increase the transmit fifo */ + /* threshold: the number of bytes required to be in the fifo */ + /* before starting the transmitter (cost: increased tx delay). */ + /* The TX_FSM must be stopped to change this parameter. */ + if ((status & TLP_STAT_TX_UNDERRUN) != 0) + { + op_mode = READ_CSR(TLP_OP_MODE); + /* enable store-and-forward mode if tx_threshold tops out? */ + if ((op_mode & TLP_OP_TX_THRESH) < TLP_OP_TX_THRESH) + { + op_mode += 0x4000; /* increment TX_THRESH field; can't overflow */ + WRITE_CSR(TLP_OP_MODE, op_mode & ~TLP_OP_TX_RUN); + /* Wait for the TX FSM to stop; it might be processing a pkt. */ + while (READ_CSR(TLP_STATUS) & TLP_STAT_TX_FSM); /* XXX HANG */ + WRITE_CSR(TLP_OP_MODE, op_mode); /* restart tx */ + if (DRIVER_DEBUG) + printf("%s: tx underrun; tx fifo threshold now %d bytes\n", + NAME_UNIT, 128<<((op_mode>>TLP_OP_TR_SHIFT)&3)); + } + } + + /* 3) Errata memo from Digital Equipment Corp warns that 21140A */ + /* receivers through rev 2.2 can hang if the fifo overruns. */ + /* Recommended fix: stop and start the RX FSM after an overrun. */ + missed = READ_CSR(TLP_MISSED); + if ((overruns = ((missed & TLP_MISS_OVERRUN)>>TLP_OVERRUN_SHIFT)) != 0) + { + if (DRIVER_DEBUG) + printf("%s: rx overrun cntr=%d\n", NAME_UNIT, overruns); + sc->status.cntrs.overruns += overruns; + if ((READ_PCI_CFG(sc, TLP_CFRV) & 0xFF) <= 0x22) + { + op_mode = READ_CSR(TLP_OP_MODE); + WRITE_CSR(TLP_OP_MODE, op_mode & ~TLP_OP_RX_RUN); + /* Wait for the RX FSM to stop; it might be processing a pkt. */ + while (READ_CSR(TLP_STATUS) & TLP_STAT_RX_FSM); /* XXX HANG */ + WRITE_CSR(TLP_OP_MODE, op_mode); /* restart rx */ + } + } + + /* 4) When the receiver is enabled and a packet arrives, but no DMA */ + /* descriptor is available, the packet is counted as 'missed'. */ + /* The receiver should never miss packets; warn if it happens. */ + if ((missed = (missed & TLP_MISS_MISSED)) != 0) + { + if (DRIVER_DEBUG) + printf("%s: rx missed %d pkts\n", NAME_UNIT, missed); + sc->status.cntrs.missed += missed; + } + } + +static void /* This is where the work gets done. */ +core_interrupt(void *arg, int check_status) + { + softc_t *sc = arg; + int activity; + + /* If any CPU is inside this critical section, then */ + /* other CPUs should go away without doing anything. */ + if (BOTTOM_TRYLOCK == 0) + { + sc->status.cntrs.lck_intr++; + return; + } + + /* Clear pending card interrupts. */ + WRITE_CSR(TLP_STATUS, READ_CSR(TLP_STATUS)); + + /* In Linux, pci_alloc_consistent() means DMA descriptors */ + /* don't need explicit syncing. */ +#if BSD + { + struct desc_ring *ring = &sc->txring; + DMA_SYNC(sc->txring.map, sc->txring.size_descs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + ring = &sc->rxring; + DMA_SYNC(sc->rxring.map, sc->rxring.size_descs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + } +#endif + + do /* This is the main loop for interrupt processing. */ + { + activity = txintr_cleanup(sc); + activity += txintr_setup(sc); + activity += rxintr_cleanup(sc); + activity += rxintr_setup(sc); + } while (activity); + +#if BSD + { + struct desc_ring *ring = &sc->txring; + DMA_SYNC(sc->txring.map, sc->txring.size_descs, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + ring = &sc->rxring; + DMA_SYNC(sc->rxring.map, sc->rxring.size_descs, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +#endif + + /* As the interrupt is dismissed, check for four unusual events. */ + if (check_status) check_intr_status(sc); + + BOTTOM_UNLOCK; + } + +/* user_interrupt() may be called from a syscall or a softirq */ +static void +user_interrupt(softc_t *sc, int check_status) + { + DISABLE_INTR; /* noop on FreeBSD-5 and Linux */ + core_interrupt(sc, check_status); + ENABLE_INTR; /* noop on FreeBSD-5 and Linux */ + } + +#if BSD + +# if (__FreeBSD__ && DEVICE_POLLING) + +/* Service the card from the kernel idle loop without interrupts. */ +static void +fbsd_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) + { + softc_t *sc = IFP2SC(ifp); + +#if (__FreeBSD_version < 500000) + if ((ifp->if_capenable & IFCAP_POLLING) == 0) + { + ether_poll_deregister(ifp); + cmd = POLL_DEREGISTER; + } + + if (cmd == POLL_DEREGISTER) + { + /* Last call -- reenable card interrupts. */ + WRITE_CSR(TLP_INT_ENBL, TLP_INT_TXRX); + return; + } +#endif + + sc->quota = count; + core_interrupt(sc, (cmd==POLL_AND_CHECK_STATUS)); + } + +# endif /* (__FreeBSD__ && DEVICE_POLLING) */ + +/* BSD kernels call this procedure when an interrupt happens. */ +static intr_return_t +bsd_interrupt(void *arg) + { + softc_t *sc = arg; + + /* Cut losses early if this is not our interrupt. */ + if ((READ_CSR(TLP_STATUS) & TLP_INT_TXRX) == 0) + return IRQ_NONE; + +# if (__FreeBSD__ && DEVICE_POLLING) + if (sc->ifp->if_capenable & IFCAP_POLLING) + return IRQ_NONE; + + if ((sc->ifp->if_capabilities & IFCAP_POLLING) && + (ether_poll_register(fbsd_poll, sc->ifp))) + { + WRITE_CSR(TLP_INT_ENBL, TLP_INT_DISABLE); + return IRQ_NONE; + } + else + sc->quota = sc->rxring.num_descs; /* input flow control */ +# endif /* (__FreeBSD__ && DEVICE_POLLING) */ + + /* Disable card interrupts. */ + WRITE_CSR(TLP_INT_ENBL, TLP_INT_DISABLE); + + core_interrupt(sc, 0); + + /* Enable card interrupts. */ + WRITE_CSR(TLP_INT_ENBL, TLP_INT_TXRX); + + return IRQ_HANDLED; + } + +#endif /* BSD */ + +/* Administrative status of the driver (UP or DOWN) has changed. */ +/* A card-specific action may be required: T1 and T3 cards: no-op. */ +/* HSSI and SSI cards change the state of modem ready signals. */ +static void +set_status(softc_t *sc, int status) + { + struct ioctl ioctl; + + ioctl.cmd = IOCTL_SET_STATUS; + ioctl.data = status; + + sc->card->ioctl(sc, &ioctl); + } + +#if P2P + +/* Callout from P2P: */ +/* Get the state of DCD (Data Carrier Detect). */ +static int +p2p_getmdm(struct p2pcom *p2p, caddr_t result) + { + softc_t *sc = IFP2SC(&p2p->p2p_if); + + /* Non-zero isn't good enough; TIOCM_CAR is 0x40. */ + *(int *)result = (sc->status.oper_status==STATUS_UP) ? TIOCM_CAR : 0; + + return 0; + } + +/* Callout from P2P: */ +/* Set the state of DTR (Data Terminal Ready). */ +static int +p2p_mdmctl(struct p2pcom *p2p, int flag) + { + softc_t *sc = IFP2SC(&p2p->p2p_if); + + set_status(sc, flag); + + return 0; + } + +#endif /* P2P */ + +#if NSPPP + +# ifndef PP_FR +# define PP_FR 0 +# endif + +/* Callout from SPPP: */ +static void +sppp_tls(struct sppp *sppp) + { +# if __FreeBSD__ + if (!(sppp->pp_mode & IFF_LINK2) && + !(sppp->pp_flags & PP_FR)) +# elif __NetBSD__ || __OpenBSD__ + if (!(sppp->pp_flags & PP_CISCO)) +# endif + sppp->pp_up(sppp); + } + +/* Callout from SPPP: */ +static void +sppp_tlf(struct sppp *sppp) + { +# if __FreeBSD__ + if (!(sppp->pp_mode & IFF_LINK2) && + !(sppp->pp_flags & PP_FR)) +# elif __NetBSD__ || __OpenBSD__ + if (!(sppp->pp_flags & PP_CISCO)) +# endif + sppp->pp_down(sppp); + } + +#endif /* NSPPP */ + +/* Configure line protocol stuff. + * Called by attach_card() during module init. + * Called by core_ioctl() when lmcconfig writes sc->config. + * Called by detach_card() during module shutdown. + */ +static void +config_proto(softc_t *sc, struct config *config) + { + /* Use line protocol stack instead of RAWIP mode. */ + if ((sc->config.line_pkg == PKG_RAWIP) && + (config->line_pkg != PKG_RAWIP)) + { +#if NSPPP + LMC_BPF_DETACH; + sppp_attach(sc->ifp); + LMC_BPF_ATTACH(DLT_PPP, 4); + sc->sppp->pp_tls = sppp_tls; + sc->sppp->pp_tlf = sppp_tlf; + /* Force reconfiguration of SPPP params. */ + sc->config.line_prot = 0; + sc->config.keep_alive = config->keep_alive ? 0:1; +#elif P2P + int error = 0; + sc->p2p->p2p_proto = 0; /* force p2p_attach */ + if ((error = p2p_attach(sc->p2p))) /* calls bpfattach() */ + { + printf("%s: p2p_attach() failed; error %d\n", NAME_UNIT, error); + config->line_pkg = PKG_RAWIP; /* still in RAWIP mode */ + } + else + { + sc->p2p->p2p_mdmctl = p2p_mdmctl; /* set DTR */ + sc->p2p->p2p_getmdm = p2p_getmdm; /* get DCD */ + } +#elif GEN_HDLC + int error = 0; + sc->net_dev->mtu = HDLC_MAX_MTU; + if ((error = hdlc_open(sc->net_dev))) + { + printf("%s: hdlc_open() failed; error %d\n", NAME_UNIT, error); + printf("%s: Try 'sethdlc %s ppp'\n", NAME_UNIT, NAME_UNIT); + config->line_pkg = PKG_RAWIP; /* still in RAWIP mode */ + } +#else /* no line protocol stack was configured */ + config->line_pkg = PKG_RAWIP; /* still in RAWIP mode */ +#endif + } + + /* Bypass line protocol stack and return to RAWIP mode. */ + if ((sc->config.line_pkg != PKG_RAWIP) && + (config->line_pkg == PKG_RAWIP)) + { +#if NSPPP + LMC_BPF_DETACH; + sppp_flush(sc->ifp); + sppp_detach(sc->ifp); + setup_ifnet(sc->ifp); + LMC_BPF_ATTACH(DLT_RAW, 0); +#elif P2P + int error = 0; + if_qflush(&sc->p2p->p2p_isnd); + if ((error = p2p_detach(sc->p2p))) + { + printf("%s: p2p_detach() failed; error %d\n", NAME_UNIT, error); + printf("%s: Try 'ifconfig %s down -remove'\n", NAME_UNIT, NAME_UNIT); + config->line_pkg = PKG_P2P; /* not in RAWIP mode; still attached to P2P */ + } + else + { + setup_ifnet(sc->ifp); + LMC_BPF_ATTACH(DLT_RAW, 0); + } +#elif GEN_HDLC + hdlc_proto_detach(sc->hdlc_dev); + hdlc_close(sc->net_dev); + setup_netdev(sc->net_dev); +#endif + } + +#if NSPPP + + if (config->line_pkg != PKG_RAWIP) + { + /* Check for change to PPP protocol. */ + if ((sc->config.line_prot != PROT_PPP) && + (config->line_prot == PROT_PPP)) + { + LMC_BPF_DETACH; +# if (__NetBSD__ || __OpenBSD__) + sc->sppp->pp_flags &= ~PP_CISCO; +# elif __FreeBSD__ + sc->ifp->if_flags &= ~IFF_LINK2; + sc->sppp->pp_flags &= ~PP_FR; +# endif + LMC_BPF_ATTACH(DLT_PPP, 4); + sppp_ioctl(sc->ifp, SIOCSIFFLAGS, NULL); + } + +# ifndef DLT_C_HDLC +# define DLT_C_HDLC DLT_PPP +# endif + + /* Check for change to C_HDLC protocol. */ + if ((sc->config.line_prot != PROT_C_HDLC) && + (config->line_prot == PROT_C_HDLC)) + { + LMC_BPF_DETACH; +# if (__NetBSD__ || __OpenBSD__) + sc->sppp->pp_flags |= PP_CISCO; +# elif __FreeBSD__ + sc->ifp->if_flags |= IFF_LINK2; + sc->sppp->pp_flags &= ~PP_FR; +# endif + LMC_BPF_ATTACH(DLT_C_HDLC, 4); + sppp_ioctl(sc->ifp, SIOCSIFFLAGS, NULL); + } + + /* Check for change to Frame Relay protocol. */ + if ((sc->config.line_prot != PROT_FRM_RLY) && + (config->line_prot == PROT_FRM_RLY)) + { + LMC_BPF_DETACH; +# if (__NetBSD__ || __OpenBSD__) + sc->sppp->pp_flags &= ~PP_CISCO; +# elif __FreeBSD__ + sc->ifp->if_flags &= ~IFF_LINK2; + sc->sppp->pp_flags |= PP_FR; +# endif + LMC_BPF_ATTACH(DLT_FRELAY, 4); + sppp_ioctl(sc->ifp, SIOCSIFFLAGS, NULL); + } + + /* Check for disabling keep-alives. */ + if ((sc->config.keep_alive != 0) && + (config->keep_alive == 0)) + sc->sppp->pp_flags &= ~PP_KEEPALIVE; + + /* Check for enabling keep-alives. */ + if ((sc->config.keep_alive == 0) && + (config->keep_alive != 0)) + sc->sppp->pp_flags |= PP_KEEPALIVE; + } + +#endif /* NSPPP */ + + /* Loop back through the TULIP Ethernet chip; (no CRC). */ + /* Data sheet says stop DMA before changing OPMODE register. */ + /* But that's not as simple as it sounds; works anyway. */ + /* Check for enabling loopback thru Tulip chip. */ + if ((sc->config.loop_back != CFG_LOOP_TULIP) && + (config->loop_back == CFG_LOOP_TULIP)) + { + u_int32_t op_mode = READ_CSR(TLP_OP_MODE); + op_mode |= TLP_OP_INT_LOOP; + WRITE_CSR(TLP_OP_MODE, op_mode); + config->crc_len = CFG_CRC_0; + } + + /* Check for disabling loopback thru Tulip chip. */ + if ((sc->config.loop_back == CFG_LOOP_TULIP) && + (config->loop_back != CFG_LOOP_TULIP)) + { + u_int32_t op_mode = READ_CSR(TLP_OP_MODE); + op_mode &= ~TLP_OP_LOOP_MODE; + WRITE_CSR(TLP_OP_MODE, op_mode); + config->crc_len = CFG_CRC_16; + } + } + +/* This is the core ioctl procedure. */ +/* It handles IOCTLs from lmcconfig(8). */ +/* It must not run when card watchdogs run. */ +/* Called from a syscall (user context; no spinlocks). */ +/* This procedure can SLEEP. */ +static int +core_ioctl(softc_t *sc, u_long cmd, caddr_t data) + { + struct iohdr *iohdr = (struct iohdr *) data; + struct ioctl *ioctl = (struct ioctl *) data; + struct status *status = (struct status *) data; + struct config *config = (struct config *) data; + int error = 0; + + /* All structs start with a string and a cookie. */ + if (((struct iohdr *)data)->cookie != NGM_LMC_COOKIE) + return EINVAL; + + while (TOP_TRYLOCK == 0) + { + sc->status.cntrs.lck_ioctl++; + SLEEP(10000); /* yield? */ + } + switch (cmd) + { + case LMCIOCGSTAT: + { + *status = sc->status; + iohdr->cookie = NGM_LMC_COOKIE; + break; + } + case LMCIOCGCFG: + { + *config = sc->config; + iohdr->cookie = NGM_LMC_COOKIE; + break; + } + case LMCIOCSCFG: + { + if ((error = CHECK_CAP)) break; + config_proto(sc, config); + sc->config = *config; + sc->card->config(sc); + break; + } + case LMCIOCREAD: + { + if (ioctl->cmd == IOCTL_RW_PCI) + { + if (ioctl->address > 252) { error = EFAULT; break; } + ioctl->data = READ_PCI_CFG(sc, ioctl->address); + } + else if (ioctl->cmd == IOCTL_RW_CSR) + { + if (ioctl->address > 15) { error = EFAULT; break; } + ioctl->data = READ_CSR(ioctl->address*TLP_CSR_STRIDE); + } + else if (ioctl->cmd == IOCTL_RW_SROM) + { + if (ioctl->address > 63) { error = EFAULT; break; } + ioctl->data = read_srom(sc, ioctl->address); + } + else if (ioctl->cmd == IOCTL_RW_BIOS) + ioctl->data = read_bios(sc, ioctl->address); + else if (ioctl->cmd == IOCTL_RW_MII) + ioctl->data = read_mii(sc, ioctl->address); + else if (ioctl->cmd == IOCTL_RW_FRAME) + ioctl->data = read_framer(sc, ioctl->address); + else + error = EINVAL; + break; + } + case LMCIOCWRITE: + { + if ((error = CHECK_CAP)) break; + if (ioctl->cmd == IOCTL_RW_PCI) + { + if (ioctl->address > 252) { error = EFAULT; break; } + WRITE_PCI_CFG(sc, ioctl->address, ioctl->data); + } + else if (ioctl->cmd == IOCTL_RW_CSR) + { + if (ioctl->address > 15) { error = EFAULT; break; } + WRITE_CSR(ioctl->address*TLP_CSR_STRIDE, ioctl->data); + } + else if (ioctl->cmd == IOCTL_RW_SROM) + { + if (ioctl->address > 63) { error = EFAULT; break; } + write_srom(sc, ioctl->address, ioctl->data); /* can sleep */ + } + else if (ioctl->cmd == IOCTL_RW_BIOS) + { + if (ioctl->address == 0) erase_bios(sc); + write_bios(sc, ioctl->address, ioctl->data); /* can sleep */ + } + else if (ioctl->cmd == IOCTL_RW_MII) + write_mii(sc, ioctl->address, ioctl->data); + else if (ioctl->cmd == IOCTL_RW_FRAME) + write_framer(sc, ioctl->address, ioctl->data); + else if (ioctl->cmd == IOCTL_WO_SYNTH) + write_synth(sc, (struct synth *)&ioctl->data); + else if (ioctl->cmd == IOCTL_WO_DAC) + { + write_dac(sc, 0x9002); /* set Vref = 2.048 volts */ + write_dac(sc, ioctl->data & 0xFFF); + } + else + error = EINVAL; + break; + } + case LMCIOCTL: + { + if ((error = CHECK_CAP)) break; + if (ioctl->cmd == IOCTL_XILINX_RESET) + { + reset_xilinx(sc); + sc->card->config(sc); + } + else if (ioctl->cmd == IOCTL_XILINX_ROM) + { + load_xilinx_from_rom(sc); /* can sleep */ + sc->card->config(sc); + } + else if (ioctl->cmd == IOCTL_XILINX_FILE) + { + /* load_xilinx_from_file() can sleep. */ + error = load_xilinx_from_file(sc, ioctl->ucode, ioctl->data); + if (error != 0) load_xilinx_from_rom(sc); /* try the rom */ + sc->card->config(sc); + set_status(sc, (error==0)); /* XXX */ + } + else if (ioctl->cmd == IOCTL_RESET_CNTRS) + { + memset(&sc->status.cntrs, 0, sizeof(struct event_cntrs)); + microtime(&sc->status.cntrs.reset_time); + } + else + error = sc->card->ioctl(sc, ioctl); /* can sleep */ + break; + } + default: + error = EINVAL; + break; + } + TOP_UNLOCK; + + return error; + } + +/* This is the core watchdog procedure. */ +/* It calculates link speed, and calls the card-specific watchdog code. */ +/* Calls interrupt() in case one got lost; also kick-starts the device. */ +/* ioctl syscalls and card watchdog routines must be interlocked. */ +/* This procedure must not sleep. */ +static void +core_watchdog(softc_t *sc) + { + /* Read and restart the Tulip timer. */ + u_int32_t tx_speed = READ_CSR(TLP_TIMER); + WRITE_CSR(TLP_TIMER, 0xFFFF); + + /* Measure MII clock using a timer in the Tulip chip. + * This timer counts transmitter bits divided by 4096. + * Since this is called once a second the math is easy. + * This is only correct when the link is NOT sending pkts. + * On a fully-loaded link, answer will be HALF actual rate. + * Clock rate during pkt is HALF clk rate between pkts. + * Measuring clock rate really measures link utilization! + */ + sc->status.tx_speed = (0xFFFF - (tx_speed & 0xFFFF)) << 12; + + /* The first status reset time is when the calendar clock is set. */ + if (sc->status.cntrs.reset_time.tv_sec < 1000) + microtime(&sc->status.cntrs.reset_time); + + /* Update hardware (operational) status. */ + /* Call the card-specific watchdog routines. */ + if (TOP_TRYLOCK != 0) + { + sc->status.oper_status = sc->card->watchdog(sc); + + /* Increment a counter which tells user-land */ + /* observers that SNMP state has been updated. */ + sc->status.ticks++; + + TOP_UNLOCK; + } + else + sc->status.cntrs.lck_watch++; + + /* In case an interrupt gets lost... */ + user_interrupt(sc, 1); + } + +#if IFNET + +/* Called from a syscall (user context; no spinlocks). */ +static int +raw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) + { + struct ifreq *ifr = (struct ifreq *) data; + int error = 0; + + switch (cmd) + { +# if (__FreeBSD__ && DEVICE_POLLING) /* XXX necessary? */ + case SIOCSIFCAP: +# endif + case SIOCSIFDSTADDR: + case SIOCAIFADDR: + case SIOCSIFFLAGS: +#if 0 + case SIOCADDMULTI: + case SIOCDELMULTI: + break; +#endif + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; /* a Unix tradition */ + break; + case SIOCSIFMTU: + ifp->if_mtu = ifr->ifr_mtu; + break; + default: + error = EINVAL; + break; + } + return error; + } + +/* Called from a syscall (user context; no spinlocks). */ +static int +ifnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) + { + softc_t *sc = IFP2SC(ifp); +# if __OpenBSD__ + struct ifreq *ifr = (struct ifreq *) data; +# endif + int error = 0; + + switch (cmd) + { + /* Catch the IOCTLs used by lmcconfig. */ + case LMCIOCGSTAT: + case LMCIOCGCFG: + case LMCIOCSCFG: + case LMCIOCREAD: + case LMCIOCWRITE: + case LMCIOCTL: + error = core_ioctl(sc, cmd, data); + break; +# if __OpenBSD__ + /* Catch the IOCTLs used by ifconfig. */ + case SIOCSIFMEDIA: + if ((error = CHECK_CAP)) break; + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->ifm, cmd); + break; + case SIOCSIFTIMESLOT: + if ((error = CHECK_CAP)) break; + if (sc->status.card_type == TLP_CSID_T1E1) + { + struct config config = sc->config; + if ((error = copyin(ifr->ifr_data, &config.time_slots, + sizeof config.time_slots))) break; + config.iohdr.cookie = NGM_LMC_COOKIE; + error = core_ioctl(sc, LMCIOCSCFG, (caddr_t)&config); + } + else + error = EINVAL; + break; + case SIOCGIFTIMESLOT: + if (sc->status.card_type == TLP_CSID_T1E1) + error = copyout(&sc->config.time_slots, ifr->ifr_data, + sizeof sc->config.time_slots); + else + error = EINVAL; + break; +# endif + /* Pass the rest to the line protocol. */ + default: + if (sc->config.line_pkg == PKG_RAWIP) + error = raw_ioctl(ifp, cmd, data); + else +# if NSPPP + error = sppp_ioctl(ifp, cmd, data); +# elif P2P + error = p2p_ioctl(ifp, cmd, data); +# else + error = EINVAL; +# endif + break; + } + + if (DRIVER_DEBUG && (error!=0)) + printf("%s: ifnet_ioctl; cmd=0x%08lx error=%d\n", + NAME_UNIT, cmd, error); + + return error; + } + +/* Called from a syscall (user context; no spinlocks). */ +static void +ifnet_start(struct ifnet *ifp) + { + softc_t *sc = IFP2SC(ifp); + + /* Start the transmitter; incoming pkts are NOT processed. */ + user_interrupt(sc, 0); + } + +/* sppp and p2p replace this with their own proc. */ +/* RAWIP mode is the only time this is used. */ +/* Called from a syscall (user context; no spinlocks). */ +static int +raw_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt) + { + softc_t *sc = IFP2SC(ifp); + int error = 0; + + /* Fail if the link is down. */ + if (sc->status.oper_status != STATUS_UP) + { + m_freem(m); + sc->status.cntrs.odiscards++; + if (DRIVER_DEBUG) + printf("%s: raw_output: tx pkt discarded: link down\n", NAME_UNIT); + return ENETDOWN; + } + +# if NETGRAPH + /* Netgraph has priority over the ifnet kernel interface. */ + if (sc->ng_hook != NULL) + { + m_freem(m); + sc->status.cntrs.odiscards++; + if (DRIVER_DEBUG) + printf("%s: raw_output: tx pkt discarded: netgraph active\n", NAME_UNIT); + return EBUSY; + } +# endif + + /* raw_output() ENQUEUEs in a syscall or softirq. */ + /* txintr_setup() DEQUEUEs in a hard interrupt. */ + /* Some BSD QUEUE routines are not interrupt-safe. */ + { + DISABLE_INTR; +# if (__FreeBSD_version >= 503000) + IFQ_ENQUEUE(&ifp->if_snd, m, error); +# else + IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); +# endif + ENABLE_INTR; + } + + if (error==0) + user_interrupt(sc, 0); /* start the transmitter */ + else + { + m_freem(m); + sc->status.cntrs.odiscards++; + if (DRIVER_DEBUG) + printf("%s: raw_output: IFQ_ENQUEUE() failed; error %d\n", + NAME_UNIT, error); + } + + return error; + } + +/* Called from a softirq once a second. */ +static void +ifnet_watchdog(struct ifnet *ifp) + { + softc_t *sc = IFP2SC(ifp); + u_int8_t old_oper_status = sc->status.oper_status; + struct event_cntrs *cntrs = &sc->status.cntrs; + + core_watchdog(sc); /* updates oper_status */ + +#if NETGRAPH + if (sc->ng_hook != NULL) + { + sc->status.line_pkg = PKG_NG; + sc->status.line_prot = 0; + } + else +#endif + if (sc->config.line_pkg == PKG_RAWIP) + { + sc->status.line_pkg = PKG_RAWIP; + sc->status.line_prot = PROT_IP_HDLC; + } + else + { +# if P2P + /* Notice change in link status. */ + if ((old_oper_status != sc->status.oper_status) && (sc->p2p->p2p_modem)) + (*sc->p2p->p2p_modem)(sc->p2p, sc->status.oper_status==STATUS_UP); + + /* Notice change in line protocol. */ + sc->status.line_pkg = PKG_P2P; + switch (sc->ifp->if_type) + { + case IFT_PPP: + sc->status.line_prot = PROT_PPP; + break; + case IFT_PTPSERIAL: + sc->status.line_prot = PROT_C_HDLC; + break; + case IFT_FRELAY: + sc->status.line_prot = PROT_FRM_RLY; + break; + default: + sc->status.line_prot = 0; + break; + } + +# elif NSPPP + /* Notice change in link status. */ + if ((old_oper_status != STATUS_UP) && + (sc->status.oper_status == STATUS_UP)) /* link came up */ + sppp_tls(sc->sppp); + if ((old_oper_status == STATUS_UP) && + (sc->status.oper_status != STATUS_UP)) /* link went down */ + sppp_tlf(sc->sppp); + + /* Notice change in line protocol. */ + sc->status.line_pkg = PKG_SPPP; +# if __FreeBSD__ + if (sc->sppp->pp_flags & PP_FR) + sc->status.line_prot = PROT_FRM_RLY; + else if (sc->ifp->if_flags & IFF_LINK2) +# elif (__NetBSD__ || __OpenBSD__) + if (sc->sppp->pp_flags & PP_CISCO) +# endif + sc->status.line_prot = PROT_C_HDLC; + else + sc->status.line_prot = PROT_PPP; + +# else + /* Suppress compiler warning. */ + if (old_oper_status == STATUS_UP); +# endif + } + + /* Copy statistics from sc to ifp. */ + ifp->if_baudrate = sc->status.tx_speed; + ifp->if_ipackets = cntrs->ipackets; + ifp->if_opackets = cntrs->opackets; + ifp->if_ibytes = cntrs->ibytes; + ifp->if_obytes = cntrs->obytes; + ifp->if_ierrors = cntrs->ierrors; + ifp->if_oerrors = cntrs->oerrors; + ifp->if_iqdrops = cntrs->idiscards; + +# if ((__FreeBSD_version >= 500000) || __OpenBSD__ || __NetBSD__) + if (sc->status.oper_status == STATUS_UP) + ifp->if_link_state = LINK_STATE_UP; + else + ifp->if_link_state = LINK_STATE_DOWN; +# endif + + /* Call this procedure again after one second. */ + ifp->if_timer = 1; + } + +# if __OpenBSD__ + +/* Callback from ifmedia. */ +static int +ifmedia_change(struct ifnet *ifp) + { + softc_t *sc = IFP2SC(ifp); + struct config config = sc->config; + int media = sc->ifm.ifm_media; + int error; + + /* ifconfig lmc0 media t1 */ + if (sc->status.card_type == TLP_CSID_T3) + { + if ((media & IFM_TMASK) == IFM_TDM_T3) + config.format = CFG_FORMAT_T3CPAR; + else if ((media & IFM_TMASK) == IFM_TDM_T3_M13) + config.format = CFG_FORMAT_T3M13; + } + else if (sc->status.card_type == TLP_CSID_T1E1) + { + if ((media & IFM_TMASK) == IFM_TDM_T1) + config.format = CFG_FORMAT_T1ESF; + else if ((media & IFM_TMASK) == IFM_TDM_T1_AMI) + config.format = CFG_FORMAT_T1SF; + else if ((media & IFM_TMASK) == IFM_TDM_E1) + config.format = CFG_FORMAT_E1NONE; + else if ((media & IFM_TMASK) == IFM_TDM_E1_G704) + config.format = CFG_FORMAT_E1FASCRC; + } + + /* ifconfig lmc0 mediaopt loopback */ + if (media & IFM_LOOP) + config.loop_back = CFG_LOOP_TULIP; + else + config.loop_back = CFG_LOOP_NONE; + + /* ifconfig lmc0 mediaopt crc16 */ + if (media & IFM_TDM_HDLC_CRC16) + config.crc_len = CFG_CRC_16; + else + config.crc_len = CFG_CRC_32; + + /* Set ConFiGuration. */ + config.iohdr.cookie = NGM_LMC_COOKIE; + error = core_ioctl(sc, LMCIOCSCFG, (caddr_t)&config); + + return error; + } + +/* Callback from ifmedia. */ +static void +ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) + { + softc_t *sc = IFP2SC(ifp); + + /* ifconfig wants to know if the hardware link is up. */ + ifmr->ifm_status = IFM_AVALID; + if (sc->status.oper_status == STATUS_UP) + ifmr->ifm_status |= IFM_ACTIVE; + + ifmr->ifm_active = sc->ifm.ifm_cur->ifm_media; + + if (sc->config.loop_back != CFG_LOOP_NONE) + ifmr->ifm_active |= IFM_LOOP; + + if (sc->config.crc_len == CFG_CRC_16) + ifmr->ifm_active |= IFM_TDM_HDLC_CRC16; + } + +# endif /* __OpenBSD__ */ + +static void +setup_ifnet(struct ifnet *ifp) + { + softc_t *sc = ifp->if_softc; + + /* Initialize the generic network interface. */ + /* Note similarity to linux's setup_netdev(). */ + ifp->if_flags = IFF_POINTOPOINT; + ifp->if_flags |= IFF_RUNNING; + ifp->if_ioctl = ifnet_ioctl; + ifp->if_start = ifnet_start; /* sppp changes this */ + ifp->if_output = raw_output; /* sppp & p2p change this */ + ifp->if_input = raw_input; + ifp->if_watchdog = ifnet_watchdog; + ifp->if_timer = 1; + ifp->if_mtu = MAX_DESC_LEN; /* sppp & p2p change this */ + ifp->if_type = IFT_PTPSERIAL; /* p2p changes this */ + +# if (__FreeBSD__ && DEVICE_POLLING) + ifp->if_capabilities |= IFCAP_POLLING; +# if (__FreeBSD_version < 500000) + ifp->if_capenable |= IFCAP_POLLING; +# endif +# endif + + /* Every OS does it differently! */ +# if (__FreeBSD__ && (__FreeBSD_version < 502000)) + (const char *)ifp->if_name = device_get_name(sc->dev); + ifp->if_unit = device_get_unit(sc->dev); +# elif (__FreeBSD_version >= 502000) + ifp->if_dname = device_get_name(sc->dev); + ifp->if_dunit = device_get_unit(sc->dev); + strlcpy(ifp->if_xname, device_get_nameunit(sc->dev), IFNAMSIZ); +# elif __NetBSD__ + strcpy(ifp->if_xname, sc->dev.dv_xname); +# elif __OpenBSD__ + bcopy(sc->dev.dv_xname, ifp->if_xname, IFNAMSIZ); +# elif __bsdi__ + ifp->if_name = sc->dev.dv_cfdata->cf_driver->cd_name; + ifp->if_unit = sc->dev.dv_unit; +# endif + } + +static int +ifnet_attach(softc_t *sc) + { +# if (__FreeBSD_version >= 600000) + sc->ifp = if_alloc(NSPPP ? IFT_PPP : IFT_OTHER); + if (sc->ifp == NULL) return ENOMEM; +# endif +# if NSPPP +# if (__FreeBSD_version >= 600000) + sc->sppp = sc->ifp->if_l2com; +# else + sc->ifp = &sc->spppcom.pp_if; + sc->sppp = &sc->spppcom; +# endif +# elif P2P + sc->ifp = &sc->p2pcom.p2p_if; + sc->p2p = &sc->p2pcom; +# elif (__FreeBSD_version < 600000) + sc->ifp = &sc->ifnet; +# endif + + /* Initialize the network interface struct. */ + sc->ifp->if_softc = sc; + setup_ifnet(sc->ifp); + + /* ALTQ output queue initialization. */ + IFQ_SET_MAXLEN(&sc->ifp->if_snd, SNDQ_MAXLEN); + IFQ_SET_READY(&sc->ifp->if_snd); + + /* Attach to the ifnet kernel interface. */ + if_attach(sc->ifp); + +# if ((__NetBSD_Version__ >= 106000000) || (OpenBSD >= 200211)) + if_alloc_sadl(sc->ifp); +# endif + + /* Attach Berkeley Packet Filter. */ + LMC_BPF_ATTACH(DLT_RAW, 0); + +# if __OpenBSD__ + /* Initialize ifmedia mechanism. */ + ifmedia_init(&sc->ifm, IFM_OMASK | IFM_GMASK | IFM_IMASK, + ifmedia_change, ifmedia_status); + if (sc->status.card_type == TLP_CSID_T3) + { + ifmedia_add(&sc->ifm, IFM_TDM | IFM_TDM_T3, 0, NULL); + ifmedia_add(&sc->ifm, IFM_TDM | IFM_TDM_T3_M13, 0, NULL); + ifmedia_set(&sc->ifm, IFM_TDM | IFM_TDM_T3); + } + else if (sc->status.card_type == TLP_CSID_T1E1) + { + ifmedia_add(&sc->ifm, IFM_TDM | IFM_TDM_T1, 0, NULL); + ifmedia_add(&sc->ifm, IFM_TDM | IFM_TDM_T1_AMI, 0, NULL); + ifmedia_add(&sc->ifm, IFM_TDM | IFM_TDM_E1, 0, NULL); + ifmedia_add(&sc->ifm, IFM_TDM | IFM_TDM_E1_G704, 0, NULL); + ifmedia_set(&sc->ifm, IFM_TDM | IFM_TDM_T1); + } + else if ((sc->status.card_type == TLP_CSID_HSSI) || + (sc->status.card_type == TLP_CSID_SSI)) + { + ifmedia_add(&sc->ifm, IFM_TDM | IFM_NONE, 0, NULL); + ifmedia_set(&sc->ifm, IFM_TDM | IFM_NONE); + } +# endif /* __OpenBSD__ */ + + return 0; + } + +static void +ifnet_detach(softc_t *sc) + { +# if __OpenBSD__ + ifmedia_delete_instance(&sc->ifm, IFM_INST_ANY); +# endif + +# if (__FreeBSD__ && DEVICE_POLLING) + if (sc->ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(sc->ifp); +# endif + + /* Detach Berkeley Packet Filter. */ + LMC_BPF_DETACH; + +# if ((__NetBSD_Version__ >= 106000000) || (OpenBSD >= 200211)) + if_free_sadl(sc->ifp); +# endif + + /* Detach from the ifnet kernel interface. */ + if_detach(sc->ifp); + +# if (__FreeBSD_version >= 600000) + if_free_type(sc->ifp, NSPPP ? IFT_PPP : IFT_OTHER); +# endif + } + +#endif /* IFNET */ + +#if NETGRAPH + +/* Netgraph changed significantly between FreeBSD-4 and -5. */ +/* These are backward compatibility hacks for FreeBSD-4. */ +# if (__FreeBSD_version >= 500000) +/* These next two macros should be added to netgraph */ +# define NG_TYPE_REF(type) atomic_add_int(&(type)->refs, 1) +# define NG_TYPE_UNREF(type) \ +do { \ + if ((type)->refs == 1) \ + ng_rmtype(type); \ + else \ + atomic_subtract_int(&(type)->refs, 1); \ + } while (0) +# else /* FreeBSD-4 */ +# define NGI_GET_MSG(item, msg) /* nothing */ +# define NG_HOOK_FORCE_QUEUE(hook) /* nothing */ +# define NG_TYPE_REF(type) atomic_add_int(&(type)->refs, 1) +# define NG_TYPE_UNREF(type) \ +do { \ + if ((type)->refs == 1) \ + LIST_REMOVE(type, types); \ + else \ + atomic_subtract_int(&(type)->refs, 1); \ + } while (0) +# endif + +/* It is an error to construct new copies of this Netgraph node. */ +/* All instances are constructed by ng_attach and are persistent. */ +# if (__FreeBSD_version >= 500000) +static int ng_constructor(node_p node) { return EINVAL; } +# else /* FreeBSD-4 */ +static int ng_constructor(node_p *node) { return EINVAL; } +# endif + +/* Incoming Netgraph control message. */ +# if (__FreeBSD_version >= 500000) +static int +ng_rcvmsg(node_p node, item_p item, hook_p lasthook) + { + struct ng_mesg *msg; +# else /* FreeBSD-4 */ +static int +ng_rcvmsg(node_p node, struct ng_mesg *msg, + const char *retaddr, struct ng_mesg **rptr) + { +# endif + struct ng_mesg *resp = NULL; + softc_t *sc = NG_NODE_PRIVATE(node); + int error = 0; + + NGI_GET_MSG(item, msg); + if (msg->header.typecookie == NGM_LMC_COOKIE) + { + switch (msg->header.cmd) + { + case LMCIOCGSTAT: + case LMCIOCGCFG: + case LMCIOCSCFG: + case LMCIOCREAD: + case LMCIOCWRITE: + case LMCIOCTL: + { + /* Call the core ioctl procedure. */ + error = core_ioctl(sc, msg->header.cmd, msg->data); + if ((msg->header.cmd & IOC_OUT) != 0) + { /* synchronous response */ + NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + + IOCPARM_LEN(msg->header.cmd), M_NOWAIT); + if (resp == NULL) + error = ENOMEM; + else + memcpy(resp->data, msg->data, IOCPARM_LEN(msg->header.cmd)); + } + break; + } + default: + error = EINVAL; + break; + } + } + else if ((msg->header.typecookie == NGM_GENERIC_COOKIE) && + (msg->header.cmd == NGM_TEXT_STATUS)) + { /* synchronous response */ + NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + + NG_TEXTRESPONSE, M_NOWAIT); + if (resp == NULL) + error = ENOMEM; + else + { + char *s = resp->data; + sprintf(s, "Card type = <%s>\n" + "This driver considers the link to be %s.\n" + "Use lmcconfig to configure this interface.\n", + sc->dev_desc, (sc->status.oper_status==STATUS_UP) ? "UP" : "DOWN"); + resp->header.arglen = strlen(s) +1; + } + } + else +/* Netgraph should be able to read and write these + * parameters with text-format control messages: + * SSI HSSI T1E1 T3 + * crc crc crc crc + * loop loop loop loop + * clksrc clksrc + * dte dte format format + * synth synth cablen cablen + * cable timeslot scram + * gain + * pulse + * lbo + * Someday I'll implement this... + */ + error = EINVAL; + + /* Handle synchronous response. */ +# if (__FreeBSD_version >= 500000) + NG_RESPOND_MSG(error, node, item, resp); + NG_FREE_MSG(msg); +# else /* FreeBSD-4 */ + if (rptr != NULL) + *rptr = resp; + else if (resp != NULL) + FREE(resp, M_NETGRAPH); + FREE(msg, M_NETGRAPH); +# endif + + return error; + } + +/* This is a persistent netgraph node. */ +static int +ng_shutdown(node_p node) + { +# if (__FreeBSD_version >= 500000) + /* unless told to really die, bounce back to life */ + if ((node->nd_flags & NG_REALLY_DIE)==0) + node->nd_flags &= ~NG_INVALID; /* bounce back to life */ +# else /* FreeBSD-4 */ + ng_cutlinks(node); + node->flags &= ~NG_INVALID; /* bounce back to life */ +# endif + + return 0; + } + +/* ng_disconnect is the opposite of this procedure. */ +static int +ng_newhook(node_p node, hook_p hook, const char *name) + { + softc_t *sc = NG_NODE_PRIVATE(node); + + /* Hook name must be 'rawdata'. */ + if (strncmp(name, "rawdata", 7) != 0) return EINVAL; + + /* Is our hook connected? */ + if (sc->ng_hook != NULL) return EBUSY; + + /* Accept the hook. */ + sc->ng_hook = hook; + + return 0; + } + +/* Both ends have accepted their hooks and the links have been made. */ +/* This is the last chance to reject the connection request. */ +static int +ng_connect(hook_p hook) + { + /* Probably not at splnet, force outward queueing. (huh?) */ + NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); + return 0; /* always accept */ + } + +/* Receive data in mbufs from another Netgraph node. */ +/* Transmit an mbuf-chain on the communication link. */ +/* This procedure is very similar to raw_output(). */ +/* Called from a syscall (user context; no spinlocks). */ +# if (__FreeBSD_version >= 500000) +static int +ng_rcvdata(hook_p hook, item_p item) + { + softc_t *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + int error = 0; + struct mbuf *m; + meta_p meta = NULL; + + NGI_GET_M(item, m); + NGI_GET_META(item, meta); + NG_FREE_ITEM(item); +# else /* FreeBSD-4 */ +static int +ng_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) + { + softc_t *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + int error = 0; +# endif + + /* This macro must not store into meta! */ + NG_FREE_META(meta); + + /* Fail if the link is down. */ + if (sc->status.oper_status != STATUS_UP) + { + m_freem(m); + sc->status.cntrs.odiscards++; + if (DRIVER_DEBUG) + printf("%s: ng_rcvdata: tx pkt discarded: link down\n", NAME_UNIT); + return ENETDOWN; + } + + /* ng_rcvdata() ENQUEUEs in a syscall or softirq. */ + /* txintr_setup() DEQUEUEs in a hard interrupt. */ + /* Some BSD QUEUE routines are not interrupt-safe. */ + { + DISABLE_INTR; +# if (__FreeBSD_version >= 503000) + if (meta==NULL) + IFQ_ENQUEUE(&sc->ng_sndq, m, error); + else + IFQ_ENQUEUE(&sc->ng_fastq, m, error); +# else + if (meta==NULL) + IFQ_ENQUEUE(&sc->ng_sndq, m, NULL, error); + else + IFQ_ENQUEUE(&sc->ng_fastq, m, NULL, error); +# endif + ENABLE_INTR; + } + + if (error==0) + user_interrupt(sc, 0); /* start the transmitter */ + else + { + m_freem(m); + sc->status.cntrs.odiscards++; + if (DRIVER_DEBUG) + printf("%s: ng_rcvdata: IFQ_ENQUEUE() failed; error %d\n", + NAME_UNIT, error); + } + + return error; + } + +/* ng_newhook is the opposite of this procedure, not */ +/* ng_connect, as you might expect from the names. */ +static int +ng_disconnect(hook_p hook) + { + softc_t *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + + /* Disconnect the hook. */ + sc->ng_hook = NULL; + + return 0; + } + +static +struct ng_type ng_type = + { + .version = NG_ABI_VERSION, + .name = NG_LMC_NODE_TYPE, + .mod_event = NULL, + .constructor = ng_constructor, + .rcvmsg = ng_rcvmsg, +# if (__FreeBSD_version >=503000) + .close = NULL, +# endif + .shutdown = ng_shutdown, + .newhook = ng_newhook, + .findhook = NULL, + .connect = ng_connect, + .rcvdata = ng_rcvdata, +# if (__FreeBSD__ && (__FreeBSD_version < 500000)) + .rcvdataq = ng_rcvdata, +# endif + .disconnect = ng_disconnect, + }; + +# if (IFNET == 0) +/* Called from a softirq once a second. */ +static void +ng_watchdog(void *arg) + { + softc_t *sc = arg; + + /* Call the core watchdog procedure. */ + core_watchdog(sc); + + /* Set line protocol and package status. */ + sc->status.line_pkg = PKG_NG; + sc->status.line_prot = 0; + + /* Call this procedure again after one second. */ + callout_reset(&sc->ng_callout, hz, ng_watchdog, sc); + } +# endif + +/* Attach to the Netgraph kernel interface (/sys/netgraph). + * It is called once for each physical card during device attach. + * This is effectively ng_constructor. + */ +static int +ng_attach(softc_t *sc) + { + int error; + + /* If this node type is not known to Netgraph then register it. */ + if (ng_type.refs == 0) /* or: if (ng_findtype(&ng_type) == NULL) */ + { + if ((error = ng_newtype(&ng_type))) + { + printf("%s: ng_newtype() failed; error %d\n", NAME_UNIT, error); + return error; + } + } + else + NG_TYPE_REF(&ng_type); + + /* Call the superclass node constructor. */ + if ((error = ng_make_node_common(&ng_type, &sc->ng_node))) + { + NG_TYPE_UNREF(&ng_type); + printf("%s: ng_make_node_common() failed; error %d\n", NAME_UNIT, error); + return error; + } + + /* Associate a name with this netgraph node. */ + if ((error = ng_name_node(sc->ng_node, NAME_UNIT))) + { + NG_NODE_UNREF(sc->ng_node); + NG_TYPE_UNREF(&ng_type); + printf("%s: ng_name_node() failed; error %d\n", NAME_UNIT, error); + return error; + } + +# if (__FreeBSD_version >= 500000) + /* Initialize the send queue mutexes. */ + mtx_init(&sc->ng_sndq.ifq_mtx, NAME_UNIT, "sndq", MTX_DEF); + mtx_init(&sc->ng_fastq.ifq_mtx, NAME_UNIT, "fastq", MTX_DEF); +# endif + + /* Put a backpointer to the softc in the netgraph node. */ + NG_NODE_SET_PRIVATE(sc->ng_node, sc); + + /* ALTQ output queue initialization. */ + IFQ_SET_MAXLEN(&sc->ng_fastq, SNDQ_MAXLEN); + IFQ_SET_READY(&sc->ng_fastq); + IFQ_SET_MAXLEN(&sc->ng_sndq, SNDQ_MAXLEN); + IFQ_SET_READY(&sc->ng_sndq); + + /* If ifnet is present, it will call watchdog. */ + /* Otherwise, arrange to call watchdog here. */ +# if (IFNET == 0) + /* Arrange to call ng_watchdog() once a second. */ +# if (__FreeBSD_version >= 500000) + callout_init(&sc->ng_callout, 0); +# else /* FreeBSD-4 */ + callout_init(&sc->ng_callout); +# endif + callout_reset(&sc->ng_callout, hz, ng_watchdog, sc); +# endif + + return 0; + } + +static void +ng_detach(softc_t *sc) + { +# if (IFNET == 0) + callout_stop(&sc->ng_callout); +# endif +# if (__FreeBSD_version >= 500000) + mtx_destroy(&sc->ng_sndq.ifq_mtx); + mtx_destroy(&sc->ng_fastq.ifq_mtx); + ng_rmnode_self(sc->ng_node); /* free hook */ + NG_NODE_UNREF(sc->ng_node); /* free node */ + NG_TYPE_UNREF(&ng_type); +# else /* FreeBSD-4 */ + ng_unname(sc->ng_node); /* free name */ + ng_cutlinks(sc->ng_node); /* free hook */ + NG_NODE_UNREF(sc->ng_node); /* free node */ + NG_TYPE_UNREF(&ng_type); +# endif + } + +#endif /* NETGRAPH */ + +/* The next few procedures initialize the card. */ + +/* Returns 0 on success; error code on failure. */ +static int +startup_card(softc_t *sc) + { + int num_rx_descs, error = 0; + u_int32_t tlp_bus_pbl, tlp_bus_cal, tlp_op_tr; + u_int32_t tlp_cfdd, tlp_cfcs; + u_int32_t tlp_cflt, tlp_csid, tlp_cfit; + + /* Make sure the COMMAND bits are reasonable. */ + tlp_cfcs = READ_PCI_CFG(sc, TLP_CFCS); + tlp_cfcs &= ~TLP_CFCS_MWI_ENABLE; + tlp_cfcs |= TLP_CFCS_BUS_MASTER; + tlp_cfcs |= TLP_CFCS_MEM_ENABLE; + tlp_cfcs |= TLP_CFCS_IO_ENABLE; + tlp_cfcs |= TLP_CFCS_PAR_ERROR; + tlp_cfcs |= TLP_CFCS_SYS_ERROR; + WRITE_PCI_CFG(sc, TLP_CFCS, tlp_cfcs); + + /* Set the LATENCY TIMER to the recommended value, */ + /* and make sure the CACHE LINE SIZE is reasonable. */ + tlp_cfit = READ_PCI_CFG(sc, TLP_CFIT); + tlp_cflt = READ_PCI_CFG(sc, TLP_CFLT); + tlp_cflt &= ~TLP_CFLT_LATENCY; + tlp_cflt |= (tlp_cfit & TLP_CFIT_MAX_LAT)>>16; + /* "prgmbl burst length" and "cache alignment" used below. */ + switch(tlp_cflt & TLP_CFLT_CACHE) + { + case 8: /* 8 bytes per cache line */ + { tlp_bus_pbl = 32; tlp_bus_cal = 1; break; } + case 16: + { tlp_bus_pbl = 32; tlp_bus_cal = 2; break; } + case 32: + { tlp_bus_pbl = 32; tlp_bus_cal = 3; break; } + default: + { + tlp_bus_pbl = 32; tlp_bus_cal = 1; + tlp_cflt &= ~TLP_CFLT_CACHE; + tlp_cflt |= 8; + break; + } + } + WRITE_PCI_CFG(sc, TLP_CFLT, tlp_cflt); + + /* Make sure SNOOZE and SLEEP modes are disabled. */ + tlp_cfdd = READ_PCI_CFG(sc, TLP_CFDD); + tlp_cfdd &= ~TLP_CFDD_SLEEP; + tlp_cfdd &= ~TLP_CFDD_SNOOZE; + WRITE_PCI_CFG(sc, TLP_CFDD, tlp_cfdd); + DELAY(11*1000); /* Tulip wakes up in 10 ms max */ + + /* Software Reset the Tulip chip; stops DMA and Interrupts. */ + /* This does not change the PCI config regs just set above. */ + WRITE_CSR(TLP_BUS_MODE, TLP_BUS_RESET); /* self-clearing */ + DELAY(5); /* Tulip is dead for 50 PCI cycles after reset. */ + + /* Reset the Xilinx Field Programmable Gate Array. */ + reset_xilinx(sc); /* side effect: turns on all four LEDs */ + + /* Configure card-specific stuff (framers, line interfaces, etc.). */ + sc->card->config(sc); + + /* Initializing cards can glitch clocks and upset fifos. */ + /* Reset the FIFOs between the Tulip and Xilinx chips. */ + set_mii16_bits(sc, MII16_FIFO); + clr_mii16_bits(sc, MII16_FIFO); + + /* Initialize the PCI busmode register. */ + /* The PCI bus cycle type "Memory Write and Invalidate" does NOT */ + /* work cleanly in any version of the 21140A, so don't enable it! */ + WRITE_CSR(TLP_BUS_MODE, + (tlp_bus_cal ? TLP_BUS_READ_LINE : 0) | + (tlp_bus_cal ? TLP_BUS_READ_MULT : 0) | + (tlp_bus_pbl<<TLP_BUS_PBL_SHIFT) | + (tlp_bus_cal<<TLP_BUS_CAL_SHIFT) | + ((BYTE_ORDER == BIG_ENDIAN) ? TLP_BUS_DESC_BIGEND : 0) | + ((BYTE_ORDER == BIG_ENDIAN) ? TLP_BUS_DATA_BIGEND : 0) | + TLP_BUS_DSL_VAL | + TLP_BUS_ARB); + + /* Pick number of RX descriptors and TX fifo threshold. */ + /* tx_threshold in bytes: 0=128, 1=256, 2=512, 3=1024 */ + tlp_csid = READ_PCI_CFG(sc, TLP_CSID); + switch(tlp_csid) + { + case TLP_CSID_HSSI: /* 52 Mb/s */ + case TLP_CSID_HSSIc: /* 52 Mb/s */ + case TLP_CSID_T3: /* 45 Mb/s */ + { num_rx_descs = 48; tlp_op_tr = 2; break; } + case TLP_CSID_SSI: /* 10 Mb/s */ + { num_rx_descs = 32; tlp_op_tr = 1; break; } + case TLP_CSID_T1E1: /* 2 Mb/s */ + { num_rx_descs = 16; tlp_op_tr = 0; break; } + default: + { num_rx_descs = 16; tlp_op_tr = 0; break; } + } + + /* Create DMA descriptors and initialize list head registers. */ + if ((error = create_ring(sc, &sc->txring, NUM_TX_DESCS))) return error; + WRITE_CSR(TLP_TX_LIST, sc->txring.dma_addr); + if ((error = create_ring(sc, &sc->rxring, num_rx_descs))) return error; + WRITE_CSR(TLP_RX_LIST, sc->rxring.dma_addr); + + /* Initialize the operating mode register. */ + WRITE_CSR(TLP_OP_MODE, TLP_OP_INIT | (tlp_op_tr<<TLP_OP_TR_SHIFT)); + + /* Read the missed frame register (result ignored) to zero it. */ + error = READ_CSR( TLP_MISSED); /* error is used as a bit-dump */ + + /* Disable rx watchdog and tx jabber features. */ + WRITE_CSR(TLP_WDOG, TLP_WDOG_INIT); + + /* Enable card interrupts. */ + WRITE_CSR(TLP_INT_ENBL, TLP_INT_TXRX); + + return 0; + } + +/* Stop DMA and Interrupts; free descriptors and buffers. */ +static void +shutdown_card(void *arg) + { + softc_t *sc = arg; + + /* Leave the LEDs in the state they were in after power-on. */ + led_on(sc, MII16_LED_ALL); + + /* Software reset the Tulip chip; stops DMA and Interrupts */ + WRITE_CSR(TLP_BUS_MODE, TLP_BUS_RESET); /* self-clearing */ + DELAY(5); /* Tulip is dead for 50 PCI cycles after reset. */ + + /* Disconnect from the PCI bus except for config cycles. */ + /* Hmmm; Linux syslogs a warning that IO and MEM are disabled. */ + WRITE_PCI_CFG(sc, TLP_CFCS, TLP_CFCS_MEM_ENABLE | TLP_CFCS_IO_ENABLE); + + /* Free the DMA descriptor rings. */ + destroy_ring(sc, &sc->txring); + destroy_ring(sc, &sc->rxring); + } + +/* Start the card and attach a kernel interface and line protocol. */ +static int +attach_card(softc_t *sc, const char *intrstr) + { + struct config config; + u_int32_t tlp_cfrv; + u_int16_t mii3; + u_int8_t *ieee; + int i, error = 0; + + /* Start the card. */ + if ((error = startup_card(sc))) return error; + + /* Attach a kernel interface. */ +#if NETGRAPH + if ((error = ng_attach(sc))) return error; + sc->flags |= FLAG_NETGRAPH; +#endif +#if IFNET + if ((error = ifnet_attach(sc))) return error; + sc->flags |= FLAG_IFNET; +#endif + + /* Attach a line protocol stack. */ + sc->config.line_pkg = PKG_RAWIP; + config = sc->config; /* get current config */ + config.line_pkg = 0; /* select external stack */ + config.line_prot = PROT_C_HDLC; + config.keep_alive = 1; + config_proto(sc, &config); /* reconfigure */ + sc->config = config; /* save new configuration */ + + /* Print interesting hardware-related things. */ + mii3 = read_mii(sc, 3); + tlp_cfrv = READ_PCI_CFG(sc, TLP_CFRV); + printf("%s: PCI rev %d.%d, MII rev %d.%d", NAME_UNIT, + (tlp_cfrv>>4) & 0xF, tlp_cfrv & 0xF, (mii3>>4) & 0xF, mii3 & 0xF); + ieee = (u_int8_t *)sc->status.ieee; + for (i=0; i<3; i++) sc->status.ieee[i] = read_srom(sc, 10+i); + printf(", IEEE addr %02x:%02x:%02x:%02x:%02x:%02x", + ieee[0], ieee[1], ieee[2], ieee[3], ieee[4], ieee[5]); + sc->card->ident(sc); + printf(" %s\n", intrstr); + + /* Print interesting software-related things. */ + printf("%s: Driver rev %d.%d.%d", NAME_UNIT, + DRIVER_MAJOR_VERSION, DRIVER_MINOR_VERSION, DRIVER_SUB_VERSION); + printf(", Options %s%s%s%s%s%s%s%s%s\n", + NETGRAPH ? "NETGRAPH " : "", GEN_HDLC ? "GEN_HDLC " : "", + NSPPP ? "SPPP " : "", P2P ? "P2P " : "", + ALTQ_PRESENT ? "ALTQ " : "", NBPFILTER ? "BPF " : "", + DEV_POLL ? "POLL " : "", IOREF_CSR ? "IO_CSR " : "MEM_CSR ", + (BYTE_ORDER == BIG_ENDIAN) ? "BIG_END " : "LITTLE_END "); + + /* Make the local hardware ready. */ + set_status(sc, 1); + + return 0; + } + +/* Detach from the kernel in all ways. */ +static void +detach_card(softc_t *sc) + { + struct config config; + + /* Make the local hardware NOT ready. */ + set_status(sc, 0); + + /* Detach external line protocol stack. */ + if (sc->config.line_pkg != PKG_RAWIP) + { + config = sc->config; + config.line_pkg = PKG_RAWIP; + config_proto(sc, &config); + sc->config = config; + } + + /* Detach kernel interfaces. */ +#if NETGRAPH + if (sc->flags & FLAG_NETGRAPH) + { + IFQ_PURGE(&sc->ng_fastq); + IFQ_PURGE(&sc->ng_sndq); + ng_detach(sc); + sc->flags &= ~FLAG_NETGRAPH; + } +#endif +#if IFNET + if (sc->flags & FLAG_IFNET) + { + IFQ_PURGE(&sc->ifp->if_snd); + ifnet_detach(sc); + sc->flags &= ~FLAG_IFNET; + } +#endif + + /* Reset the Tulip chip; stops DMA and Interrupts. */ + shutdown_card(sc); + } + +/* This is the I/O configuration interface for FreeBSD */ + +#if __FreeBSD__ + +static int +fbsd_probe(device_t dev) + { + u_int32_t cfid = pci_read_config(dev, TLP_CFID, 4); + u_int32_t csid = pci_read_config(dev, TLP_CSID, 4); + + /* Looking for a DEC 21140A chip on any Lan Media Corp card. */ + if (cfid != TLP_CFID_TULIP) return ENXIO; + switch (csid) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + device_set_desc(dev, HSSI_DESC); + break; + case TLP_CSID_T3: + device_set_desc(dev, T3_DESC); + break; + case TLP_CSID_SSI: + device_set_desc(dev, SSI_DESC); + break; + case TLP_CSID_T1E1: + device_set_desc(dev, T1E1_DESC); + break; + default: + return ENXIO; + } + return 0; + } + +static int +fbsd_detach(device_t dev) + { + softc_t *sc = device_get_softc(dev); + + /* Stop the card and detach from the kernel. */ + detach_card(sc); + + /* Release resources. */ + if (sc->irq_cookie != NULL) + { + bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); + sc->irq_cookie = NULL; + } + if (sc->irq_res != NULL) + { + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_res_id, sc->irq_res); + sc->irq_res = NULL; + } + if (sc->csr_res != NULL) + { + bus_release_resource(dev, sc->csr_res_type, sc->csr_res_id, sc->csr_res); + sc->csr_res = NULL; + } + +# if (__FreeBSD_version >= 500000) + mtx_destroy(&sc->top_mtx); + mtx_destroy(&sc->bottom_mtx); +# endif + return 0; /* no error */ + } + +static void +fbsd_shutdown(device_t dev) + { + shutdown_card(device_get_softc(dev)); + } + +static int +fbsd_attach(device_t dev) + { + softc_t *sc = device_get_softc(dev); + int error; + + /* READ/WRITE_PCI_CFG need this. */ + sc->dev = dev; + + /* What kind of card are we driving? */ + switch (READ_PCI_CFG(sc, TLP_CSID)) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + sc->card = &hssi_card; + break; + case TLP_CSID_T3: + sc->card = &t3_card; + break; + case TLP_CSID_SSI: + sc->card = &ssi_card; + break; + case TLP_CSID_T1E1: + sc->card = &t1_card; + break; + default: + return ENXIO; + } + sc->dev_desc = device_get_desc(dev); + + /* Allocate PCI memory or IO resources to access the Tulip chip CSRs. */ +# if IOREF_CSR + sc->csr_res_id = TLP_CBIO; + sc->csr_res_type = SYS_RES_IOPORT; +# else + sc->csr_res_id = TLP_CBMA; + sc->csr_res_type = SYS_RES_MEMORY; +# endif + sc->csr_res = bus_alloc_resource(dev, sc->csr_res_type, &sc->csr_res_id, + 0, ~0, 1, RF_ACTIVE); + if (sc->csr_res == NULL) + { + printf("%s: bus_alloc_resource(csr) failed.\n", NAME_UNIT); + return ENXIO; + } + sc->csr_tag = rman_get_bustag(sc->csr_res); + sc->csr_handle = rman_get_bushandle(sc->csr_res); + + /* Allocate PCI interrupt resources for the card. */ + sc->irq_res_id = 0; + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_res_id, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (sc->irq_res == NULL) + { + printf("%s: bus_alloc_resource(irq) failed.\n", NAME_UNIT); + fbsd_detach(dev); + return ENXIO; + } + if ((error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, + bsd_interrupt, sc, &sc->irq_cookie))) + { + printf("%s: bus_setup_intr() failed; error %d\n", NAME_UNIT, error); + fbsd_detach(dev); + return error; + } + +# if (__FreeBSD_version >= 500000) + /* Initialize the top-half and bottom-half locks. */ + mtx_init(&sc->top_mtx, NAME_UNIT, "top half lock", MTX_DEF); + mtx_init(&sc->bottom_mtx, NAME_UNIT, "bottom half lock", MTX_DEF); +# endif + + /* Start the card and attach a kernel interface and line protocol. */ + if ((error = attach_card(sc, ""))) detach_card(sc); + return error; + } + +static device_method_t methods[] = + { + DEVMETHOD(device_probe, fbsd_probe), + DEVMETHOD(device_attach, fbsd_attach), + DEVMETHOD(device_detach, fbsd_detach), + DEVMETHOD(device_shutdown, fbsd_shutdown), + /* This driver does not suspend and resume. */ + { 0, 0 } + }; + +static driver_t driver = + { + .name = DEVICE_NAME, + .methods = methods, +# if (__FreeBSD_version >= 500000) + .size = sizeof(softc_t), +# else /* FreeBSD-4 */ + .softc = sizeof(softc_t), +# endif + }; + +static devclass_t devclass; + +DRIVER_MODULE(if_lmc, pci, driver, devclass, 0, 0); +MODULE_VERSION(if_lmc, 2); +MODULE_DEPEND(if_lmc, pci, 1, 1, 1); +# if NETGRAPH +MODULE_DEPEND(if_lmc, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); +# endif +# if NSPPP +MODULE_DEPEND(if_lmc, sppp, 1, 1, 1); +# endif + +#endif /* __FreeBSD__ */ + +/* This is the I/O configuration interface for NetBSD. */ + +#if __NetBSD__ + +static int +nbsd_match(struct device *parent, struct cfdata *match, void *aux) + { + struct pci_attach_args *pa = aux; + u_int32_t cfid = pci_conf_read(pa->pa_pc, pa->pa_tag, TLP_CFID); + u_int32_t csid = pci_conf_read(pa->pa_pc, pa->pa_tag, TLP_CSID); + + /* Looking for a DEC 21140A chip on any Lan Media Corp card. */ + if (cfid != TLP_CFID_TULIP) return 0; + switch (csid) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + case TLP_CSID_T3: + case TLP_CSID_SSI: + case TLP_CSID_T1E1: + return 100; + default: + return 0; + } + } + +static int +nbsd_detach(struct device *self, int flags) + { + softc_t *sc = (softc_t *)self; /* device is first in softc */ + + /* Stop the card and detach from the kernel. */ + detach_card(sc); + + /* Release resources. */ + if (sc->sdh_cookie != NULL) + { + shutdownhook_disestablish(sc->sdh_cookie); + sc->sdh_cookie = NULL; + } + if (sc->irq_cookie != NULL) + { + pci_intr_disestablish(sc->pa_pc, sc->irq_cookie); + sc->irq_cookie = NULL; + } + if (sc->csr_handle) + { + bus_space_unmap(sc->csr_tag, sc->csr_handle, TLP_CSR_SIZE); + sc->csr_handle = 0; + } + + return 0; /* no error */ + } + +static void +nbsd_attach(struct device *parent, struct device *self, void *aux) + { + softc_t *sc = (softc_t *)self; /* device is first in softc */ + struct pci_attach_args *pa = aux; + const char *intrstr; + bus_addr_t csr_addr; + int error; + + /* READ/WRITE_PCI_CFG need these. */ + sc->pa_pc = pa->pa_pc; + sc->pa_tag = pa->pa_tag; + /* bus_dma needs this. */ + sc->pa_dmat = pa->pa_dmat; + + /* What kind of card are we driving? */ + switch (READ_PCI_CFG(sc, TLP_CSID)) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + sc->dev_desc = HSSI_DESC; + sc->card = &hssi_card; + break; + case TLP_CSID_T3: + sc->dev_desc = T3_DESC; + sc->card = &t3_card; + break; + case TLP_CSID_SSI: + sc->dev_desc = SSI_DESC; + sc->card = &ssi_card; + break; + case TLP_CSID_T1E1: + sc->dev_desc = T1E1_DESC; + sc->card = &t1_card; + break; + default: + return; + } + printf(": %s\n", sc->dev_desc); + + /* Allocate PCI resources to access the Tulip chip CSRs. */ +# if IOREF_CSR + csr_addr = (bus_addr_t)READ_PCI_CFG(sc, TLP_CBIO) & -2; + sc->csr_tag = pa->pa_iot; /* bus_space tag for IO refs */ +# else + csr_addr = (bus_addr_t)READ_PCI_CFG(sc, TLP_CBMA); + sc->csr_tag = pa->pa_memt; /* bus_space tag for MEM refs */ +# endif + if ((error = bus_space_map(sc->csr_tag, csr_addr, + TLP_CSR_SIZE, 0, &sc->csr_handle))) + { + printf("%s: bus_space_map() failed; error %d\n", NAME_UNIT, error); + return; + } + + /* Allocate PCI interrupt resources. */ + if ((error = pci_intr_map(pa, &sc->intr_handle))) + { + printf("%s: pci_intr_map() failed; error %d\n", NAME_UNIT, error); + nbsd_detach(self, 0); + return; + } + sc->irq_cookie = pci_intr_establish(pa->pa_pc, sc->intr_handle, + IPL_NET, bsd_interrupt, sc); + if (sc->irq_cookie == NULL) + { + printf("%s: pci_intr_establish() failed\n", NAME_UNIT); + nbsd_detach(self, 0); + return; + } + intrstr = pci_intr_string(pa->pa_pc, sc->intr_handle); + + /* Install a shutdown hook. */ + sc->sdh_cookie = shutdownhook_establish(shutdown_card, sc); + if (sc->sdh_cookie == NULL) + { + printf("%s: shutdown_hook_establish() failed\n", NAME_UNIT); + nbsd_detach(self, 0); + return; + } + + /* Initialize the top-half and bottom-half locks. */ + simple_lock_init(&sc->top_lock); + simple_lock_init(&sc->bottom_lock); + + /* Start the card and attach a kernel interface and line protocol. */ + if ((error = attach_card(sc, intrstr))) detach_card(sc); + } + +# if (__NetBSD_Version__ >= 106080000) /* 1.6H */ +CFATTACH_DECL(lmc, sizeof(softc_t), + nbsd_match, nbsd_attach, nbsd_detach, NULL); +# else +struct cfattach lmc_ca = + { +/*.ca_name = DEVICE_NAME, */ + .ca_devsize = sizeof(softc_t), + .ca_match = nbsd_match, + .ca_attach = nbsd_attach, + .ca_detach = nbsd_detach, + .ca_activate = NULL, + }; +# endif + +# if (__NetBSD_Version__ >= 106080000) +CFDRIVER_DECL(lmc, DV_IFNET, NULL); +# else +static struct cfdriver lmc_cd = + { + .cd_name = DEVICE_NAME, + .cd_class = DV_IFNET, + .cd_ndevs = 0, + .cd_devs = NULL, + }; +# endif + +/* cfdata is declared static, unseen outside this module. */ +/* It is used for LKM; config builds its own in ioconf.c. */ +static struct cfdata lmc_cf = + { +# if (__NetBSD_Version__ >= 106080000) + .cf_name = DEVICE_NAME, + .cf_atname = DEVICE_NAME, +# else + .cf_driver = &lmc_cd, + .cf_attach = &lmc_ca, +# endif + .cf_unit = 0, + .cf_fstate = FSTATE_STAR, + }; + +# if (__NetBSD_Version__ >= 106080000) +MOD_MISC(DEVICE_NAME) +# else +static struct lkm_misc _module = + { + .lkm_name = DEVICE_NAME, + .lkm_type = LM_MISC, + .lkm_offset = 0, + .lkm_ver = LKM_VERSION, + }; +# endif + +/* From /sys/dev/pci/pci.c (no public prototype). */ +int pciprint(void *, const char *); + +static int lkm_nbsd_match(struct pci_attach_args *pa) + { return nbsd_match(0, 0, pa); } + +/* LKM loader finds this by appending "_lkmentry" to filename "if_lmc". */ +int if_lmc_lkmentry(struct lkm_table *lkmtp, int cmd, int ver) + { + int i, error = 0; + + if (ver != LKM_VERSION) return EINVAL; + switch (cmd) + { + case LKM_E_LOAD: + { + struct cfdriver* pcicd; + + lkmtp->private.lkm_misc = &_module; + if ((pcicd = config_cfdriver_lookup("pci")) == NULL) + { + printf("%s: config_cfdriver_lookup(pci) failed; error %d\n", + lmc_cd.cd_name, error); + return error; + } +# if (__NetBSD_Version__ >= 106080000) + if ((error = config_cfdriver_attach(&lmc_cd))) + { + printf("%s: config_cfdriver_attach() failed; error %d\n", + lmc_cd.cd_name, error); + return error; + } + if ((error = config_cfattach_attach(lmc_cd.cd_name, &lmc_ca))) + { + printf("%s: config_cfattach_attach() failed; error %d\n", + lmc_cd.cd_name, error); + config_cfdriver_detach(&lmc_cd); + return error; + } +# endif + for (i=0; i<pcicd->cd_ndevs; i++) + { + int dev; + /* A pointer to a device is a pointer to its softc. */ + struct pci_softc *sc = pcicd->cd_devs[i]; + if (sc == NULL) continue; + for (dev=0; dev<sc->sc_maxndevs; dev++) + { + struct pci_attach_args pa; + pcitag_t tag = pci_make_tag(sc->sc_pc, sc->sc_bus, dev, 0); + if (pci_probe_device(sc, tag, lkm_nbsd_match, &pa) != 0) + config_attach(pcicd->cd_devs[i], &lmc_cf, &pa, pciprint); + /* config_attach doesn't return on failure; it calls panic. */ + } + } + break; + } + case LKM_E_UNLOAD: + { + for (i=lmc_cd.cd_ndevs-1; i>=0; i--) + { + struct device *dev = lmc_cd.cd_devs[i]; + if (dev == NULL) continue; + if ((error = config_detach(dev, 0))) + { + printf("%s: config_detach() failed; error %d\n", + dev->dv_xname, error); + return error; + } + } +# if (__NetBSD_Version__ >= 106080000) + if ((error = config_cfattach_detach(lmc_cd.cd_name, &lmc_ca))) + { + printf("%s: config_cfattach_detach() failed; error %d\n", + lmc_cd.cd_name, error); + return error; + } + if ((error = config_cfdriver_detach(&lmc_cd))) + { + printf("%s: config_cfdriver_detach() failed; error %d\n", + lmc_cd.cd_name, error); + return error; + } +# endif + break; + } + case LKM_E_STAT: + break; + } + + return error; + } + +#endif /* __NetBSD__ */ + +/* This is the I/O configuration interface for OpenBSD. */ + +#if __OpenBSD__ + +static int +obsd_match(struct device *parent, void *match, void *aux) + { + struct pci_attach_args *pa = aux; + u_int32_t cfid = pci_conf_read(pa->pa_pc, pa->pa_tag, TLP_CFID); + u_int32_t csid = pci_conf_read(pa->pa_pc, pa->pa_tag, TLP_CSID); + + /* Looking for a DEC 21140A chip on any Lan Media Corp card. */ + if (cfid != TLP_CFID_TULIP) return 0; + switch (csid) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + case TLP_CSID_T3: + case TLP_CSID_SSI: + case TLP_CSID_T1E1: + return 100; /* match better than other 21140 drivers */ + default: + return 0; + } + } + +static int +obsd_detach(struct device *self, int flags) + { + softc_t *sc = (softc_t *)self; /* device is first in softc */ + + /* Stop the card and detach from the kernel. */ + detach_card(sc); + + /* Release resources. */ + if (sc->sdh_cookie != NULL) + { + shutdownhook_disestablish(sc->sdh_cookie); + sc->sdh_cookie = NULL; + } + if (sc->irq_cookie != NULL) + { + pci_intr_disestablish(sc->pa_pc, sc->irq_cookie); + sc->irq_cookie = NULL; + } + if (sc->csr_handle) + { + bus_space_unmap(sc->csr_tag, sc->csr_handle, TLP_CSR_SIZE); + sc->csr_handle = 0; + } + + return 0; /* no error */ + } + +static void +obsd_attach(struct device *parent, struct device *self, void *aux) + { + softc_t *sc = (softc_t *)self; /* device is first in softc */ + struct pci_attach_args *pa = aux; + const char *intrstr; + bus_addr_t csr_addr; + int error; + + /* READ/WRITE_PCI_CFG need these. */ + sc->pa_pc = pa->pa_pc; + sc->pa_tag = pa->pa_tag; + /* bus_dma needs this. */ + sc->pa_dmat = pa->pa_dmat; + + /* What kind of card are we driving? */ + switch (READ_PCI_CFG(sc, TLP_CSID)) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + sc->dev_desc = HSSI_DESC; + sc->card = &hssi_card; + break; + case TLP_CSID_T3: + sc->dev_desc = T3_DESC; + sc->card = &t3_card; + break; + case TLP_CSID_SSI: + sc->dev_desc = SSI_DESC; + sc->card = &ssi_card; + break; + case TLP_CSID_T1E1: + sc->dev_desc = T1E1_DESC; + sc->card = &t1_card; + break; + default: + return; + } + printf(": %s\n", sc->dev_desc); + + /* Allocate PCI resources to access the Tulip chip CSRs. */ +# if IOREF_CSR + csr_addr = (bus_addr_t)READ_PCI_CFG(sc, TLP_CBIO) & -2; + sc->csr_tag = pa->pa_iot; /* bus_space tag for IO refs */ +# else + csr_addr = (bus_addr_t)READ_PCI_CFG(sc, TLP_CBMA); + sc->csr_tag = pa->pa_memt; /* bus_space tag for MEM refs */ +# endif + if ((error = bus_space_map(sc->csr_tag, csr_addr, + TLP_CSR_SIZE, 0, &sc->csr_handle))) + { + printf("%s: bus_space_map() failed; error %d\n", NAME_UNIT, error); + return; + } + + /* Allocate PCI interrupt resources. */ + if ((error = pci_intr_map(pa, &sc->intr_handle))) + { + printf("%s: pci_intr_map() failed; error %d\n", NAME_UNIT, error); + obsd_detach(self, 0); + return; + } + sc->irq_cookie = pci_intr_establish(pa->pa_pc, sc->intr_handle, + IPL_NET, bsd_interrupt, sc, self->dv_xname); + if (sc->irq_cookie == NULL) + { + printf("%s: pci_intr_establish() failed\n", NAME_UNIT); + obsd_detach(self, 0); + return; + } + intrstr = pci_intr_string(pa->pa_pc, sc->intr_handle); + + /* Install a shutdown hook. */ + sc->sdh_cookie = shutdownhook_establish(shutdown_card, sc); + if (sc->sdh_cookie == NULL) + { + printf("%s: shutdown_hook_establish() failed\n", NAME_UNIT); + obsd_detach(self, 0); + return; + } + + /* Initialize the top-half and bottom-half locks. */ + simple_lock_init(&sc->top_lock); + simple_lock_init(&sc->bottom_lock); + + /* Start the card and attach a kernel interface and line protocol. */ + if ((error = attach_card(sc, intrstr))) detach_card(sc); + } + +struct cfattach lmc_ca = + { + .ca_devsize = sizeof(softc_t), + .ca_match = obsd_match, + .ca_attach = obsd_attach, + .ca_detach = obsd_detach, + .ca_activate = NULL, + }; + +struct cfdriver lmc_cd = + { + .cd_name = DEVICE_NAME, + .cd_devs = NULL, + .cd_class = DV_IFNET, + .cd_indirect = 0, + .cd_ndevs = 0, + }; + +/* cfdata is declared static, unseen outside this module. */ +/* It is used for LKM; config builds its own in ioconf.c. */ +static struct cfdata lmc_cfdata = + { + .cf_attach = &lmc_ca, + .cf_driver = &lmc_cd, + .cf_unit = 0, + .cf_fstate = FSTATE_STAR, + }; + +static struct lkm_any _module = + { + .lkm_name = DEVICE_NAME, + .lkm_type = LM_MISC, + .lkm_offset = 0, + .lkm_ver = LKM_VERSION, + }; + +/* From /sys/dev/pci/pci.c (no public prototype). */ +int pciprint(void *, const char *); + +extern struct cfdriver pci_cd; + +/* LKM loader finds this by appending "_lkmentry" to filename "if_lmc". */ +int if_lmc_lkmentry(struct lkm_table *lkmtp, int cmd, int ver) + { + int i, error = 0; + + if (ver != LKM_VERSION) return EINVAL; + switch (cmd) + { + case LKM_E_LOAD: + { /* XXX This works for ONE card on pci0 of a i386 machine! XXX */ + lkmtp->private.lkm_any = &_module; + for (i=0; i<pci_cd.cd_ndevs; i++) + { + struct pci_attach_args pa; + struct device *parent = pci_cd.cd_devs[i]; + if (parent == NULL) continue; /* dead clone? */ + if ((parent->dv_unit)!=0) continue; /* only bus zero */ + /* XXX For machine independence, need: pcibus_attach_args. XXX */ + /* XXX See NetBSD's sys/dev/pci/pci.c/pci_probe_device. XXX */ + /* XXX Why isn't there an LKM network interface module? XXX */ + pa.pa_pc = NULL; /* XXX */ + pa.pa_bus = 0; /* XXX */ + pa.pa_iot = I386_BUS_SPACE_IO; /* XXX */ + pa.pa_memt = I386_BUS_SPACE_MEM; /* XXX */ + pa.pa_dmat = &pci_bus_dma_tag; /* XXX */ + for (pa.pa_device=0; pa.pa_device<32; pa.pa_device++) /* XXX */ + { + int intr; + pa.pa_function = 0; /* DEC-21140A has function 0 only XXX */ + pa.pa_tag = pci_make_tag(pa.pa_pc, pa.pa_bus, pa.pa_device, 0); + pa.pa_id = pci_conf_read(pa.pa_pc, pa.pa_tag, PCI_ID_REG); + if ((pa.pa_id & 0xFFFF) == 0xFFFF) continue; + if ((pa.pa_id & 0xFFFF) == 0) continue; + /* XXX this only works for pci0 -- no swizzelling XXX */ + pa.pa_intrswiz = 0; + pa.pa_intrtag = pa.pa_tag; + intr = pci_conf_read(pa.pa_pc, pa.pa_tag, PCI_INTERRUPT_REG); + pa.pa_intrline = PCI_INTERRUPT_LINE(intr); + pa.pa_intrpin = ((PCI_INTERRUPT_PIN(intr) -1) % 4) +1; + if (obsd_match(parent, &lmc_cfdata, &pa)) + config_attach(parent, &lmc_cfdata, &pa, pciprint); + /* config_attach doesn't return on failure; it calls panic. */ + } + } + break; + } + case LKM_E_UNLOAD: + { + for (i=lmc_cd.cd_ndevs-1; i>=0; i--) + { + struct device *dev = lmc_cd.cd_devs[i]; + if (dev == NULL) continue; + if ((error = config_detach(dev, 0))) + printf("%s: config_detach() failed; error %d\n", dev->dv_xname, error); + } + break; + } + case LKM_E_STAT: + break; + } + + return error; + } + +#endif /* __OpenBSD__ */ + +/* This is the I/O configuration interface for BSD/OS. */ + +#if __bsdi__ + +static int +bsdi_match(pci_devaddr_t *pa) + { + u_int32_t cfid = pci_inl(pa, TLP_CFID); + u_int32_t csid = pci_inl(pa, TLP_CSID); + + /* Looking for a DEC 21140A chip on any Lan Media Corp card. */ + if (cfid != TLP_CFID_TULIP) return 0; + switch (csid) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + case TLP_CSID_T3: + case TLP_CSID_SSI: + case TLP_CSID_T1E1: + return 1; + default: + return 0; + } + } + +static int +bsdi_probe(struct device *parent, struct cfdata *cf, void *aux) + { + struct isa_attach_args *ia = aux; + pci_devaddr_t *pa = NULL; + pci_devres_t res; + + /* This must be a PCI bus. */ + if (ia->ia_bustype != BUS_PCI) return 0; + + /* Scan PCI bus for our boards. */ + if ((pa = pci_scan(bsdi_match)) == 0) return 0; + + /* Scan config space for IO and MEM base registers and IRQ info. */ + pci_getres(pa, &res, 1, ia); + + /* Crucial: pass pci_devaddr to bsdi_attach in ia_aux. */ + ia->ia_aux = (void *)pa; + + return 1; + } + +static void +bsdi_attach(struct device *parent, struct device *self, void *aux) + { + softc_t *sc = (softc_t *)self; /* device is first in softc */ + struct isa_attach_args *ia = aux; + pci_devaddr_t *pa = ia->ia_aux; /* this is crucial! */ + int error; + + /* READ/WRITE_PCI_CFG need this. */ + sc->cfgbase = *pa; + + /* What kind of card are we driving? */ + switch (READ_PCI_CFG(sc, TLP_CSID)) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + sc->dev_desc = HSSI_DESC; + sc->card = &hssi_card; + break; + case TLP_CSID_T3: + sc->dev_desc = T3_DESC; + sc->card = &t3_card; + break; + case TLP_CSID_SSI: + sc->dev_desc = SSI_DESC; + sc->card = &ssi_card; + break; + case TLP_CSID_T1E1: + sc->dev_desc = T1E1_DESC; + sc->card = &t1_card; + break; + default: + return; + } + printf(": %s\n", sc->dev_desc); + + /* Allocate PCI memory or IO resources to access the Tulip chip CSRs. */ + sc->csr_iobase = ia->ia_iobase; + sc->csr_membase = (u_int32_t *)mapphys((vm_offset_t)ia->ia_maddr, TLP_CSR_SIZE); + + /* Attach to the PCI bus. */ + isa_establish(&sc->id, &sc->dev); + + /* Allocate PCI interrupt resources for the card. */ + sc->ih.ih_fun = bsd_interrupt; + sc->ih.ih_arg = sc; + intr_establish(ia->ia_irq, &sc->ih, DV_NET); + + /* Install a shutdown hook. */ + sc->ats.func = shutdown_card; + sc->ats.arg = sc; + atshutdown(&sc->ats, ATSH_ADD); + + /* Initialize the top-half and bottom-half locks. */ + simple_lock_init(&sc->top_lock); + simple_lock_init(&sc->bottom_lock); + + /* Start the card and attach a kernel interface and line protocol. */ + if ((error = attach_card(sc, ""))) detach_card(sc); + } + +struct cfdriver lmccd = + { + .cd_devs = NULL, + .cd_name = DEVICE_NAME, + .cd_match = bsdi_probe, + .cd_attach = bsdi_attach, + .cd_class = DV_IFNET, + .cd_devsize = sizeof(softc_t), + }; +#endif /* __bsdi__ */ + +#if __linux__ + +/* The kernel calls this procedure when an interrupt happens. */ +static irqreturn_t +linux_interrupt(int irq, void *dev, struct pt_regs *regs) + { + struct net_device *net_dev = dev; + softc_t *sc = dev_to_hdlc(net_dev)->priv; + + /* Cut losses early if this is not our interrupt. */ + if ((READ_CSR(TLP_STATUS) & TLP_INT_TXRX) == 0) + return IRQ_NONE; + + /* Disable card interrupts. */ + WRITE_CSR(TLP_INT_ENBL, TLP_INT_DISABLE); + + /* Handle the card interrupt with the dev->poll method. */ + if (netif_rx_schedule_prep(net_dev)) + __netif_rx_schedule(net_dev); /* NAPI - add to poll list */ + else + printk("%s: interrupt while on poll list\n", NAME_UNIT); + + return IRQ_HANDLED; + } + +/* This net_device method services interrupts in a softirq. */ +/* With rxintr_cleanup(), it implements input flow control. */ +static int +linux_poll(struct net_device *net_dev, int *budget) + { + softc_t *sc = dev_to_hdlc(net_dev)->priv; + int received; + + /* Yes, we do NAPI. */ + /* Allow processing up to net_dev->quota incoming packets. */ + /* This is the ONLY time core_interrupt() may process rx pkts. */ + /* Otherwise (sc->quota == 0) and rxintr_cleanup() is a NOOP. */ + sc->quota = net_dev->quota; + + /* Handle the card interrupt with kernel ints enabled. */ + /* Process rx pkts (and tx pkts, too). */ + /* Card interrupts are disabled. */ + core_interrupt(sc, 0); + + /* Report number of rx packets processed. */ + received = net_dev->quota - sc->quota; + net_dev->quota -= received; + *budget -= received; + + /* if quota prevented processing all rx pkts, leave rx ints disabled */ + if (sc->quota == 0) /* this is off by one...but harmless */ + { + WRITE_CSR(TLP_INT_ENBL, TLP_INT_TX); + return 1; /* more pkts to handle -- reschedule */ + } + + sc->quota = 0; /* disable rx pkt processing by rxintr_cleanup() */ + netif_rx_complete(net_dev); /* NAPI - remove from poll list */ + + /* Enable card interrupts. */ + WRITE_CSR(TLP_INT_ENBL, TLP_INT_TXRX); + return 0; + } + +/* These next routines are similar to BSD's ifnet kernel/driver interface. */ + +/* This net_device method hands outgoing packets to the transmitter. */ +/* With txintr_setup(), it implements output flow control. */ +/* Called from a syscall (user context; no spinlocks). */ +static int +linux_start(struct sk_buff *skb, struct net_device *net_dev) + { + softc_t *sc = dev_to_hdlc(net_dev)->priv; + + if (sc->tx_skb == NULL) + { + /* Put this skb where the transmitter will see it. */ + sc->tx_skb = skb; + + /* Start the transmitter; incoming pkts are NOT processed. */ + user_interrupt(sc, 0); + + /* If the tx didn't take the skb then stop the queue. */ + /* This can happen if another CPU is in core_interrupt(). */ + if (sc->tx_skb != NULL) netif_stop_queue(net_dev); + + return 0; + } + + /* This shouldn't happen; skb is NOT consumed. */ + if (netif_queue_stopped(net_dev)) + printk("%s: dev->start() called with queue stopped\n", NAME_UNIT); + else + netif_stop_queue(net_dev); + + return 1; + } + +/* This net_device method restarts the transmitter if it hangs. */ +/* Called from a softirq. */ +static void +linux_timeout(struct net_device *net_dev) + { + softc_t *sc = dev_to_hdlc(net_dev)->priv; + + /* Start the transmitter; incoming packets are NOT processed. */ + user_interrupt(sc, 1); + } + +/* This net_device method handles IOCTL syscalls. */ +/* Called from a syscall (user context; no spinlocks; can sleep). */ +static int +linux_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) + { + softc_t *sc = dev_to_hdlc(net_dev)->priv; + int error = 0; + + if ((cmd >= SIOCDEVPRIVATE) && (cmd <= SIOCDEVPRIVATE+15)) + { + struct iohdr *iohdr = (struct iohdr *)ifr; + u_int16_t direction = iohdr->direction; + u_int16_t length = iohdr->length; + char *user_addr = (char *)iohdr->iohdr; + char *kern_addr; + + if (iohdr->cookie != NGM_LMC_COOKIE) return -EINVAL; + + /* Emulate a BSD-style IOCTL syscall. */ + kern_addr = kmalloc(length, GFP_KERNEL); + if (kern_addr == NULL) + error = -ENOMEM; + if ((error == 0) && ((direction & DIR_IOW) != 0)) + error = copy_from_user(kern_addr, user_addr, length); + if (error == 0) + error = -core_ioctl(sc, (unsigned long)cmd, kern_addr); + if ((error == 0) && ((direction & DIR_IOR) != 0)) + error = copy_to_user(user_addr, kern_addr, length); + kfree(kern_addr); + } +# if GEN_HDLC + else if (cmd == SIOCWANDEV) + { + const size_t size = sizeof(sync_serial_settings); + + switch (ifr->ifr_settings.type) + { + case IF_GET_IFACE: /* get interface config */ + { + ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; + if (ifr->ifr_settings.size < size) + { + ifr->ifr_settings.size = size; + error = -ENOBUFS; + } + else + { + if (sc->config.tx_clk_src == CFG_CLKMUX_ST) + sc->hdlc_settings.clock_type = CLOCK_EXT; + if (sc->config.tx_clk_src == CFG_CLKMUX_INT) + sc->hdlc_settings.clock_type = CLOCK_TXINT; + if (sc->config.tx_clk_src == CFG_CLKMUX_RT) + sc->hdlc_settings.clock_type = CLOCK_TXFROMRX; + sc->hdlc_settings.loopback = (sc->config.loop_back != CFG_LOOP_NONE) ? 1:0; + sc->hdlc_settings.clock_rate = sc->status.tx_speed; + error = copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, + &sc->hdlc_settings, size); + } + break; + } + case IF_IFACE_SYNC_SERIAL: /* set interface config */ + { + if (!capable(CAP_NET_ADMIN)) + error = -EPERM; + if (error == 0) + error = copy_from_user(&sc->hdlc_settings, + ifr->ifr_settings.ifs_ifsu.sync, size); + /* hdlc_settings are currently ignored. */ + break; + } + default: /* Pass the rest to the line protocol code. */ + { + error = hdlc_ioctl(net_dev, ifr, cmd); + break; + } + } + } +# endif /* GEN_HDLC */ + else /* unknown IOCTL command */ + error = -EINVAL; + + if (DRIVER_DEBUG) + printk("%s: linux_ioctl; cmd=0x%08x error=%d\n", + NAME_UNIT, cmd, error); + + return error; + } + +/* This net_device method returns a pointer to device statistics. */ +static struct net_device_stats * +linux_stats(struct net_device *net_dev) + { +# if GEN_HDLC + return &dev_to_hdlc(net_dev)->stats; +# else + softc_t *sc = net_dev->priv; + return &sc->net_stats; +# endif + } + +/* Called from a softirq once a second. */ +static void +linux_watchdog(unsigned long softc) + { + softc_t *sc = (softc_t *)softc; + u_int8_t old_oper_status = sc->status.oper_status; + struct event_cntrs *cntrs = &sc->status.cntrs; + struct net_device_stats *stats = linux_stats(sc->net_dev); + + core_watchdog(sc); /* updates oper_status */ + + /* Notice change in link status. */ + if ((old_oper_status != STATUS_UP) && + (sc->status.oper_status == STATUS_UP)) /* link came up */ + { + hdlc_set_carrier(1, sc->net_dev); + netif_wake_queue(sc->net_dev); + } + if ((old_oper_status == STATUS_UP) && + (sc->status.oper_status != STATUS_UP)) /* link went down */ + { + hdlc_set_carrier(0, sc->net_dev); + netif_stop_queue(sc->net_dev); + } + + /* Notice change in line protocol. */ + if (sc->config.line_pkg == PKG_RAWIP) + { + sc->status.line_pkg = PKG_RAWIP; + sc->status.line_prot = PROT_IP_HDLC; + } +# if GEN_HDLC + else + { + sc->status.line_pkg = PKG_GEN_HDLC; + switch (sc->hdlc_dev->proto.id) + { + case IF_PROTO_PPP: + sc->status.line_prot = PROT_PPP; + break; + case IF_PROTO_CISCO: + sc->status.line_prot = PROT_C_HDLC; + break; + case IF_PROTO_FR: + sc->status.line_prot = PROT_FRM_RLY; + break; + case IF_PROTO_HDLC: + sc->status.line_prot = PROT_IP_HDLC; + break; + case IF_PROTO_X25: + sc->status.line_prot = PROT_X25; + break; + case IF_PROTO_HDLC_ETH: + sc->status.line_prot = PROT_ETH_HDLC; + break; + default: + sc->status.line_prot = 0; + break; + } + } +# endif /* GEN_HDLC */ + + /* Copy statistics from sc to net_dev for get_stats(). */ + stats->rx_packets = cntrs->ipackets; + stats->tx_packets = cntrs->opackets; + stats->rx_bytes = cntrs->ibytes; + stats->tx_bytes = cntrs->obytes; + stats->rx_errors = cntrs->ierrors; + stats->tx_errors = cntrs->oerrors; + stats->rx_dropped = cntrs->idiscards; + stats->tx_dropped = cntrs->odiscards; + stats->rx_fifo_errors = cntrs->fifo_over; + stats->tx_fifo_errors = cntrs->fifo_under; + stats->rx_missed_errors = cntrs->missed; + stats->rx_over_errors = cntrs->overruns; + + /* Call this procedure again after one second. */ + sc->wd_timer.expires = jiffies + HZ; /* now plus one second */ + add_timer(&sc->wd_timer); + } + +/* This is the I/O configuration interface for Linux. */ + +/* This net_device method is called when IFF_UP goes false. */ +static int +linux_stop(struct net_device *net_dev) + { + softc_t *sc = dev_to_hdlc(net_dev)->priv; + + /* Stop the card and detach from the kernel. */ + detach_card(sc); /* doesn't fail */ + + free_irq(net_dev->irq, net_dev); /* doesn't fail */ + + del_timer(&sc->wd_timer); /* return value ignored */ + + return 0; + } + +/* This net_device method is called when IFF_UP goes true. */ +static int +linux_open(struct net_device *net_dev) + { + softc_t *sc = dev_to_hdlc(net_dev)->priv; + int error; + + /* Allocate PCI interrupt resources for the card. */ + if ((error = request_irq(net_dev->irq, &linux_interrupt, SA_SHIRQ, + NAME_UNIT, net_dev))) + { + printk("%s: request_irq() failed; error %d\n", NAME_UNIT, error); + return error; + } + + /* Arrange to call linux_watchdog() once a second. */ + init_timer(&sc->wd_timer); + sc->wd_timer.expires = jiffies + HZ; /* now plus one second */ + sc->wd_timer.function = &linux_watchdog; + sc->wd_timer.data = (unsigned long) sc; + add_timer(&sc->wd_timer); + + /* Start the card and attach a kernel interface and line protocol. */ + if ((error = -attach_card(sc, ""))) + linux_stop(net_dev); + else + { + net_dev->weight = sc->rxring.num_descs; /* input flow control */ + netif_start_queue(net_dev); /* output flow control */ + } + + return error; + } + +# if GEN_HDLC +static int +hdlc_attach(struct net_device *net_dev, + unsigned short encoding, unsigned short parity) + { return 0; } +# endif + +/* This pci_driver method is called during shutdown or module-unload. */ +/* This is called from user context; can sleep; no spinlocks! */ +static void __exit +linux_remove(struct pci_dev *pci_dev) + { + struct net_device *net_dev = (struct net_device *)pci_get_drvdata(pci_dev); + softc_t *sc = dev_to_hdlc(net_dev)->priv; + + if (net_dev == NULL) return; + + /* Assume that linux_stop() has already been called. */ + if (sc->flags & FLAG_NETDEV) +# if GEN_HDLC + unregister_hdlc_device(net_dev); +# else + unregister_netdev(net_dev); +# endif + +# if (IOREF_CSR == 0) + if (sc->csr_membase != NULL) + iounmap(sc->csr_membase); +# endif + + pci_disable_device(pci_dev); + + if (sc->csr_iobase != 0) + pci_release_regions(pci_dev); + + pci_set_drvdata(pci_dev, NULL); + + kfree(sc); + free_netdev(net_dev); + } + +static void +setup_netdev(struct net_device *net_dev) + { + /* Initialize the generic network device. */ + /* Note similarity to BSD's ifnet_attach(). */ + net_dev->flags = IFF_POINTOPOINT; + net_dev->flags |= IFF_RUNNING; + net_dev->open = linux_open; + net_dev->stop = linux_stop; + net_dev->hard_start_xmit = linux_start; + net_dev->do_ioctl = linux_ioctl; + net_dev->get_stats = linux_stats; + net_dev->tx_timeout = linux_timeout; + net_dev->poll = linux_poll; + net_dev->watchdog_timeo = 1 * HZ; + net_dev->tx_queue_len = SNDQ_MAXLEN; + net_dev->mtu = MAX_DESC_LEN; + net_dev->type = ARPHRD_RAWHDLC; +/* The receiver generates frag-lists for packets >4032 bytes. */ +/* The transmitter accepts scatter/gather lists and frag-lists. */ +/* However Linux linearizes outgoing packets since our hardware */ +/* doesn't compute soft checksums. All that work for nothing! */ +/*net_dev->features |= NETIF_F_SG; */ +/*net_dev->features |= NETIF_F_FRAGLIST; */ + } + +/* This pci_driver method is called during boot or module-load. */ +/* This is called from user context; can sleep; no spinlocks! */ +static int __init +linux_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) + { + u_int32_t cfid, csid; + struct net_device *net_dev; + softc_t *sc; + int error; + + /* Looking for a DEC 21140A chip on any Lan Media Corp card. */ + pci_read_config_dword(pci_dev, TLP_CFID, &cfid); + if (cfid != TLP_CFID_TULIP) return -ENXIO; + pci_read_config_dword(pci_dev, TLP_CSID, &csid); + switch (csid) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + case TLP_CSID_T3: + case TLP_CSID_SSI: + case TLP_CSID_T1E1: + break; + default: + return -ENXIO; + } + + /* Declare that these cards use 32-bit single-address PCI cycles. */ + if ((error = pci_set_dma_mask(pci_dev, DMA_32BIT_MASK))) + { + printk("%s: pci_set_dma_mask() failed; error %d\n", DEVICE_NAME, error); + return error; + } + pci_set_consistent_dma_mask(pci_dev, DMA_32BIT_MASK); /* can't fail */ + +# if GEN_HDLC /* generic-hdlc line protocols */ + + /* device driver instance data, aka Soft Context or sc */ + if ((sc = kmalloc(sizeof(softc_t), GFP_KERNEL)) == NULL) + { + printk("%s: kmalloc() failed\n", DEVICE_NAME); + return -ENOMEM; + } + memset(sc, 0, sizeof(softc_t)); + + /* Allocate space for the HDLC network device struct. */ + if ((net_dev = alloc_hdlcdev(sc)) == NULL) + { + printk("%s: alloc_hdlcdev() failed\n", DEVICE_NAME); + kfree(sc); + return -ENOMEM; + } + + /* Initialize the network device struct. */ + setup_netdev(net_dev); + + /* Initialize the HDLC extension to the network device. */ + sc->hdlc_dev = dev_to_hdlc(net_dev); + sc->hdlc_dev->attach = hdlc_attach; /* noop for this driver */ + sc->hdlc_dev->xmit = linux_start; /* the REAL hard_start_xmit() */ + +# else /* GEN_HDLC */ /* no line protocol. */ + + /* Allocate space for the bare network device struct. */ + net_dev = alloc_netdev(sizeof(softc_t), DEVICE_NAME"%d", setup_netdev); + if (net_dev == NULL) + { + printk("%s: alloc_netdev() failed\n", DEVICE_NAME); + return -ENOMEM; + } + /* device driver instance data, aka Soft Context or sc */ + sc = net_dev->priv; + +# endif /* GEN_HDLC */ + + sc->net_dev = net_dev; /* NAME_UNIT macro needs this */ + sc->pci_dev = pci_dev; /* READ/WRITE_PCI_CFG macros need this */ + + /* Cross-link pci_dev and net_dev. */ + pci_set_drvdata(pci_dev, net_dev); /* pci_dev->driver_data = net_dev */ + SET_NETDEV_DEV(net_dev, &pci_dev->dev); /* net_dev->class_dev.dev = &pci_dev->dev */ + SET_MODULE_OWNER(net_dev); /* ??? NOOP in linux-2.6.3. ??? */ + + /* Sets cfcs.io and cfcs.mem; sets pci_dev->irq based on cfit.int */ + if ((error = pci_enable_device(pci_dev))) + { + printk("%s: pci_enable_device() failed; error %d\n", DEVICE_NAME, error); + linux_remove(pci_dev); + return error; + } + net_dev->irq = pci_dev->irq; /* linux_open/stop need this */ + + /* Allocate PCI memory and IO resources to access the Tulip chip CSRs. */ + if ((error = pci_request_regions(pci_dev, DEVICE_NAME))) + { + printk("%s: pci_request_regions() failed; error %d\n", DEVICE_NAME, error); + linux_remove(pci_dev); + return error; + } + net_dev->base_addr = pci_resource_start(pci_dev, 0); + net_dev->mem_start = pci_resource_start(pci_dev, 1); + net_dev->mem_end = pci_resource_end(pci_dev, 1); + sc->csr_iobase = net_dev->base_addr; + +# if (IOREF_CSR == 0) + sc->csr_membase = ioremap_nocache(net_dev->mem_start, TLP_CSR_SIZE); + if (sc->csr_membase == NULL) + { + printk("%s: ioremap_nocache() failed\n", DEVICE_NAME); + linux_remove(pci_dev); + return -EFAULT; + } +# endif + + /* Sets cfcs.master, enabling PCI DMA; checks latency timer value. */ + pci_set_master(pci_dev); /* Later, attach_card() does this too. */ + + /* Initialize the top-half and bottom-half locks. */ + /* Top_lock must be initialized before net_dev is registered. */ + init_MUTEX(&sc->top_lock); + spin_lock_init(&sc->bottom_lock); + +# if GEN_HDLC + if ((error = register_hdlc_device(net_dev))) + { + printk("%s: register_hdlc_device() failed; error %d\n", DEVICE_NAME, error); + linux_remove(pci_dev); + return error; + } +# else + if ((error = register_netdev(net_dev))) + { + printk("%s: register_netdev() failed; error %d\n", DEVICE_NAME, error); + linux_remove(pci_dev); + return error; + } +# endif + /* The NAME_UNIT macro now works. Use DEVICE_NAME before this. */ + sc->flags |= FLAG_NETDEV; + + /* What kind of card are we driving? */ + switch (READ_PCI_CFG(sc, TLP_CSID)) + { + case TLP_CSID_HSSI: + case TLP_CSID_HSSIc: + sc->dev_desc = HSSI_DESC; + sc->card = &hssi_card; + break; + case TLP_CSID_T3: + sc->dev_desc = T3_DESC; + sc->card = &t3_card; + break; + case TLP_CSID_SSI: + sc->dev_desc = SSI_DESC; + sc->card = &ssi_card; + break; + case TLP_CSID_T1E1: + sc->dev_desc = T1E1_DESC; + sc->card = &t1_card; + break; + default: /* shouldn't happen! */ + linux_remove(pci_dev); + return -ENXIO; + } + + /* Announce the hardware on the console. */ + printk("%s: <%s> io 0x%04lx/9 mem 0x%08lx/25 rom 0x%08lx/14 irq %d pci %s\n", + NAME_UNIT, sc->dev_desc, pci_resource_start(pci_dev, 0), + pci_resource_start(pci_dev, 1), pci_resource_start(pci_dev, 6), + pci_dev->irq, pci_name(pci_dev)); + + return 0; + } + +/* This pci driver knows how to drive these devices: */ +static __initdata struct pci_device_id pci_device_id_tbl[] = + { + /* Looking for a DEC 21140A chip on any Lan Media Corp card. */ + { 0x1011, 0x0009, 0x1376, PCI_ANY_ID, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 } + }; +MODULE_DEVICE_TABLE(pci, pci_device_id_tbl); + +static struct pci_driver pci_driver = + { + .name = DEVICE_NAME, + .id_table = pci_device_id_tbl, + .probe = linux_probe, + .remove = __devexit_p(linux_remove), + /* This driver does not suspend and resume. */ + }; + +/* This ultimately calls our pci_driver.probe() method. */ +static int __init linux_modload(void) + { return pci_module_init(&pci_driver); } +module_init(linux_modload); + +/* This ultimately calls our pci_driver.remove() method. */ +static void __exit linux_modunload(void) + { pci_unregister_driver(&pci_driver); } +module_exit(linux_modunload); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Device driver for SBE/LMC Wide-Area Network cards"); +MODULE_AUTHOR("David Boggs <boggs@boggs.palo-alto.ca.us>"); + +#endif /* __linux__ */ diff --git a/sys/dev/lmc/if_lmc.h b/sys/dev/lmc/if_lmc.h new file mode 100644 index 0000000..2d024bd --- /dev/null +++ b/sys/dev/lmc/if_lmc.h @@ -0,0 +1,1687 @@ +/* + * $FreeBSD$ + * + * Copyright (c) 2002-2004 David Boggs. (boggs@boggs.palo-alto.ca.us) + * All rights reserved. + * + * BSD License: + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * GNU General Public License: + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef IF_LMC_H +#define IF_LMC_H + +#define DEVICE_NAME "lmc" + +/* Linux RPM-style version information */ +#define DRIVER_MAJOR_VERSION 2005 /* year */ +#define DRIVER_MINOR_VERSION 9 /* month */ +#define DRIVER_SUB_VERSION 29 /* day */ + +/* netgraph stuff */ +#define NG_LMC_NODE_TYPE DEVICE_NAME +#define NGM_LMC_COOKIE 1128054761 /* date -u +'%s' */ + +/* Tulip PCI configuration registers */ +#define TLP_CFID 0x00 /* 0: CFg ID register */ +#define TLP_CFCS 0x04 /* 1: CFg Command/Status */ +#define TLP_CFRV 0x08 /* 2: CFg ReVision */ +#define TLP_CFLT 0x0C /* 3: CFg Latency Timer */ +#define TLP_CBIO 0x10 /* 4: Cfg Base IO address */ +#define TLP_CBMA 0x14 /* 5: Cfg Base Mem Addr */ +#define TLP_CSID 0x2C /* 11: Cfg Subsys ID reg */ +#define TLP_CFIT 0x3C /* 15: CFg InTerrupt */ +#define TLP_CFDD 0x40 /* 16: CFg Driver Data */ + +#define TLP_CFID_TULIP 0x00091011 /* DEC 21140A Ethernet chip */ + +#define TLP_CFCS_MSTR_ABORT 0x20000000 +#define TLP_CFCS_TARG_ABORT 0x10000000 +#define TLP_CFCS_SYS_ERROR 0x00000100 +#define TLP_CFCS_PAR_ERROR 0x00000040 +#define TLP_CFCS_MWI_ENABLE 0x00000010 +#define TLP_CFCS_BUS_MASTER 0x00000004 +#define TLP_CFCS_MEM_ENABLE 0x00000002 +#define TLP_CFCS_IO_ENABLE 0x00000001 + +#define TLP_CFLT_LATENCY 0x0000FF00 +#define TLP_CFLT_CACHE 0x000000FF + +#define TLP_CSID_HSSI 0x00031376 /* LMC 5200 HSSI card */ +#define TLP_CSID_T3 0x00041376 /* LMC 5245 T3 card */ +#define TLP_CSID_SSI 0x00051376 /* LMC 1000 SSI card */ +#define TLP_CSID_T1E1 0x00061376 /* LMC 1200 T1E1 card */ +#define TLP_CSID_HSSIc 0x00071376 /* LMC 5200 HSSI cPCI */ +#define TLP_CSID_SDSL 0x00081376 /* LMC 1168 SDSL card */ + +#define TLP_CFIT_MAX_LAT 0xFF000000 + +#define TLP_CFDD_SLEEP 0x80000000 +#define TLP_CFDD_SNOOZE 0x40000000 + +/* Tulip Control and Status Registers */ +#define TLP_CSR_STRIDE 8 /* 64 bits */ +#define TLP_BUS_MODE 0 * TLP_CSR_STRIDE +#define TLP_TX_POLL 1 * TLP_CSR_STRIDE +#define TLP_RX_POLL 2 * TLP_CSR_STRIDE +#define TLP_RX_LIST 3 * TLP_CSR_STRIDE +#define TLP_TX_LIST 4 * TLP_CSR_STRIDE +#define TLP_STATUS 5 * TLP_CSR_STRIDE +#define TLP_OP_MODE 6 * TLP_CSR_STRIDE +#define TLP_INT_ENBL 7 * TLP_CSR_STRIDE +#define TLP_MISSED 8 * TLP_CSR_STRIDE +#define TLP_SROM_MII 9 * TLP_CSR_STRIDE +#define TLP_BIOS_ROM 10 * TLP_CSR_STRIDE +#define TLP_TIMER 11 * TLP_CSR_STRIDE +#define TLP_GPIO 12 * TLP_CSR_STRIDE +#define TLP_CSR13 13 * TLP_CSR_STRIDE +#define TLP_CSR14 14 * TLP_CSR_STRIDE +#define TLP_WDOG 15 * TLP_CSR_STRIDE +#define TLP_CSR_SIZE 128 /* IO bus space size */ + +/* CSR 0 - PCI Bus Mode Register */ +#define TLP_BUS_WRITE_INVAL 0x01000000 /* DONT USE! */ +#define TLP_BUS_READ_LINE 0x00800000 +#define TLP_BUS_READ_MULT 0x00200000 +#define TLP_BUS_DESC_BIGEND 0x00100000 +#define TLP_BUS_TAP 0x000E0000 +#define TLP_BUS_CAL 0x0000C000 +#define TLP_BUS_PBL 0x00003F00 +#define TLP_BUS_DATA_BIGEND 0x00000080 +#define TLP_BUS_DSL 0x0000007C +#define TLP_BUS_ARB 0x00000002 +#define TLP_BUS_RESET 0x00000001 +#define TLP_BUS_CAL_SHIFT 14 +#define TLP_BUS_PBL_SHIFT 8 + +/* CSR 5 - Status Register */ +#define TLP_STAT_FATAL_BITS 0x03800000 +#define TLP_STAT_TX_FSM 0x00700000 +#define TLP_STAT_RX_FSM 0x000E0000 +#define TLP_STAT_FATAL_ERROR 0x00002000 +#define TLP_STAT_TX_UNDERRUN 0x00000020 +#define TLP_STAT_FATAL_SHIFT 23 + +/* CSR 6 - Operating Mode Register */ +#define TLP_OP_RECEIVE_ALL 0x40000000 +#define TLP_OP_MUST_BE_ONE 0x02000000 +#define TLP_OP_NO_HEART_BEAT 0x00080000 +#define TLP_OP_PORT_SELECT 0x00040000 +#define TLP_OP_TX_THRESH 0x0000C000 +#define TLP_OP_TX_RUN 0x00002000 +#define TLP_OP_LOOP_MODE 0x00000C00 +#define TLP_OP_EXT_LOOP 0x00000800 +#define TLP_OP_INT_LOOP 0x00000400 +#define TLP_OP_FULL_DUPLEX 0x00000200 +#define TLP_OP_PROMISCUOUS 0x00000040 +#define TLP_OP_PASS_BAD_PKT 0x00000008 +#define TLP_OP_RX_RUN 0x00000002 +#define TLP_OP_TR_SHIFT 14 +#define TLP_OP_INIT (TLP_OP_PORT_SELECT | \ + TLP_OP_FULL_DUPLEX | \ + TLP_OP_MUST_BE_ONE | \ + TLP_OP_NO_HEART_BEAT | \ + TLP_OP_RECEIVE_ALL | \ + TLP_OP_PROMISCUOUS | \ + TLP_OP_PASS_BAD_PKT | \ + TLP_OP_RX_RUN | \ + TLP_OP_TX_RUN) + +/* CSR 7 - Interrupt Enable Register */ +#define TLP_INT_NORMAL_INTR 0x00010000 +#define TLP_INT_ABNRML_INTR 0x00008000 +#define TLP_INT_FATAL_ERROR 0x00002000 +#define TLP_INT_RX_NO_BUFS 0x00000080 +#define TLP_INT_RX_INTR 0x00000040 +#define TLP_INT_TX_UNDERRUN 0x00000020 +#define TLP_INT_TX_INTR 0x00000001 +#define TLP_INT_DISABLE 0 +#define TLP_INT_TX (TLP_INT_NORMAL_INTR | \ + TLP_INT_ABNRML_INTR | \ + TLP_INT_FATAL_ERROR | \ + TLP_INT_TX_UNDERRUN | \ + TLP_INT_TX_INTR) +#define TLP_INT_RX (TLP_INT_NORMAL_INTR | \ + TLP_INT_ABNRML_INTR | \ + TLP_INT_FATAL_ERROR | \ + TLP_INT_RX_NO_BUFS | \ + TLP_INT_RX_INTR) +#define TLP_INT_TXRX (TLP_INT_TX | TLP_INT_RX) + +/* CSR 8 - RX Missed Frames & Overrun Register */ +#define TLP_MISS_OCO 0x10000000 +#define TLP_MISS_OVERRUN 0x0FFE0000 +#define TLP_MISS_MFO 0x00010000 +#define TLP_MISS_MISSED 0x0000FFFF +#define TLP_OVERRUN_SHIFT 17 + +/* CSR 9 - SROM & MII & Boot ROM Register */ +#define TLP_MII_MDIN 0x00080000 +#define TLP_MII_MDOE 0x00040000 +#define TLP_MII_MDOUT 0x00020000 +#define TLP_MII_MDC 0x00010000 + +#define TLP_BIOS_RD 0x00004000 +#define TLP_BIOS_WR 0x00002000 +#define TLP_BIOS_SEL 0x00001000 + +#define TLP_SROM_RD 0x00004000 +#define TLP_SROM_SEL 0x00000800 +#define TLP_SROM_DOUT 0x00000008 +#define TLP_SROM_DIN 0x00000004 +#define TLP_SROM_CLK 0x00000002 +#define TLP_SROM_CS 0x00000001 + +/* CSR 12 - General Purpose IO register */ +#define TLP_GPIO_DIR 0x00000100 + +/* CSR 15 - Watchdog Timer Register */ +#define TLP_WDOG_RX_OFF 0x00000010 +#define TLP_WDOG_TX_OFF 0x00000001 +#define TLP_WDOG_INIT (TLP_WDOG_TX_OFF | \ + TLP_WDOG_RX_OFF) + +/* GPIO bits common to all cards */ +#define GPIO_INIT 0x01 /* from Xilinx */ +#define GPIO_RESET 0x02 /* to Xilinx */ +/* bits 2 and 3 vary with board type -- see below */ +#define GPIO_MODE 0x10 /* to Xilinx */ +#define GPIO_DP 0x20 /* to/from Xilinx */ +#define GPIO_DATA 0x40 /* serial data */ +#define GPIO_CLK 0x80 /* serial clock */ + +/* HSSI GPIO bits */ +#define GPIO_HSSI_ST 0x04 /* send timing sense (deprecated) */ +#define GPIO_HSSI_TXCLK 0x08 /* clock source */ + +/* HSSIc GPIO bits */ +#define GPIO_HSSI_SYNTH 0x04 /* Synth osc chip select */ +#define GPIO_HSSI_DCE 0x08 /* provide clock on TXCLOCK output */ + +/* T3 GPIO bits */ +#define GPIO_T3_DAC 0x04 /* DAC chip select */ +#define GPIO_T3_INTEN 0x08 /* Framer Interupt enable */ + +/* SSI GPIO bits */ +#define GPIO_SSI_SYNTH 0x04 /* Synth osc chip select */ +#define GPIO_SSI_DCE 0x08 /* provide clock on TXCLOCK output */ + +/* T1E1 GPIO bits */ +#define GPIO_T1_INTEN 0x08 /* Framer Interupt enable */ + +/* MII register 16 bits common to all cards */ +/* NB: LEDs for HSSI & SSI are in DIFFERENT bits than for T1E1 & T3; oops */ +/* NB: CRC32 for HSSI & SSI is in DIFFERENT bit than for T1E1 & T3; oops */ +#define MII16_LED_ALL 0x0780 /* RW: LED bit mask */ +#define MII16_FIFO 0x0800 /* RW: 1=reset, 0=not reset */ + +/* MII register 16 bits for HSSI */ +#define MII16_HSSI_TA 0x0001 /* RW: host ready; host->modem */ +#define MII16_HSSI_CA 0x0002 /* RO: modem ready; modem->host */ +#define MII16_HSSI_LA 0x0004 /* RW: loopback A; host->modem */ +#define MII16_HSSI_LB 0x0008 /* RW: loopback B; host->modem */ +#define MII16_HSSI_LC 0x0010 /* RO: loopback C; modem->host */ +#define MII16_HSSI_TM 0x0020 /* RO: test mode; modem->host */ +#define MII16_HSSI_CRC32 0x0040 /* RW: CRC length 16/32 */ +#define MII16_HSSI_LED_LL 0x0080 /* RW: lower left - green */ +#define MII16_HSSI_LED_LR 0x0100 /* RW: lower right - green */ +#define MII16_HSSI_LED_UL 0x0200 /* RW: upper left - green */ +#define MII16_HSSI_LED_UR 0x0400 /* RW: upper right - red */ +#define MII16_HSSI_FIFO 0x0800 /* RW: reset fifos */ +#define MII16_HSSI_FORCECA 0x1000 /* RW: [cPCI] force CA on */ +#define MII16_HSSI_CLKMUX 0x6000 /* RW: [cPCI] TX clock selection */ +#define MII16_HSSI_LOOP 0x8000 /* RW: [cPCI] LOOP TX into RX */ +#define MII16_HSSI_MODEM 0x003F /* TA+CA+LA+LB+LC+TM */ + +/* MII register 16 bits for DS3 */ +#define MII16_DS3_ZERO 0x0001 /* RW: short/long cables */ +#define MII16_DS3_TRLBK 0x0002 /* RW: loop towards host */ +#define MII16_DS3_LNLBK 0x0004 /* RW: loop towards net */ +#define MII16_DS3_RAIS 0x0008 /* RO: LIU receive AIS (depr) */ +#define MII16_DS3_TAIS 0x0010 /* RW: LIU transmit AIS (depr) */ +#define MII16_DS3_BIST 0x0020 /* RO: LIU QRSS patt match (depr) */ +#define MII16_DS3_DLOS 0x0040 /* RO: LIU Digital LOS (depr) */ +#define MII16_DS3_LED_BLU 0x0080 /* RW: lower right - blue */ +#define MII16_DS3_LED_YEL 0x0100 /* RW: lower left - yellow */ +#define MII16_DS3_LED_RED 0x0200 /* RW: upper right - red */ +#define MII16_DS3_LED_GRN 0x0400 /* RW: upper left - green */ +#define MII16_DS3_FIFO 0x0800 /* RW: reset fifos */ +#define MII16_DS3_CRC32 0x1000 /* RW: CRC length 16/32 */ +#define MII16_DS3_SCRAM 0x2000 /* RW: payload scrambler */ +#define MII16_DS3_POLY 0x4000 /* RW: 1=Larse, 0=DigLink|Kentrox */ +#define MII16_DS3_FRAME 0x8000 /* RW: 1=stop txframe pulses */ + +/* MII register 16 bits for SSI */ +#define MII16_SSI_DTR 0x0001 /* RW: DTR host->modem */ +#define MII16_SSI_DSR 0x0002 /* RO: DSR modem->host */ +#define MII16_SSI_RTS 0x0004 /* RW: RTS host->modem */ +#define MII16_SSI_CTS 0x0008 /* RO: CTS modem->host */ +#define MII16_SSI_DCD 0x0010 /* RW: DCD modem<->host */ +#define MII16_SSI_RI 0x0020 /* RO: RI modem->host */ +#define MII16_SSI_CRC32 0x0040 /* RW: CRC length 16/32 */ +#define MII16_SSI_LED_LL 0x0080 /* RW: lower left - green */ +#define MII16_SSI_LED_LR 0x0100 /* RW: lower right - green */ +#define MII16_SSI_LED_UL 0x0200 /* RW: upper left - green */ +#define MII16_SSI_LED_UR 0x0400 /* RW: upper right - red */ +#define MII16_SSI_FIFO 0x0800 /* RW: reset fifos */ +#define MII16_SSI_LL 0x1000 /* RW: LL: host->modem */ +#define MII16_SSI_RL 0x2000 /* RW: RL: host->modem */ +#define MII16_SSI_TM 0x4000 /* RO: TM: modem->host */ +#define MII16_SSI_LOOP 0x8000 /* RW: Loop at ext conn */ +#define MII16_SSI_MODEM 0x703F /* DTR+DSR+RTS+CTS+DCD+RI+LL+RL+TM */ + +/* Mii register 17 has the SSI cable bits */ +#define MII17_SSI_CABLE_SHIFT 3 /* shift to get cable type */ +#define MII17_SSI_CABLE_MASK 0x0038 /* RO: mask to get cable type */ +#define MII17_SSI_PRESCALE 0x0040 /* RW: divide by: 0=16; 1=512 */ +#define MII17_SSI_ITF 0x0100 /* RW: fill with: 0=flags; 1=ones */ +#define MII17_SSI_NRZI 0x0400 /* RW: coding: NRZ= 0; NRZI=1 */ + +/* MII register 16 bits for T1/E1 */ +#define MII16_T1_UNUSED1 0x0001 +#define MII16_T1_INVERT 0x0002 /* RW: invert data (for SF/AMI) */ +#define MII16_T1_XOE 0x0004 /* RW: TX Output Enable; 0=disable */ +#define MII16_T1_RST 0x0008 /* RW: Bt8370 chip reset */ +#define MII16_T1_Z 0x0010 /* RW: output impedance T1=1 E1=0 */ +#define MII16_T1_INTR 0x0020 /* RO: interrupt from Bt8370 */ +#define MII16_T1_ONESEC 0x0040 /* RO: one second square wave */ +#define MII16_T1_LED_BLU 0x0080 /* RW: lower right - blue */ +#define MII16_T1_LED_YEL 0x0100 /* RW: lower left - yellow */ +#define MII16_T1_LED_RED 0x0200 /* RW: upper right - red */ +#define MII16_T1_LED_GRN 0x0400 /* RW: upper left - green */ +#define MII16_T1_FIFO 0x0800 /* RW: reset fifos */ +#define MII16_T1_CRC32 0x1000 /* RW: CRC length 16/32 */ +#define MII16_T1_UNUSED2 0xE000 + +/* T3 framer: RW=Read/Write; RO=Read-Only; RC=Read/Clr; WO=Write-Only */ +#define T3CSR_STAT0 0x00 /* RO: real-time status */ +#define T3CSR_CTL1 0x01 /* RW: global control bits */ +#define T3CSR_FEBE 0x02 /* RC: Far End Block Error Counter */ +#define T3CSR_CERR 0x03 /* RC: C-bit Parity Error Counter */ +#define T3CSR_PERR 0x04 /* RC: P-bit Parity Error Counter */ +#define T3CSR_TX_FEAC 0x05 /* RW: Far End Alarm & Control */ +#define T3CSR_RX_FEAC 0x06 /* RO: Far End Alarm & Control */ +#define T3CSR_STAT7 0x07 /* RL: latched real-time status */ +#define T3CSR_CTL8 0x08 /* RW: extended global ctl bits */ +#define T3CSR_STAT9 0x09 /* RL: extended status bits */ +#define T3CSR_FERR 0x0A /* RC: F-bit Error Counter */ +#define T3CSR_MERR 0x0B /* RC: M-bit Error Counter */ +#define T3CSR_CTL12 0x0C /* RW: more extended ctl bits */ +#define T3CSR_DBL_FEAC 0x0D /* RW: TX double FEAC */ +#define T3CSR_CTL14 0x0E /* RW: even more extended ctl bits */ +#define T3CSR_FEAC_STK 0x0F /* RO: RX FEAC stack */ +#define T3CSR_STAT16 0x10 /* RL: extended latched status */ +#define T3CSR_INTEN 0x11 /* RW: interrupt enable */ +#define T3CSR_CVLO 0x12 /* RC: coding violation cntr LSB */ +#define T3CSR_CVHI 0x13 /* RC: coding violation cntr MSB */ +#define T3CSR_CTL20 0x14 /* RW: yet more extended ctl bits */ + +#define CTL1_XTX 0x01 /* Transmit X-bit value */ +#define CTL1_3LOOP 0x02 /* framer loop back */ +#define CTL1_SER 0x04 /* SERial interface selected */ +#define CTL1_M13MODE 0x08 /* M13 frame format */ +#define CTL1_TXIDL 0x10 /* Transmit Idle signal */ +#define CTL1_ENAIS 0x20 /* Enable AIS upon LOS */ +#define CTL1_TXAIS 0x40 /* Transmit Alarm Indication Sig */ +#define CTL1_NOFEBE 0x80 /* No Far End Block Errors */ + +#define CTL5_EMODE 0x80 /* rev B Extended features enabled */ +#define CTL5_START 0x40 /* transmit the FEAC msg now */ + +#define CTL8_FBEC 0x80 /* F-Bit Error Count control */ +#define CTL8_TBLU 0x20 /* Transmit Blue signal */ + +#define STAT9_SEF 0x80 /* Severely Errored Frame */ +#define STAT9_RBLU 0x20 /* Receive Blue signal */ + +#define CTL12_RTPLLEN 0x80 /* Rx-to-Tx Payload Lpbk Lock ENbl */ +#define CTL12_RTPLOOP 0x40 /* Rx-to-Tx Payload Loopback */ +#define CTL12_DLCB1 0x08 /* Data Link C-Bits forced to 1 */ +#define CTL12_C21 0x04 /* C2 forced to 1 */ +#define CTL12_MCB1 0x02 /* Most C-Bits forced to 1 */ + +#define CTL13_DFEXEC 0x40 /* Execute Double FEAC */ + +#define CTL14_FEAC10 0x80 /* Transmit FEAC word 10 times */ +#define CTL14_RGCEN 0x20 /* Receive Gapped Clock Out Enbl */ +#define CTL14_TGCEN 0x10 /* Timing Gen Gapped Clk Out Enbl */ + +#define FEAC_STK_MORE 0x80 /* FEAC stack has more FEACs */ +#define FEAC_STK_VALID 0x40 /* FEAC stack is valid */ +#define FEAC_STK_FEAC 0x3F /* FEAC stack FEAC data */ + +#define STAT16_XERR 0x01 /* X-bit Error */ +#define STAT16_SEF 0x02 /* Severely Errored Frame */ +#define STAT16_RTLOC 0x04 /* Rx/Tx Loss Of Clock */ +#define STAT16_FEAC 0x08 /* new FEAC msg */ +#define STAT16_RIDL 0x10 /* channel IDLe signal */ +#define STAT16_RAIS 0x20 /* Alarm Indication Signal */ +#define STAT16_ROOF 0x40 /* Out Of Frame sync */ +#define STAT16_RLOS 0x80 /* Loss Of Signal */ + +#define CTL20_CVEN 0x01 /* Coding Violation Counter Enbl */ + +/* T1.107 Bit Oriented C-Bit Parity Far End Alarm Control and Status codes */ +#define T3BOP_OOF 0x00 /* Yellow alarm status */ +#define T3BOP_LINE_UP 0x07 /* line loopback activate */ +#define T3BOP_LINE_DOWN 0x1C /* line loopback deactivate */ +#define T3BOP_LOOP_DS3 0x1B /* loopback full DS3 */ +#define T3BOP_IDLE 0x1A /* IDLE alarm status */ +#define T3BOP_AIS 0x16 /* AIS alarm status */ +#define T3BOP_LOS 0x0E /* LOS alarm status */ + +/* T1E1 regs; RW=Read/Write; RO=Read-Only; RC=Read/Clr; WO=Write-Only */ +#define Bt8370_DID 0x000 /* RO: Device ID */ +#define Bt8370_CR0 0x001 /* RW; Primary Control Register */ +#define Bt8370_JAT_CR 0x002 /* RW: Jitter Attenuator CR */ +#define Bt8370_IRR 0x003 /* RO: Interrupt Request Reg */ +#define Bt8370_ISR7 0x004 /* RC: Alarm 1 Interrupt Status */ +#define Bt8370_ISR6 0x005 /* RC: Alarm 2 Interrupt Status */ +#define Bt8370_ISR5 0x006 /* RC: Error Interrupt Status */ +#define Bt8370_ISR4 0x007 /* RC; Cntr Ovfl Interrupt Status */ +#define Bt8370_ISR3 0x008 /* RC: Timer Interrupt Status */ +#define Bt8370_ISR2 0x009 /* RC: Data Link 1 Int Status */ +#define Bt8370_ISR1 0x00A /* RC: Data Link 2 Int Status */ +#define Bt8370_ISR0 0x00B /* RC: Pattrn Interrupt Status */ +#define Bt8370_IER7 0x00C /* RW: Alarm 1 Interrupt Enable */ +#define Bt8370_IER6 0x00D /* RW: Alarm 2 Interrupt Enable */ +#define Bt8370_IER5 0x00E /* RW: Error Interrupt Enable */ +#define Bt8370_IER4 0x00F /* RW: Cntr Ovfl Interrupt Enable */ + +#define Bt8370_IER3 0x010 /* RW: Timer Interrupt Enable */ +#define Bt8370_IER2 0x011 /* RW: Data Link 1 Int Enable */ +#define Bt8370_IER1 0x012 /* RW: Data Link 2 Int Enable */ +#define Bt8370_IER0 0x013 /* RW: Pattern Interrupt Enable */ +#define Bt8370_LOOP 0x014 /* RW: Loopback Config Reg */ +#define Bt8370_DL3_TS 0x015 /* RW: External Data Link Channel */ +#define Bt8370_DL3_BIT 0x016 /* RW: External Data Link Bit */ +#define Bt8370_FSTAT 0x017 /* RO: Offline Framer Status */ +#define Bt8370_PIO 0x018 /* RW: Programmable Input/Output */ +#define Bt8370_POE 0x019 /* RW: Programmable Output Enable */ +#define Bt8370_CMUX 0x01A /* RW: Clock Input Mux */ +#define Bt8370_TMUX 0x01B /* RW: Test Mux Config */ +#define Bt8370_TEST 0x01C /* RW: Test Config */ + +#define Bt8370_LIU_CR 0x020 /* RW: Line Intf Unit Config Reg */ +#define Bt8370_RSTAT 0x021 /* RO; Receive LIU Status */ +#define Bt8370_RLIU_CR 0x022 /* RW: Receive LIU Config */ +#define Bt8370_LPF 0x023 /* RW: RPLL Low Pass Filter */ +#define Bt8370_VGA_MAX 0x024 /* RW: Variable Gain Amplifier Max */ +#define Bt8370_EQ_DAT 0x025 /* RW: Equalizer Coeff Data Reg */ +#define Bt8370_EQ_PTR 0x026 /* RW: Equzlizer Coeff Table Ptr */ +#define Bt8370_DSLICE 0x027 /* RW: Data Slicer Threshold */ +#define Bt8370_EQ_OUT 0x028 /* RW: Equalizer Output Levels */ +#define Bt8370_VGA 0x029 /* RO: Variable Gain Ampl Status */ +#define Bt8370_PRE_EQ 0x02A /* RW: Pre-Equalizer */ + +#define Bt8370_COEFF0 0x030 /* RO: LMS Adj Eq Coeff Status */ +#define Bt8370_GAIN0 0x038 /* RW: Equalizer Gain Thresh */ +#define Bt8370_GAIN1 0x039 /* RW: Equalizer Gain Thresh */ +#define Bt8370_GAIN2 0x03A /* RW: Equalizer Gain Thresh */ +#define Bt8370_GAIN3 0x03B /* RW: Equalizer Gain Thresh */ +#define Bt8370_GAIN4 0x03C /* RW: Equalizer Gain Thresh */ + +#define Bt8370_RCR0 0x040 /* RW: Rx Configuration */ +#define Bt8370_RPATT 0x041 /* RW: Rx Test Pattern Config */ +#define Bt8370_RLB 0x042 /* RW: Rx Loopback Code Detr Conf */ +#define Bt8370_LBA 0x043 /* RW: Loopback Activate Code Patt */ +#define Bt8370_LBD 0x044 /* RW: Loopback Deact Code Patt */ +#define Bt8370_RALM 0x045 /* RW: Rx Alarm Signal Config */ +#define Bt8370_LATCH 0x046 /* RW: Alarm/Err/Cntr Latch Config */ +#define Bt8370_ALM1 0x047 /* RO: Alarm 1 Status */ +#define Bt8370_ALM2 0x048 /* RO: Alarm 2 Status */ +#define Bt8370_ALM3 0x049 /* RO: Alarm 3 Status */ + +#define Bt8370_FERR_LO 0x050 /* RC: Framing Bit Error Cntr LSB */ +#define Bt8370_FERR_HI 0x051 /* RC: Framing Bit Error Cntr MSB */ +#define Bt8370_CRC_LO 0x052 /* RC: CRC Error Counter LSB */ +#define Bt8370_CRC_HI 0x053 /* RC: CRC Error Counter MSB */ +#define Bt8370_LCV_LO 0x054 /* RC: Line Code Viol Counter LSB */ +#define Bt8370_LCV_HI 0x055 /* RC: Line Code Viol Counter MSB */ +#define Bt8370_FEBE_LO 0x056 /* RC: Far End Block Err Cntr LSB */ +#define Bt8370_FEBE_HI 0x057 /* RC: Far End Block Err Cntr MSB */ +#define Bt8370_BERR_LO 0x058 /* RC: PRBS Bit Error Counter LSB */ +#define Bt8370_BERR_HI 0x059 /* RC: PRBS Bit Error Counter MSB */ +#define Bt8370_AERR 0x05A /* RC: SEF/LOF/COFA counter */ +#define Bt8370_RSA4 0x05B /* RO: Rx Sa4 Byte Buffer */ +#define Bt8370_RSA5 0x05C /* RO: Rx Sa5 Byte Buffer */ +#define Bt8370_RSA6 0x05D /* RO: Rx Sa6 Byte Buffer */ +#define Bt8370_RSA7 0x05E /* RO: Rx Sa7 Byte Buffer */ +#define Bt8370_RSA8 0x05F /* RO: Rx Sa8 Byte Buffer */ + +#define Bt8370_SHAPE0 0x060 /* RW: Tx Pulse Shape Config */ +#define Bt8370_TLIU_CR 0x068 /* RW: Tx LIU Config Reg */ + +#define Bt8370_TCR0 0x070 /* RW: Tx Framer Config */ +#define Bt8370_TCR1 0x071 /* RW: Txter Configuration */ +#define Bt8370_TFRM 0x072 /* RW: Tx Frame Format */ +#define Bt8370_TERROR 0x073 /* RW: Tx Error Insert */ +#define Bt8370_TMAN 0x074 /* RW: Tx Manual Sa/FEBE Config */ +#define Bt8370_TALM 0x075 /* RW: Tx Alarm Signal Config */ +#define Bt8370_TPATT 0x076 /* RW: Tx Test Pattern Config */ +#define Bt8370_TLB 0x077 /* RW: Tx Inband Loopback Config */ +#define Bt8370_LBP 0x078 /* RW: Tx Inband Loopback Patt */ +#define Bt8370_TSA4 0x07B /* RW: Tx Sa4 Byte Buffer */ +#define Bt8370_TSA5 0x07C /* RW: Tx Sa5 Byte Buffer */ +#define Bt8370_TSA6 0x07D /* RW: Tx Sa6 Byte Buffer */ +#define Bt8370_TSA7 0x07E /* RW: Tx Sa7 Byte Buffer */ +#define Bt8370_TSA8 0x07F /* RW: Tx Sa8 Byte Buffer */ + +#define Bt8370_CLAD_CR 0x090 /* RW: Clock Rate Adapter Config */ +#define Bt8370_CSEL 0x091 /* RW: CLAD Frequency Select */ +#define Bt8370_CPHASE 0x092 /* RW: CLAD Phase Det Scale Factor */ +#define Bt8370_CTEST 0x093 /* RW: CLAD Test */ + +#define Bt8370_BOP 0x0A0 /* RW: Bit Oriented Protocol Xcvr */ +#define Bt8370_TBOP 0x0A1 /* RW: Tx BOP Codeword */ +#define Bt8370_RBOP 0x0A2 /* RO; Rx BOP Codeword */ +#define Bt8370_BOP_STAT 0x0A3 /* RO: BOP Status */ +#define Bt8370_DL1_TS 0x0A4 /* RW: DL1 Time Slot Enable */ +#define Bt8370_DL1_BIT 0x0A5 /* RW: DL1 Bit Enable */ +#define Bt8370_DL1_CTL 0x0A6 /* RW: DL1 Control */ +#define Bt8370_RDL1_FFC 0x0A7 /* RW: RDL1 FIFO Fill Control */ +#define Bt8370_RDL1 0x0A8 /* RO: RDL1 FIFO */ +#define Bt8370_RDL1_STAT 0x0A9 /* RO: RDL1 Status */ +#define Bt8370_PRM 0x0AA /* RW: Performance Report Message */ +#define Bt8370_TDL1_FEC 0x0AB /* RW: TDL1 FIFO Empty Control */ +#define Bt8370_TDL1_EOM 0x0AC /* WO: TDL1 End Of Message Control */ +#define Bt8370_TDL1 0x0AD /* RW: TDL1 FIFO */ +#define Bt8370_TDL1_STAT 0x0AE /* RO: TDL1 Status */ +#define Bt8370_DL2_TS 0x0AF /* RW: DL2 Time Slot Enable */ + +#define Bt8370_DL2_BIT 0x0B0 /* RW: DL2 Bit Enable */ +#define Bt8370_DL2_CTL 0x0B1 /* RW: DL2 Control */ +#define Bt8370_RDL2_FFC 0x0B2 /* RW: RDL2 FIFO Fill Control */ +#define Bt8370_RDL2 0x0B3 /* RO: RDL2 FIFO */ +#define Bt8370_RDL2_STAT 0x0B4 /* RO: RDL2 Status */ +#define Bt8370_TDL2_FEC 0x0B6 /* RW: TDL2 FIFO Empty Control */ +#define Bt8370_TDL2_EOM 0x0B7 /* WO; TDL2 End Of Message Control */ +#define Bt8370_TDL2 0x0B8 /* RW: TDL2 FIFO */ +#define Bt8370_TDL2_STAT 0x0B9 /* RO: TDL2 Status */ +#define Bt8370_DL_TEST1 0x0BA /* RW: DLINK Test Config */ +#define Bt8370_DL_TEST2 0x0BB /* RW: DLINK Test Status */ +#define Bt8370_DL_TEST3 0x0BC /* RW: DLINK Test Status */ +#define Bt8370_DL_TEST4 0x0BD /* RW: DLINK Test Control */ +#define Bt8370_DL_TEST5 0x0BE /* RW: DLINK Test Control */ + +#define Bt8370_SBI_CR 0x0D0 /* RW: System Bus Interface Config */ +#define Bt8370_RSB_CR 0x0D1 /* RW: Rx System Bus Config */ +#define Bt8370_RSYNC_BIT 0x0D2 /* RW: Rx System Bus Sync Bit Offs */ +#define Bt8370_RSYNC_TS 0x0D3 /* RW: Rx System Bus Sync TS Offs */ +#define Bt8370_TSB_CR 0x0D4 /* RW: Tx System Bus Config */ +#define Bt8370_TSYNC_BIT 0x0D5 /* RW: Tx System Bus Sync Bit OFfs */ +#define Bt8370_TSYNC_TS 0x0D6 /* RW: Tx System Bus Sync TS Offs */ +#define Bt8370_RSIG_CR 0x0D7 /* RW: Rx Siganalling Config */ +#define Bt8370_RSYNC_FRM 0x0D8 /* RW: Sig Reinsertion Frame Offs */ +#define Bt8370_SSTAT 0x0D9 /* RO: Slip Buffer Status */ +#define Bt8370_STACK 0x0DA /* RO: Rx Signalling Stack */ +#define Bt8370_RPHASE 0x0DB /* RO: RSLIP Phase Status */ +#define Bt8370_TPHASE 0x0DC /* RO: TSLIP Phase Status */ +#define Bt8370_PERR 0x0DD /* RO: RAM Parity Status */ + +#define Bt8370_SBCn 0x0E0 /* RW: System Bus Per-Channel Ctl */ +#define Bt8370_TPCn 0x100 /* RW: Tx Per-Channel Control */ +#define Bt8370_TSIGn 0x120 /* RW: Tx Signalling Buffer */ +#define Bt8370_TSLIP_LOn 0x140 /* RW: Tx PCM Slip Buffer Lo */ +#define Bt8370_TSLIP_HIn 0x160 /* RW: Tx PCM Slip Buffer Hi */ +#define Bt8370_RPCn 0x180 /* RW: Rx Per-Channel Control */ +#define Bt8370_RSIGn 0x1A0 /* RW: Rx Signalling Buffer */ +#define Bt8370_RSLIP_LOn 0x1C0 /* RW: Rx PCM Slip Buffer Lo */ +#define Bt8370_RSLIP_HIn 0x1E0 /* RW: Rx PCM Slip Buffer Hi */ + +/* Bt8370_LOOP (0x14) framer loopback control register bits */ +#define LOOP_ANALOG 0x01 /* inward loop thru LIU */ +#define LOOP_FRAMER 0x02 /* inward loop thru framer */ +#define LOOP_LINE 0x04 /* outward loop thru LIU */ +#define LOOP_PAYLOAD 0x08 /* outward loop of payload */ +#define LOOP_DUAL 0x06 /* inward framer + outward line */ + +/* Bt8370_ALM1 (0x47) receiver alarm status register bits */ +#define ALM1_SIGFRZ 0x01 /* Rx Signalling Freeze */ +#define ALM1_RLOF 0x02 /* Rx loss of frame alignment */ +#define ALM1_RLOS 0x04 /* Rx digital loss of signal */ +#define ALM1_RALOS 0x08 /* Rx analog loss of signal */ +#define ALM1_RAIS 0x10 /* Rx Alarm Indication Signal */ +#define ALM1_RYEL 0x40 /* Rx Yellow alarm indication */ +#define ALM1_RMYEL 0x80 /* Rx multiframe YELLOW alarm */ + +/* Bt8370_ALM3 (0x49) receive framer status register bits */ +#define ALM3_FRED 0x04 /* Rx Out Of T1/FAS alignment */ +#define ALM3_MRED 0x08 /* Rx Out Of MFAS alignment */ +#define ALM3_SRED 0x10 /* Rx Out Of CAS alignment */ +#define ALM3_SEF 0x20 /* Rx Severely Errored Frame */ +#define ALM3_RMAIS 0x40 /* Rx TS16 AIS (CAS) */ + +/* Bt8370_TALM (0x75) transmit alarm control register bits */ +#define TALM_TAIS 0x01 /* Tx Alarm Indication Signal */ +#define TALM_TYEL 0x02 /* Tx Yellow alarm */ +#define TALM_TMYEL 0x04 /* Tx Multiframe Yellow alarm */ +#define TALM_AUTO_AIS 0x08 /* auto send AIS on LOS */ +#define TALM_AUTO_YEL 0x10 /* auto send YEL on LOF */ +#define TALM_AUTO_MYEL 0x20 /* auto send E1-Y16 on loss-of-CAS */ + +/* 8370 BOP (Bit Oriented Protocol) command fragments */ +#define RBOP_OFF 0x00 /* BOP Rx disabled */ +#define RBOP_25 0xE0 /* BOP Rx requires 25 BOPs */ +#define TBOP_OFF 0x00 /* BOP Tx disabled */ +#define TBOP_25 0x0B /* BOP Tx sends 25 BOPs */ +#define TBOP_CONT 0x0F /* BOP Tx sends continuously */ + +/* T1.403 Bit-Oriented ESF Data-Link Message codes */ +#define T1BOP_OOF 0x00 /* Yellow alarm status */ +#define T1BOP_LINE_UP 0x07 /* line loopback activate */ +#define T1BOP_LINE_DOWN 0x1C /* line loopback deactivate */ +#define T1BOP_PAY_UP 0x0A /* payload loopback activate */ +#define T1BOP_PAY_DOWN 0x19 /* payload loopback deactivate */ +#define T1BOP_NET_UP 0x09 /* network loopback activate */ +#define T1BOP_NET_DOWN 0x12 /* network loopback deactivate */ + +/* Unix & Linux reserve 16 device-private IOCTLs */ +#if BSD +# define LMCIOCGSTAT _IOWR('i', 240, struct status) +# define LMCIOCGCFG _IOWR('i', 241, struct config) +# define LMCIOCSCFG _IOW('i', 242, struct config) +# define LMCIOCREAD _IOWR('i', 243, struct ioctl) +# define LMCIOCWRITE _IOW('i', 244, struct ioctl) +# define LMCIOCTL _IOWR('i', 245, struct ioctl) +#elif __linux__ /* sigh */ +# define LMCIOCGSTAT SIOCDEVPRIVATE+0 +# define LMCIOCGCFG SIOCDEVPRIVATE+1 +# define LMCIOCSCFG SIOCDEVPRIVATE+2 +# define LMCIOCREAD SIOCDEVPRIVATE+3 +# define LMCIOCWRITE SIOCDEVPRIVATE+4 +# define LMCIOCTL SIOCDEVPRIVATE+5 +#endif + +struct iohdr /* all LMCIOCs begin with this */ + { + char ifname[IFNAMSIZ]; /* interface name, e.g. "lmc0" */ + u_int32_t cookie; /* interface version number */ + u_int16_t direction; /* missing in Linux IOCTL */ + u_int16_t length; /* missing in Linux IOCTL */ + struct iohdr *iohdr; /* missing in Linux IOCTL */ + u_int32_t spare; /* pad this struct to **32 bytes** */ + }; + +#define DIR_IO 0 +#define DIR_IOW 1 /* copy data user->kernel */ +#define DIR_IOR 2 /* copy data kernel->user */ +#define DIR_IOWR 3 /* copy data kernel<->user */ + +struct hssi_snmp + { + u_int16_t sigs; /* MII16_HSSI & MII16_HSSI_MODEM */ + }; + +struct ssi_snmp + { + u_int16_t sigs; /* MII16_SSI & MII16_SSI_MODEM */ + }; + +struct t3_snmp + { + u_int16_t febe; /* 8 bits - Far End Block err cnt */ + u_int16_t lcv; /* 16 bits - BPV err cnt */ + u_int16_t pcv; /* 8 bits - P-bit err cnt */ + u_int16_t ccv; /* 8 bits - C-bit err cnt */ + u_int16_t line; /* line status bit vector */ + u_int16_t loop; /* loop status bit vector */ + }; + +struct t1_snmp + { + u_int16_t prm[4]; /* T1.403 Performance Report Msg */ + u_int16_t febe; /* 10 bits - E1 FAR CRC err cnt */ + u_int16_t lcv; /* 16 bits - BPV + EXZ err cnt */ + u_int16_t fe; /* 12 bits - Ft/Fs/FPS/FAS err cnt */ + u_int16_t crc; /* 10 bits - CRC6/CRC4 err cnt */ + u_int16_t line; /* line status bit vector */ + u_int16_t loop; /* loop status bit vector */ + }; + +/* SNMP trunk MIB Send codes */ +#define TSEND_NORMAL 1 /* Send data (normal or looped) */ +#define TSEND_LINE 2 /* Send 'line loopback activate' */ +#define TSEND_PAYLOAD 3 /* Send 'payload loop activate' */ +#define TSEND_RESET 4 /* Send 'loopback deactivate' */ +#define TSEND_QRS 5 /* Send Quasi Random Signal */ + +/* ANSI T1.403 Performance Report Msg -- once a second from the far end */ +#define T1PRM_FE 0x8000 /* Frame Sync Bit Error Event >= 1 */ +#define T1PRM_SE 0x4000 /* Severely Err Framing Event >= 1 */ +#define T1PRM_LB 0x2000 /* Payload Loopback Activated */ +#define T1PRM_G1 0x1000 /* CRC Error Event = 1 */ +#define T1PRM_R 0x0800 /* Reserved */ +#define T1PRM_G2 0x0400 /* 1 < CRC Error Event <= 5 */ +#define T1PRM_SEQ 0x0300 /* modulo 4 counter */ +#define T1PRM_G3 0x0080 /* 5 < CRC Error Event <= 10 */ +#define T1PRM_LV 0x0040 /* Line Code Violation Event >= 1 */ +#define T1PRM_G4 0x0020 /* 10 < CRC Error Event <= 100 */ +#define T1PRM_U 0x0018 /* Under study for synchronization */ +#define T1PRM_G5 0x0004 /* 100 < CRC Error Event <= 319 */ +#define T1PRM_SL 0x0002 /* Slip Event >= 1 */ +#define T1PRM_G6 0x0001 /* CRC Error Event >= 320 */ + +/* SNMP Line Status */ +#define TLINE_NORM 0x0001 /* no alarm present */ +#define TLINE_RX_RAI 0x0002 /* receiving RAI = Yellow alarm */ +#define TLINE_TX_RAI 0x0004 /* sending RAI = Yellow alarm */ +#define TLINE_RX_AIS 0x0008 /* receiving AIS = blue alarm */ +#define TLINE_TX_AIS 0x0010 /* sending AIS = blue alarm */ +#define TLINE_LOF 0x0020 /* near end LOF = red alarm */ +#define TLINE_LOS 0x0040 /* near end loss of Signal */ +#define TLINE_LOOP 0x0080 /* near end is looped */ +#define T1LINE_RX_TS16_AIS 0x0100 /* near end receiving TS16 AIS */ +#define T1LINE_RX_TS16_LOMF 0x0200 /* near end sending TS16 LOMF */ +#define T1LINE_TX_TS16_LOMF 0x0400 /* near end receiving TS16 LOMF */ +#define T1LINE_RX_TEST 0x0800 /* near end receiving QRS Signal */ +#define T1LINE_SEF 0x1000 /* near end severely errored frame */ +#define T3LINE_RX_IDLE 0x0100 /* near end receiving IDLE signal */ +#define T3LINE_SEF 0x0200 /* near end severely errored frame */ + +/* SNMP Loopback Status */ +#define TLOOP_NONE 0x01 /* no loopback */ +#define TLOOP_NEAR_PAYLOAD 0x02 /* near end payload loopback */ +#define TLOOP_NEAR_LINE 0x04 /* near end line loopback */ +#define TLOOP_NEAR_OTHER 0x08 /* near end looped somehow */ +#define TLOOP_NEAR_INWARD 0x10 /* near end looped inward */ +#define TLOOP_FAR_PAYLOAD 0x20 /* far end payload loopback */ +#define TLOOP_FAR_LINE 0x40 /* far end line loopback */ + +/* event counters record interesting statistics */ +struct event_cntrs + { + struct timeval reset_time; /* time when cntrs were reset */ + u_int64_t ibytes; /* Rx bytes with good status */ + u_int64_t obytes; /* Tx bytes */ + u_int64_t ipackets; /* Rx packets with good status */ + u_int64_t opackets; /* Tx packets */ + u_int32_t ierrors; /* Rx packets with bad status */ + u_int32_t oerrors; /* Tx packets with bad status */ + u_int32_t idiscards; /* Rx packets discarded */ + u_int32_t odiscards; /* Tx packets discarded */ + u_int32_t fifo_over; /* Rx fifo overruns */ + u_int32_t fifo_under; /* Tx fifo underruns */ + u_int32_t missed; /* Rx pkts missed: no DMA descs */ + u_int32_t overruns; /* Rx pkts missed: fifo overrun */ + u_int32_t fdl_pkts; /* Rx T1 Facility Data Link pkts */ + u_int32_t crc_errs; /* Rx T1 frame CRC errors */ + u_int32_t lcv_errs; /* Rx T1 T3 Line Coding Violation */ + u_int32_t frm_errs; /* Rx T1 T3 Frame bit errors */ + u_int32_t febe_errs; /* Rx T1 T3 Far End Bit Errors */ + u_int32_t par_errs; /* Rx T3 P-bit parity errors */ + u_int32_t cpar_errs; /* Rx T3 C-bit parity errors */ + u_int32_t mfrm_errs; /* Rx T3 Multi-frame bit errors */ + u_int32_t rxdma; /* Rx out of kernel buffers */ + u_int32_t txdma; /* Tx out of DMA desciptors */ + u_int32_t lck_watch; /* try_lock conflict in watchdog */ + u_int32_t lck_ioctl; /* try_lock conflict in ioctl */ + u_int32_t lck_intr; /* try_lock conflict in interrupt */ + }; + +/* sc->status is the READ ONLY status of the card. */ +/* Accessed using socket IO control calls or netgraph control messages. */ +struct status + { + struct iohdr iohdr; /* common ioctl header */ + u_int32_t card_type; /* PCI device number */ + u_int16_t ieee[3]; /* IEEE MAC-addr from Tulip SROM */ + u_int16_t oper_status; /* actual state: up, down, test */ + u_int32_t tx_speed; /* measured TX bits/sec */ + u_int32_t cable_type; /* SSI only: cable type */ + u_int32_t line_pkg; /* actual line pkg in use */ + u_int32_t line_prot; /* actual line proto in use */ + u_int32_t ticks; /* incremented by watchdog @ 1 Hz */ + struct event_cntrs cntrs; /* event counters */ + union + { + struct hssi_snmp hssi; /* data for RFC-???? HSSI MIB */ + struct t3_snmp t3; /* data for RFC-2496 T3 MIB */ + struct ssi_snmp ssi; /* data for RFC-1659 RS232 MIB */ + struct t1_snmp t1; /* data for RFC-2495 T1 MIB */ + } snmp; + }; + +/* line protocol package codes fnobl */ +#define PKG_RAWIP 1 /* driver yyyyy */ +#define PKG_SPPP 2 /* fbsd, nbsd, obsd yyynn */ +#define PKG_P2P 3 /* bsd/os nnnyn */ +#define PKG_NG 4 /* fbsd ynnnn */ +#define PKG_GEN_HDLC 5 /* linux nnnny */ + +/* line protocol codes fnobl */ +#define PROT_PPP 1 /* Point-to-Point Protocol yyyyy */ +#define PROT_C_HDLC 2 /* Cisco HDLC Protocol yyyyy */ +#define PROT_FRM_RLY 3 /* Frame Relay Protocol ynnyy */ +#define PROT_X25 4 /* X.25/LAPB Protocol nnnny */ +#define PROT_ETH_HDLC 5 /* raw Ether pkts in HDLC nnnny */ +#define PROT_IP_HDLC 6 /* raw IP4/6 pkts in HDLC yyyyy */ + +/* oper_status codes (same as SNMP status codes) */ +#define STATUS_UP 1 /* may/will tx/rx pkts */ +#define STATUS_DOWN 2 /* can't/won't tx/rx pkts */ +#define STATUS_TEST 3 /* currently not used */ + +struct synth /* programmable oscillator params */ + { + unsigned n :7; /* numerator (3..127) */ + unsigned m :7; /* denominator (3..127) */ + unsigned v :1; /* mul by 1|8 */ + unsigned x :2; /* div by 1|2|4|8 */ + unsigned r :2; /* div by 1|2|4|8 */ + unsigned prescale :13; /* log(final divisor): 2, 4 or 9 */ + } __attribute__ ((packed)); + +#define SYNTH_FREF 20e6 /* reference xtal = 20 MHz */ +#define SYNTH_FMIN 50e6 /* internal VCO min 50 MHz */ +#define SYNTH_FMAX 250e6 /* internal VCO max 250 MHz */ + +/* sc->config is the READ/WRITE configuration of the card. */ +/* Accessed using socket IO control calls or netgraph control messages. */ +struct config + { + struct iohdr iohdr; /* common ioctl header */ + u_int32_t crc_len; /* ALL: CRC-16 or CRC-32 or none */ + u_int32_t loop_back; /* ALL: many kinds of loopbacks */ + u_int32_t tx_clk_src; /* T1, HSSI: ST, RT, int, ext */ + u_int32_t format; /* T3, T1: ckt framing format */ + u_int32_t time_slots; /* T1: 64Kb time slot config */ + u_int32_t cable_len; /* T3, T1: cable length in meters */ + u_int32_t scrambler; /* T3: payload scrambler config */ + u_int32_t dte_dce; /* SSI, HSSIc: drive TXCLK */ + struct synth synth; /* SSI, HSSIc: synth oscil params */ + u_int32_t rx_gain; /* T1: receiver gain limit 0-50 dB */ + u_int32_t tx_pulse; /* T1: transmitter pulse shape */ + u_int32_t tx_lbo; /* T1: transmitter atten 0-22.5 dB */ + u_int32_t debug; /* ALL: extra printout */ + u_int32_t line_pkg; /* ALL: use this line pkg */ + u_int32_t line_prot; /* SPPP: use this line proto */ + u_int32_t keep_alive; /* SPPP: use keep-alive packets */ + }; + +#define CFG_CRC_0 0 /* no CRC */ +#define CFG_CRC_16 2 /* X^16+X^12+X^5+1 (default) */ +#define CFG_CRC_32 4 /* X^32+X^26+X^23+X^22+X^16+X^12+ */ + /* X^11+X^10+X^8+X^7+X^5+X^4+X^2+X+1 */ +#define CFG_LOOP_NONE 1 /* SNMP don't loop back anything */ +#define CFG_LOOP_PAYLOAD 2 /* SNMP loop outward thru framer */ +#define CFG_LOOP_LINE 3 /* SNMP loop outward thru LIU */ +#define CFG_LOOP_OTHER 4 /* SNMP loop inward thru LIU */ +#define CFG_LOOP_INWARD 5 /* SNMP loop inward thru framer */ +#define CFG_LOOP_DUAL 6 /* SNMP loop inward & outward */ +#define CFG_LOOP_TULIP 16 /* ALL: loop inward thru Tulip */ +#define CFG_LOOP_PINS 17 /* HSSIc, SSI: loop inward-pins */ +#define CFG_LOOP_LL 18 /* HSSI, SSI: assert LA/LL mdm pin */ +#define CFG_LOOP_RL 19 /* HSSI, SSI: assert LB/RL mdm pin */ + +#define CFG_CLKMUX_ST 1 /* TX clk <- Send timing */ +#define CFG_CLKMUX_INT 2 /* TX clk <- internal source */ +#define CFG_CLKMUX_RT 3 /* TX clk <- Receive (loop) timing */ +#define CFG_CLKMUX_EXT 4 /* TX clk <- ext connector */ + +/* values 0-31 are Bt8370 CR0 register values (LSB is zero if E1). */ +/* values 32-99 are reserved for other T1E1 formats, (even number if E1) */ +/* values 100 and up are used for T3 frame formats. */ +#define CFG_FORMAT_T1SF 9 /* T1-SF AMI */ +#define CFG_FORMAT_T1ESF 27 /* T1-ESF+CRC B8ZS X^6+X+1 */ +#define CFG_FORMAT_E1FAS 0 /* E1-FAS HDB3 TS0 */ +#define CFG_FORMAT_E1FASCRC 8 /* E1-FAS+CRC HDB3 TS0 X^4+X+1 */ +#define CFG_FORMAT_E1FASCAS 16 /* E1-FAS +CAS HDB3 TS0 & TS16 */ +#define CFG_FORMAT_E1FASCRCCAS 24 /* E1-FAS+CRC+CAS HDB3 TS0 & TS16 */ +#define CFG_FORMAT_E1NONE 32 /* E1-NO framing HDB3 */ +#define CFG_FORMAT_T3CPAR 100 /* T3-C-Bit par B3ZS */ +#define CFG_FORMAT_T3M13 101 /* T3-M13 format B3ZS */ + +/* format aliases that improve code readability */ +#define FORMAT_T1ANY ((sc->config.format & 1)==1) +#define FORMAT_E1ANY ((sc->config.format & 1)==0) +#define FORMAT_E1CAS ((sc->config.format & 0x11)==0x10) +#define FORMAT_E1CRC ((sc->config.format & 0x09)==0x08) +#define FORMAT_E1NONE (sc->config.format == CFG_FORMAT_E1NONE) +#define FORMAT_T1ESF (sc->config.format == CFG_FORMAT_T1ESF) +#define FORMAT_T1SF (sc->config.format == CFG_FORMAT_T1SF) +#define FORMAT_T3CPAR (sc->config.format == CFG_FORMAT_T3CPAR) + +#define CFG_SCRAM_OFF 1 /* DS3 payload scrambler off */ +#define CFG_SCRAM_DL_KEN 2 /* DS3 DigitalLink/Kentrox X^43+1 */ +#define CFG_SCRAM_LARS 3 /* DS3 Larscom X^20+X^17+1 w/28ZS */ + +#define CFG_DTE 1 /* HSSIc, SSI: rcv TXCLK; rcv DCD */ +#define CFG_DCE 2 /* HSSIc, SSI: drv TXCLK; drv DCD */ + +#define CFG_GAIN_SHORT 0x24 /* 0-20 dB of equalized gain */ +#define CFG_GAIN_MEDIUM 0x2C /* 0-30 dB of equalized gain */ +#define CFG_GAIN_LONG 0x34 /* 0-40 dB of equalized gain */ +#define CFG_GAIN_EXTEND 0x3F /* 0-64 dB of equalized gain */ +#define CFG_GAIN_AUTO 0xFF /* auto-set based on cable length */ + +#define CFG_PULSE_T1DSX0 0 /* T1 DSX 0- 40 meters */ +#define CFG_PULSE_T1DSX1 2 /* T1 DSX 40- 80 meters */ +#define CFG_PULSE_T1DSX2 4 /* T1 DSX 80-120 meters */ +#define CFG_PULSE_T1DSX3 6 /* T1 DSX 120-160 meters */ +#define CFG_PULSE_T1DSX4 8 /* T1 DSX 160-200 meters */ +#define CFG_PULSE_E1COAX 10 /* E1 75 ohm coax pair */ +#define CFG_PULSE_E1TWIST 12 /* E1 120 ohm twisted pairs */ +#define CFG_PULSE_T1CSU 14 /* T1 CSU 200-2000 meters; set LBO */ +#define CFG_PULSE_AUTO 0xFF /* auto-set based on cable length */ + +#define CFG_LBO_0DB 0 /* T1CSU LBO = 0.0 dB; FCC opt A */ +#define CFG_LBO_7DB 16 /* T1CSU LBO = 7.5 dB; FCC opt B */ +#define CFG_LBO_15DB 32 /* T1CSU LBO = 15.0 dB; FCC opt C */ +#define CFG_LBO_22DB 48 /* T1CSU LBO = 22.5 dB; final span */ +#define CFG_LBO_AUTO 0xFF /* auto-set based on cable length */ + +struct ioctl + { + struct iohdr iohdr; /* common ioctl header */ + u_int32_t cmd; /* command */ + u_int32_t address; /* command address */ + u_int32_t data; /* command data */ + char *ucode; /* user-land address of ucode */ + }; + +#define IOCTL_RW_PCI 1 /* RW: Tulip PCI config registers */ +#define IOCTL_RW_CSR 2 /* RW: Tulip Control & Status Regs */ +#define IOCTL_RW_SROM 3 /* RW: Tulip Serial Rom */ +#define IOCTL_RW_BIOS 4 /* RW: Tulip Boot rom */ +#define IOCTL_RW_MII 5 /* RW: MII registers */ +#define IOCTL_RW_FRAME 6 /* RW: Framer registers */ +#define IOCTL_WO_SYNTH 7 /* WO: Synthesized oscillator */ +#define IOCTL_WO_DAC 8 /* WO: Digital/Analog Converter */ + +#define IOCTL_XILINX_RESET 16 /* reset Xilinx: all FFs set to 0 */ +#define IOCTL_XILINX_ROM 17 /* load Xilinx program from ROM */ +#define IOCTL_XILINX_FILE 18 /* load Xilinx program from file */ + +#define IOCTL_SET_STATUS 50 /* set mdm ctrl bits (internal use)*/ +#define IOCTL_SNMP_SEND 51 /* trunk MIB send code */ +#define IOCTL_SNMP_LOOP 52 /* trunk MIB loop configuration */ +#define IOCTL_SNMP_SIGS 53 /* RS232-like modem control sigs */ +#define IOCTL_RESET_CNTRS 54 /* reset event counters */ + +/* storage for these strings is allocated here! */ +char *ssi_cables[] = + { + "V.10/EIA423", + "V.11/EIA530A", + "RESERVED", + "X.21", + "V.35", + "V.36/EIA449", + "V.28/EIA232", + "NO CABLE", + NULL, + }; + +/***************************************************************************/ +/* Declarations above here are shared with the user lmcconfig program. */ +/* Declarations below here are private to the kernel device driver. */ +/***************************************************************************/ + +#if (_KERNEL || KERNEL || __KERNEL__) + +#define SNDQ_MAXLEN 32 /* packets awaiting transmission */ +#define DESCS_PER_PKT 4 /* DMA descriptors per TX pkt */ +#define NUM_TX_DESCS (DESCS_PER_PKT * SNDQ_MAXLEN) +/* Increase DESCS_PER_PKT if status.cntrs.txdma increments. */ + +/* A Tulip DMA descriptor can point to two chunks of memory. + * Each chunk has a max length of 2047 bytes (ask the VMS guys...). + * 2047 isn't a multiple of a cache line size (32 bytes typically). + * So back off to 2048-32 = 2016 bytes per chunk (2 chunks per descr). + */ +#define MAX_CHUNK_LEN 2016 +#define MAX_DESC_LEN (2 * MAX_CHUNK_LEN) + +/* Tulip DMA descriptor; THIS STRUCT MUST MATCH THE HARDWARE */ +struct dma_desc + { + u_int32_t status; /* hardware->to->software */ +#if (BYTE_ORDER == LITTLE_ENDIAN) /* left-to-right packing by compiler */ + unsigned length1:11; /* buffer1 length */ + unsigned length2:11; /* buffer2 length */ + unsigned control:10; /* software->to->hardware */ +#else /* right-to-left packing by compiler */ + unsigned control:10; /* software->to->hardware */ + unsigned length2:11; /* buffer2 length */ + unsigned length1:11; /* buffer1 length */ +#endif + u_int32_t address1; /* buffer1 bus address */ + u_int32_t address2; /* buffer2 bus address */ +#if (__FreeBSD__ || __NetBSD__ || __OpenBSD__) + bus_dmamap_t map; /* bus dmamap for this descriptor */ +# define TLP_BUS_DSL_VAL (sizeof(bus_dmamap_t) & TLP_BUS_DSL) +#else +# define TLP_BUS_DSL_VAL 0 +#endif + } __attribute__ ((packed)); + +/* Tulip DMA descriptor status bits */ +#define TLP_DSTS_OWNER 0x80000000 +#define TLP_DSTS_RX_DESC_ERR 0x00004000 +#define TLP_DSTS_RX_FIRST_DESC 0x00000200 +#define TLP_DSTS_RX_LAST_DESC 0x00000100 +#define TLP_DSTS_RX_MII_ERR 0x00000008 +#define TLP_DSTS_RX_DRIBBLE 0x00000004 +#define TLP_DSTS_TX_UNDERRUN 0x00000002 +#define TLP_DSTS_RX_OVERRUN 0x00000001 /* not documented in rev AF */ +#define TLP_DSTS_RX_BAD (TLP_DSTS_RX_MII_ERR | \ + TLP_DSTS_RX_DRIBBLE | \ + TLP_DSTS_RX_DESC_ERR | \ + TLP_DSTS_RX_OVERRUN) + +/* Tulip DMA descriptor control bits */ +#define TLP_DCTL_TX_INTERRUPT 0x0200 +#define TLP_DCTL_TX_LAST_SEG 0x0100 +#define TLP_DCTL_TX_FIRST_SEG 0x0080 +#define TLP_DCTL_TX_NO_CRC 0x0010 +#define TLP_DCTL_END_RING 0x0008 +#define TLP_DCTL_TX_NO_PAD 0x0002 + +/* DMA descriptors are kept in a ring. + * Ring is empty when (read == write). + * Ring is full when (read == wrap(write+1)), + * The ring also contains a tailq of data buffers. + */ +struct desc_ring + { + struct dma_desc *read; /* next descriptor to be read */ + struct dma_desc *write; /* next descriptor to be written */ + struct dma_desc *first; /* first descriptor in ring */ + struct dma_desc *last; /* last descriptor in ring */ + struct dma_desc *temp; /* temporary write pointer for tx */ + u_int32_t dma_addr; /* bus address for desc array */ + int size_descs; /* bus_dmamap_sync needs this */ + int num_descs; /* used to set rx quota */ +#if __linux__ + struct sk_buff *head; /* tail-queue of skbuffs */ + struct sk_buff *tail; +#elif BSD + struct mbuf *head; /* tail-queue of mbufs */ + struct mbuf *tail; +# if (__FreeBSD__ || __NetBSD__ || __OpenBSD__) + bus_dma_tag_t tag; /* bus_dma tag for desc array */ + bus_dmamap_t map; /* bus_dma map for desc array */ + bus_dma_segment_t segs[2]; /* bus_dmamap_load() or bus_dmamem_alloc() */ + int nsegs; /* bus_dmamap_load() or bus_dmamem_alloc() */ +# endif +#endif + }; + +/* break circular definition */ +typedef struct softc softc_t; + +/* card-dependent methods */ +struct card + { + void (* config)(softc_t *); + void (* ident)(softc_t *); + int (* watchdog)(softc_t *); /* must not sleep */ + int (* ioctl)(softc_t *, struct ioctl *); /* can sleep */ + }; + +/* flag bits in sc->flags */ +#define FLAG_IFNET 0x00000002 /* IFNET is attached */ +#define FLAG_NETDEV 0x00000004 /* NETDEV is registered */ +#define FLAG_NETGRAPH 0x00000008 /* NETGRAPH is attached */ + +/* Accessing Tulip CSRs: + * There are two ways: IO instruction (default) and memory reference. + * IO refs are used if IOREF_CSR is defined; otherwise memory refs are used. + * MEMORY REFERENCES DO NOT WORK in BSD/OS: page faults happen. + */ +#define IOREF_CSR 1 /* access Tulip CSRs with IO cycles if 1 */ + +#if (__FreeBSD__ && DEVICE_POLLING) +# define DEV_POLL 1 +#else +# define DEV_POLL 0 +#endif + +#if (ALTQ != 0) +# define ALTQ_PRESENT 1 +#else +# define ALTQ_PRESENT 0 +#endif + +/* This is the instance data, or "software context" for the device driver. */ +/* NetBSD, OpenBSD and BSD/OS want struct device first in the softc. */ +/* FreeBSD wants struct ifnet first in the softc. */ +struct softc + { +#if (__NetBSD__ || __OpenBSD__) + struct device dev; /* base device -- must be first in softc */ + pcitag_t pa_tag; /* pci_conf_read/write need this */ + pci_chipset_tag_t pa_pc; /* pci_conf_read/write need this */ + bus_dma_tag_t pa_dmat; /* bus_dma needs this */ + bus_space_tag_t csr_tag; /* bus_space needs this */ + bus_space_handle_t csr_handle;/* bus_space needs this */ + pci_intr_handle_t intr_handle;/* interrupt handle */ + void *irq_cookie; /* pci_intr_disestablish needs this */ + void *sdh_cookie; /* shutdownhook_disestablish needs this */ + struct simplelock top_lock; /* lock card->watchdog vs core_ioctl */ + struct simplelock bottom_lock;/* lock for buf queues & descriptor rings */ + struct mbuf *tx_mbuf; /* hang mbuf here while building dma descs */ +#endif /* __NetBSD__ || __OpenBSD__ */ + +#if __bsdi__ + struct device dev; /* base device -- must be first in softc */ + struct isadev id; /* bus resource */ + struct intrhand ih; /* interrupt vectoring */ + struct atshutdown ats; /* shutdown hook */ + pci_devaddr_t cfgbase; /* base address of PCI config regs */ + u_int16_t csr_iobase; /* io base address of Tulip CSRs */ + u_int32_t *csr_membase; /* kv mem base address of Tulip CSRs */ + struct simplelock top_lock; /* lock card->watchdog vs core_ioctl */ + struct simplelock bottom_lock;/* lock for buf queues & descriptor rings */ + struct mbuf *tx_mbuf; /* hang mbuf here while building dma descs */ +#endif /* __bsdi__ */ + + /* State for kernel-resident Line Protocols */ +#if IFNET + struct ifnet *ifp; + struct ifmedia ifm; /* hooks for ifconfig(8) */ +# if NSPPP +# if (__FreeBSD_version < 600000) + struct sppp spppcom; /* must be first in sc for fbsd < 6 */ +# endif + struct sppp *sppp; +# elif P2P + struct p2pcom p2pcom; + struct p2pcom *p2p; +# elif (__FreeBSD_version < 600000) + struct ifnet ifnet; /* must be first in sc for fbsd < 6 */ +# endif +#endif + +#if __linux__ +# if GEN_HDLC + hdlc_device *hdlc_dev; /* state for HDLC code */ + sync_serial_settings hdlc_settings; /* state set by sethdlc program */ +# else + struct net_device_stats net_stats; /* linux_stats storage */ +# endif +#endif + +#if NETGRAPH + struct callout ng_callout; /* ng_watchdog needs this */ + node_p ng_node; /* pointer to our node struct */ + hook_p ng_hook; /* non-zero means NETGRAPH owns device */ +# if (__FreeBSD_version >= 503000) + struct ifaltq ng_sndq; + struct ifaltq ng_fastq; +# else + struct ifqueue ng_sndq; + struct ifqueue ng_fastq; +# endif +#endif + +#if __FreeBSD__ + struct device *dev; /* base device pointer */ + bus_space_tag_t csr_tag; /* bus_space needs this */ + bus_space_handle_t csr_handle;/* bus_space_needs this */ + void *irq_cookie; /* bus_teardown_intr needs this */ + struct resource *irq_res; /* bus_release_resource needs this */ + int irq_res_id; /* bus_release_resource needs this */ + struct resource *csr_res; /* bus_release_resource needs this */ + int csr_res_id; /* bus_release resource needs this */ + int csr_res_type; /* bus_release resource needs this */ + struct mbuf *tx_mbuf; /* hang mbuf here while building dma descs */ +# if DEVICE_POLLING + int quota; /* used for incoming packet flow control */ +# endif +# if (__FreeBSD_version >= 500000) + struct mtx top_mtx; /* lock card->watchdog vs core_ioctl */ + struct mtx bottom_mtx; /* lock for buf queues & descriptor rings */ +# else /* FreeBSD-4 */ + int top_spl; /* lock card->watchdog vs core_ioctl */ + int bottom_spl; /* lock for buf queues & descriptor rings */ +# endif +#endif /* __FreeBSD__ */ + +#if __linux__ + struct pci_dev *pci_dev; /* READ/WRITE_PCI_CFG macros need this */ + struct net_device *net_dev; /* NAME_UNIT macro needs this */ + struct timer_list wd_timer; /* timer calls watchdog() once a second */ + u_int32_t csr_iobase; /* io base address of Tulip CSRs */ + void *csr_membase; /* kv mem base address of Tulip CSRs */ + struct sk_buff *tx_skb; /* hang skb here while building dma descs */ + int quota; /* used for incoming packet flow control */ + struct semaphore top_lock; /* lock card->watchdog vs core_ioctl */ + spinlock_t bottom_lock; /* lock for buf queues & descriptor rings */ +#endif /* __linux__ */ + + /* Top-half state used by all card types; lock with top_lock, */ + const char *dev_desc; /* string describing type of board */ + struct status status; /* driver status lmcconfig can read */ + struct config config; /* driver config lmcconfig can read/write */ + struct card *card; /* card methods: config, ioctl, watchdog */ + u_int32_t gpio_dir; /* s/w copy of GPIO direction register */ + u_int16_t led_state; /* last value written to mii16 */ + u_int32_t flags; /* driver-global flags */ + + /* Top-half state used by card-specific watchdogs; lock with top_lock. */ + u_int32_t last_mii16; /* SSI, HSSI: MII reg 16 one second ago */ + u_int32_t last_stat16; /* T3: framer reg 16 one second ago */ + u_int32_t last_alm1; /* T1E1: framer reg 47 one second ago */ + u_int32_t last_FEAC; /* last FEAC msg code received */ + u_int32_t loop_timer; /* seconds until loopback expires */ + + /* Bottom-half state used by the interrupt code; lock with bottom_lock. */ + struct desc_ring txring; /* tx descriptor ring state */ + struct desc_ring rxring; /* rx descriptor ring state */ + }; /* end of softc */ + +/* Hide the minor differences between OS versions */ + +#if __FreeBSD__ + typedef void intr_return_t; +# define READ_PCI_CFG(sc, addr) pci_read_config ((sc)->dev, addr, 4) +# define WRITE_PCI_CFG(sc, addr, data) pci_write_config((sc)->dev, addr, data, 4) +# define READ_CSR(csr) bus_space_read_4 (sc->csr_tag, sc->csr_handle, csr) +# define WRITE_CSR(csr, val) bus_space_write_4(sc->csr_tag, sc->csr_handle, csr, val) +# define NAME_UNIT device_get_nameunit(sc->dev) +# define DRIVER_DEBUG ((sc->config.debug) || (sc->ifp->if_flags & IFF_DEBUG)) +# if (__FreeBSD_version >= 500000) +# define TOP_TRYLOCK mtx_trylock(&sc->top_mtx) +# define TOP_UNLOCK mtx_unlock (&sc->top_mtx) +# define BOTTOM_TRYLOCK mtx_trylock(&sc->bottom_mtx) +# define BOTTOM_UNLOCK mtx_unlock (&sc->bottom_mtx) +# define CHECK_CAP suser(curthread) +# else /* FreeBSD-4 */ +# define TOP_TRYLOCK (sc->top_spl = splimp()) +# define TOP_UNLOCK splx(sc->top_spl) +# define BOTTOM_TRYLOCK 1 /* giant_lock protects */ +# define BOTTOM_UNLOCK /* nothing */ +# define CHECK_CAP suser(curproc) +# endif +# define DISABLE_INTR /* nothing */ +# define ENABLE_INTR /* nothing */ +# define IRQ_NONE /* nothing */ +# define IRQ_HANDLED /* nothing */ +# define IFP2SC(ifp) (ifp)->if_softc +# define COPY_BREAK MHLEN +# define SLEEP(usecs) tsleep(sc, PCATCH | PZERO, DEVICE_NAME, 1+(usecs/tick)) +# define DMA_SYNC(map, size, flags) bus_dmamap_sync(ring->tag, map, flags) +# define DMA_LOAD(map, addr, size) bus_dmamap_load(ring->tag, map, addr, size, fbsd_dmamap_load, ring, 0) +# if (NBPFILTER != 0) +# if (__FreeBSD_version >= 500000) +# define LMC_BPF_MTAP(mbuf) if (sc->ifp->if_bpf) bpf_mtap(sc->ifp->if_bpf, mbuf) +# else /* FreeBSD-4 */ +# define LMC_BPF_MTAP(mbuf) if (sc->ifp->if_bpf) bpf_mtap(sc->ifp, mbuf) +# endif +# define LMC_BPF_ATTACH(dlt, len) bpfattach(sc->ifp, dlt, len) +# define LMC_BPF_DETACH bpfdetach(sc->ifp) +# endif +# if (__FreeBSD_version >= 500000) +# define IF_DROP(ifq) _IF_DROP(ifq) +# define IF_QFULL(ifq) _IF_QFULL(ifq) +# endif +# if (__FreeBSD_version < 500000) +# define INTR_MPSAFE 0 +# define BUS_DMA_COHERENT 0 +# endif +# if (__FreeBSD_version >= 600000) +# define IFF_RUNNING IFF_DRV_RUNNING +# endif +#endif /* __FreeBSD__ */ + +#if __NetBSD__ + typedef int intr_return_t; +# define READ_PCI_CFG(sc, addr) pci_conf_read ((sc)->pa_pc, (sc)->pa_tag, addr) +# define WRITE_PCI_CFG(sc, addr, data) pci_conf_write((sc)->pa_pc, (sc)->pa_tag, addr, data) +# define READ_CSR(csr) bus_space_read_4 (sc->csr_tag, sc->csr_handle, csr) +# define WRITE_CSR(csr, val) bus_space_write_4(sc->csr_tag, sc->csr_handle, csr, val) +# define NAME_UNIT sc->dev.dv_xname +# define DRIVER_DEBUG ((sc->config.debug) || (sc->ifp->if_flags & IFF_DEBUG)) +# define TOP_TRYLOCK simple_lock_try(&sc->top_lock) +# define TOP_UNLOCK simple_unlock (&sc->top_lock) +# define BOTTOM_TRYLOCK simple_lock_try(&sc->bottom_lock) +# define BOTTOM_UNLOCK simple_unlock (&sc->bottom_lock) +# define CHECK_CAP suser(curproc->p_ucred, &curproc->p_acflag) +# define DISABLE_INTR int spl = splnet() +# define ENABLE_INTR splx(spl) +# define IRQ_NONE 0 +# define IRQ_HANDLED 1 +# define IFP2SC(ifp) (ifp)->if_softc +# define COPY_BREAK MHLEN +# define SLEEP(usecs) tsleep(sc, PCATCH | PZERO, DEVICE_NAME, 1+(usecs/tick)) +# define DMA_SYNC(map, size, flags) bus_dmamap_sync(ring->tag, map, 0, size, flags) +# define DMA_LOAD(map, addr, size) bus_dmamap_load(ring->tag, map, addr, size, 0, BUS_DMA_NOWAIT) +# if (NBPFILTER != 0) +# define LMC_BPF_MTAP(mbuf) if (sc->ifp->if_bpf) bpf_mtap(sc->ifp->if_bpf, mbuf) +# define LMC_BPF_ATTACH(dlt, len) bpfattach(sc->ifp, dlt, len) +# define LMC_BPF_DETACH bpfdetach(sc->ifp) +# endif +#endif /* __NetBSD__ */ + +#if __OpenBSD__ + typedef int intr_return_t; +# define READ_PCI_CFG(sc, addr) pci_conf_read ((sc)->pa_pc, (sc)->pa_tag, addr) +# define WRITE_PCI_CFG(sc, addr, data) pci_conf_write((sc)->pa_pc, (sc)->pa_tag, addr, data) +# define READ_CSR(csr) bus_space_read_4 (sc->csr_tag, sc->csr_handle, csr) +# define WRITE_CSR(csr, val) bus_space_write_4(sc->csr_tag, sc->csr_handle, csr, val) +# define NAME_UNIT sc->dev.dv_xname +# define DRIVER_DEBUG ((sc->config.debug) || (sc->ifp->if_flags & IFF_DEBUG)) +# define TOP_TRYLOCK simple_lock_try(&sc->top_lock) +# define TOP_UNLOCK simple_unlock (&sc->top_lock) +# define BOTTOM_TRYLOCK simple_lock_try(&sc->bottom_lock) +# define BOTTOM_UNLOCK simple_unlock (&sc->bottom_lock) +# define CHECK_CAP suser(curproc, 0) +# define DISABLE_INTR int spl = splnet() +# define ENABLE_INTR splx(spl) +# define IRQ_NONE 0 +# define IRQ_HANDLED 1 +# define IFP2SC(ifp) (ifp)->if_softc +# define COPY_BREAK MHLEN +# define SLEEP(usecs) tsleep(sc, PCATCH | PZERO, DEVICE_NAME, 1+(usecs/tick)) +# define DMA_SYNC(map, size, flags) bus_dmamap_sync(ring->tag, map, 0, size, flags) +# define DMA_LOAD(map, addr, size) bus_dmamap_load(ring->tag, map, addr, size, 0, BUS_DMA_NOWAIT) +# if (NBPFILTER != 0) +# define LMC_BPF_MTAP(mbuf) if (sc->ifp->if_bpf) bpf_mtap(sc->ifp->if_bpf, mbuf) +# define LMC_BPF_ATTACH(dlt, len) bpfattach(&sc->ifp->if_bpf, sc->ifp, dlt, len) +# define LMC_BPF_DETACH bpfdetach(sc->ifp) +# endif +#endif /* __OpenBSD__ */ + +#if __bsdi__ + typedef int intr_return_t; +# define READ_PCI_CFG(sc, addr) pci_inl(&(sc)->cfgbase, addr) +# define WRITE_PCI_CFG(sc, addr, data) pci_outl(&(sc)->cfgbase, addr, data) +# if IOREF_CSR +# define READ_CSR(csr) inl(sc->csr_iobase+(csr)) +# define WRITE_CSR(csr, val) outl(sc->csr_iobase+(csr), (val)) +# else +# error Memory refs to Tulip CSRs cause page faults in BSD/OS +# define READ_CSR(csr) (0 + *(sc->csr_membase+(csr))) +# define WRITE_CSR(csr, val) ((void)(*(sc->csr_membase+(csr)) = (val))) +# endif +# define NAME_UNIT sc->dev.dv_xname +# define DRIVER_DEBUG ((sc->config.debug) || (sc->ifp->if_flags & IFF_DEBUG)) +# define TOP_TRYLOCK simple_lock_try(&sc->top_lock) +# define TOP_UNLOCK simple_unlock (&sc->top_lock) +# define BOTTOM_TRYLOCK simple_lock_try(&sc->bottom_lock) +# define BOTTOM_UNLOCK simple_unlock (&sc->bottom_lock) +# define CHECK_CAP suser(PCPU(curproc)->p_ucred, &PCPU(curproc)->p_acflag) +# define DISABLE_INTR int spl = splimp() +# define ENABLE_INTR splx(spl) +# define IRQ_NONE 1 /* XXX 0 */ +# define IRQ_HANDLED 1 +# define IFP2SC(ifp) (ifp)->if_softc +# define COPY_BREAK MHLEN +# define SLEEP(usecs) tsleep(sc, PCATCH | PZERO, DEVICE_NAME, 1+(usecs/tick)) +# define DMA_SYNC(map, size, flags) /* nothing */ +# define DMA_LOAD(map, addr, size) 0 +# define bus_dmamap_unload(tag, map) /* nothing */ +# define bus_dmamap_destroy(tag, map) /* nothing */ +# if (NBPFILTER != 0) +# define LMC_BPF_MTAP(mbuf) if (sc->ifp->if_bpf) bpf_mtap(sc->ifp->if_bpf, mbuf) +# define LMC_BPF_ATTACH(dlt, len) bpfattach(&sc->ifp->if_bpf, sc->ifp, dlt, len) +# define LMC_BPF_DETACH /* bpfdetach(sc->ifp) */ +# endif +# define memcpy(dst, src, len) bcopy(src, dst, len) +# define if_detach(ifp) /* nothing */ + +/* BSD/OS-4.1 doesn't have a back pointer to softc in struct ifnet, */ +/* and it passes a unit number not a struct ifnet* to watchdog. */ +# if (_BSDI_VERSION <= 199910) + extern struct cfdriver lmccd; +# undef IFP2SC +# define UNIT2SC(unit) ((softc_t *)lmccd.cd_devs[unit]) +# define IFP2SC(ifp) (UNIT2SC((ifp)->if_unit)) +# endif +#endif /* __bsdi__ */ + +#if __linux__ +static u_int32_t /* inline? so rare it doesn't matter */ +READ_PCI_CFG(softc_t *sc, u_int32_t addr) + { + u_int32_t data; + pci_read_config_dword(sc->pci_dev, addr, &data); + return data; + } +# define WRITE_PCI_CFG(sc, addr, data) pci_write_config_dword(sc->pci_dev, addr, data) +# if IOREF_CSR +# define READ_CSR(csr) inl((sc->csr_iobase+(csr))) +# define WRITE_CSR(csr, val) outl((val),(sc->csr_iobase+(csr))) +# else +# define READ_CSR(csr) readl((sc->csr_membase+(csr))) +# define WRITE_CSR(csr, val) writel((val),(sc->csr_membase+(csr))) +# endif +# define NAME_UNIT sc->net_dev->name +# define DRIVER_DEBUG ((sc->config.debug) || (sc->net_dev->flags & IFF_DEBUG)) +# define TOP_TRYLOCK ((down_trylock(&sc->top_lock)==0) ? 1:0) +# define TOP_UNLOCK up(&sc->top_lock) +# define BOTTOM_TRYLOCK spin_trylock_bh(&sc->bottom_lock) +# define BOTTOM_UNLOCK spin_unlock_bh(&sc->bottom_lock) +# define CHECK_CAP capable(CAP_NET_ADMIN)? 0 : -EPERM +# define DISABLE_INTR /* nothing */ +# define ENABLE_INTR /* nothing */ +# define COPY_BREAK 200 +# define DELAY(usecs) udelay(usecs) +# define SLEEP(usecs) do { set_current_state(TASK_INTERRUPTIBLE);\ + schedule_timeout(1+(usecs*HZ)/1000000UL); } while (0) +# define printf printk +# define copyin(u, k, len) copy_from_user(k, u, len) +# define microtime(time) do_gettimeofday(time) +# define malloc(len, t, f) kmalloc(len, GFP_KERNEL) +# define free(addr, t) kfree(addr) +# define LITTLE_ENDIAN 4321 +# define BIG_ENDIAN 1234 +# if defined(__LITTLE_ENDIAN) +# define BYTE_ORDER LITTLE_ENDIAN +# elif defined(__BIG_ENDIAN) +# define BYTE_ORDER BIG_ENDIAN +# else +# error "asm/byteorder.h is wrong" +# endif +# if (GEN_HDLC == 0) +# define dev_to_hdlc(net_dev) net_dev +# define hdlc_set_carrier(val, net_dev) /* nothing */ +# endif +#endif /* __linux__ */ + +#if (NBPFILTER == 0) +# define LMC_BPF_MTAP(mbuf) /* nothing */ +# define LMC_BPF_ATTACH(dlt, len) /* nothing */ +# define LMC_BPF_DETACH /* nothing */ +#endif + +#if (__bsdi__ || /* unconditionally */ \ + (__FreeBSD__ && (__FreeBSD_version < 503000)) || \ + (__NetBSD__ && (__NetBSD_Version__ < 106000000)) || \ + (__OpenBSD__ && ( OpenBSD < 200111))) +# define IFQ_ENQUEUE(ifq, m, pa, err) \ +do { \ + if (pa==0); /* suppress warning */ \ + if (IF_QFULL(ifq)) \ + { \ + IF_DROP(ifq); \ + m_freem(m); \ + err = ENOBUFS; \ + } \ + else \ + { \ + IF_ENQUEUE(ifq, m); \ + err = 0; \ + } \ + } while (0) +# define IFQ_DEQUEUE(ifq, m) do { IF_DEQUEUE((ifq), m) } while (0) +# define IFQ_IS_EMPTY(ifq) ((ifq)->ifq_head == NULL) +# define IFQ_SET_MAXLEN(ifq, len) (ifq)->ifq_maxlen = len +# define IFQ_SET_READY(ifq) /* nothing */ +# define IFQ_PURGE(ifq) \ +do { \ + while ((ifq)->ifq_head != NULL) \ + { \ + struct mbuf *m; \ + IF_DEQUEUE(ifq, m); \ + m_freem(m); \ + } \ + } while (0) +#endif + +#define HSSI_DESC "SBE/LMC HSSI Card" +#define T3_DESC "SBE/LMC T3 Card" +#define SSI_DESC "SBE/LMC SSI Card" +#define T1E1_DESC "SBE/LMC T1E1 Card" + +/* procedure prototypes */ + +static void shift_srom_bits(softc_t *, u_int32_t, u_int32_t); +static u_int16_t read_srom(softc_t *, u_int8_t); +static void write_srom(softc_t *, u_int8_t, u_int16_t); + +static u_int8_t read_bios(softc_t *, u_int32_t); +static void write_bios_phys(softc_t *, u_int32_t, u_int8_t); +static void write_bios(softc_t *, u_int32_t, u_int8_t); +static void erase_bios(softc_t *); + +static void shift_mii_bits(softc_t *, u_int32_t, u_int32_t); +static u_int16_t read_mii(softc_t *, u_int8_t); +static void write_mii(softc_t *, u_int8_t, u_int16_t); + +static void set_mii16_bits(softc_t *, u_int16_t); +static void clr_mii16_bits(softc_t *, u_int16_t); +static void set_mii17_bits(softc_t *, u_int16_t); +static void clr_mii17_bits(softc_t *, u_int16_t); + +static void led_off(softc_t *, u_int16_t); +static void led_on(softc_t *, u_int16_t); +static void led_inv(softc_t *, u_int16_t); + +static void write_framer(softc_t *, u_int16_t, u_int8_t); +static u_int8_t read_framer(softc_t *, u_int16_t); + +static void make_gpio_input(softc_t *, u_int32_t); +static void make_gpio_output(softc_t *, u_int32_t); +static u_int32_t read_gpio(softc_t *); +static void set_gpio_bits(softc_t *, u_int32_t); +static void clr_gpio_bits(softc_t *, u_int32_t); + +static void reset_xilinx(softc_t *); +static void load_xilinx_from_rom(softc_t *); +static int load_xilinx_from_file(softc_t *, char *, u_int32_t); + +static void shift_synth_bits(softc_t *, u_int32_t, u_int32_t); +static void write_synth(softc_t *, struct synth *); + +static void write_dac(softc_t *, u_int16_t); + +static void hssi_config(softc_t *); +static void hssi_ident(softc_t *); +static int hssi_watchdog(softc_t *); +static int hssi_ioctl(softc_t *, struct ioctl *); + +static void t3_config(softc_t *); +static void t3_ident(softc_t *); +static int t3_watchdog(softc_t *); +static void t3_send_dbl_feac(softc_t *, int, int); +static int t3_ioctl(softc_t *, struct ioctl *); + +static void ssi_config(softc_t *); +static void ssi_ident(softc_t *); +static int ssi_watchdog(softc_t *); +static int ssi_ioctl(softc_t *, struct ioctl *); + +static void t1_config(softc_t *); +static void t1_ident(softc_t *); +static int t1_watchdog(softc_t *); +static void t1_send_bop(softc_t *, int); +static int t1_ioctl(softc_t *, struct ioctl *); + +#if IFNET +# if ((__FreeBSD__ && (__FreeBSD_version < 500000)) ||\ + __NetBSD__ || __OpenBSD__ || __bsdi__) +static void netisr_dispatch(int, struct mbuf *); +# endif +static void raw_input(struct ifnet *, struct mbuf *); +#endif /* IFNET */ + +#if BSD +static void mbuf_enqueue(struct desc_ring *, struct mbuf *); +static struct mbuf* mbuf_dequeue(struct desc_ring *); +# if __FreeBSD__ +static void fbsd_dmamap_load(void *, bus_dma_segment_t *, int, int); +# endif +static int create_ring(softc_t *, struct desc_ring *, int); +static void destroy_ring(softc_t *, struct desc_ring *); +static int rxintr_cleanup(softc_t *); +static int rxintr_setup(softc_t *); +static int txintr_cleanup(softc_t *); +static int txintr_setup_mbuf(softc_t *, struct mbuf *); +static int txintr_setup(softc_t *); +#endif /* BSD */ + +#if __linux__ +static void skbuff_enqueue(struct desc_ring *, struct sk_buff *); +static struct sk_buff* skbuff_dequeue(struct desc_ring *); +static int create_ring(softc_t *, struct desc_ring *, int); +static void destroy_ring(softc_t *, struct desc_ring *); +static int rxintr_cleanup(softc_t *); +static int rxintr_setup(softc_t *); +static int txintr_cleanup(softc_t *sc); +static int txintr_setup_frag(softc_t *, char *, int); +static int txintr_setup_skb(softc_t *, struct sk_buff *); +static int txintr_setup(softc_t *); +#endif /* __linux__ */ + +static void check_intr_status(softc_t *); +static void core_interrupt(void *, int); +static void user_interrupt(softc_t *, int); +#if BSD +# if (__FreeBSD__ && DEVICE_POLLING) +static void fbsd_poll(struct ifnet *, enum poll_cmd, int); +# endif +static intr_return_t bsd_interrupt(void *); +#endif /* BSD */ + +static void set_status(softc_t *, int); +#if P2P +static int p2p_getmdm(struct p2pcom *, caddr_t); +static int p2p_mdmctl(struct p2pcom *, int); +#endif +#if NSPPP +static void sppp_tls(struct sppp *); +static void sppp_tlf(struct sppp *); +#endif + +static void config_proto(softc_t *, struct config *); +static int core_ioctl(softc_t *, u_long, caddr_t); +static void core_watchdog(softc_t *); + +#if IFNET +static int raw_ioctl(struct ifnet *, u_long, caddr_t); +static int ifnet_ioctl(struct ifnet *, u_long, caddr_t); +static void ifnet_start(struct ifnet *); +static int raw_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); +static void ifnet_watchdog(struct ifnet *); +# if __OpenBSD__ +static int ifmedia_change(struct ifnet *); +static void ifmedia_status(struct ifnet *, struct ifmediareq *); +# endif /* __OpenBSD__ */ +static void setup_ifnet(struct ifnet *); +static int ifnet_attach(softc_t *); +static void ifnet_detach(softc_t *); +#endif /* IFNET */ + +#if NETGRAPH +# if (__FreeBSD_version >= 500000) +static int ng_constructor(node_p); +# else /* FreeBSD-4 */ +static int ng_constructor(node_p *); +# endif +# if (__FreeBSD_version >= 500000) +static int ng_rcvmsg(node_p, item_p, hook_p); +# else /* FreeBSD-4 */ +static int ng_rcvmsg(node_p, struct ng_mesg *, + const char *, struct ng_mesg **); +# endif +static int ng_shutdown(node_p); +static int ng_newhook(node_p, hook_p, const char *); +static int ng_connect(hook_p); +# if (__FreeBSD_version >= 500000) +static int ng_rcvdata(hook_p, item_p); +# else /* FreeBSD-4 */ +static int ng_rcvdata(hook_p, struct mbuf *, meta_p); +# endif +static int ng_disconnect(hook_p); +# if (IFNET == 0) +static void ng_watchdog(void *); +# endif +static int ng_attach(softc_t *); +static void ng_detach(softc_t *); +#endif /* NETGRAPH */ + +static int startup_card(softc_t *); +static void shutdown_card(void *); +static int attach_card(softc_t *, const char *); +static void detach_card(softc_t *); + +#if __FreeBSD__ +static int fbsd_probe(device_t); +static int fbsd_detach(device_t); +static void fbsd_shutdown(device_t); +static int fbsd_attach(device_t); +#endif /* __FreeBSD__ */ + +#if __NetBSD__ +static int nbsd_match(struct device *t, struct cfdata *, void *); +static int nbsd_detach(struct device *, int); +static void nbsd_attach(struct device *, struct device *, void *); +static int lkm_nbsd_match(struct pci_attach_args *); +int if_lmc_lkmentry(struct lkm_table *, int, int); +#endif /* __NetBSD__ */ + +#if __OpenBSD__ +static int obsd_match(struct device *, void *, void *); +static int obsd_detach(struct device *, int); +static void obsd_attach(struct device *, struct device *, void *); +int if_lmc_lkmentry(struct lkm_table *, int, int); +#endif /* __OpenBSD__ */ + +#if __bsdi__ +static int bsdi_match(pci_devaddr_t *); +static int bsdi_probe(struct device *, struct cfdata *, void *); +static void bsdi_attach(struct device *, struct device *, void *); +#endif /* __bsdi__ */ + +#if __linux__ +static irqreturn_t linux_interrupt(int, void *, struct pt_regs *); +static int linux_poll(struct net_device *, int *); +static int linux_start(struct sk_buff *, struct net_device *); +static void linux_timeout(struct net_device *); +static int linux_ioctl(struct net_device *, struct ifreq *, int); +static struct net_device_stats * linux_stats(struct net_device *); +static void linux_watchdog(unsigned long); +static int linux_stop(struct net_device *); +static int linux_open(struct net_device *); +# if GEN_HDLC +static int hdlc_attach(struct net_device *, + unsigned short, unsigned short); +# endif +static void __exit linux_remove(struct pci_dev *); +static void setup_netdev(struct net_device *); +static int __init linux_probe(struct pci_dev *, const struct pci_device_id *); +#endif /* __linux__ */ + +#endif /* KERNEL */ + +#endif /* IF_LMC_H */ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index e570a89..32a86c7 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -131,6 +131,7 @@ SUBDIR= ${_3dfx} \ libmchain \ ${_linprocfs} \ ${_linux} \ + lmc \ ${_lnc} \ lpt \ mac_biba \ diff --git a/sys/modules/lmc/Makefile b/sys/modules/lmc/Makefile new file mode 100644 index 0000000..fa56579 --- /dev/null +++ b/sys/modules/lmc/Makefile @@ -0,0 +1,25 @@ +# $FreeBSD$ + +KMOD = if_lmc +.PATH: ${.CURDIR}/../../dev/lmc + +SRCS = if_lmc.c if_lmc.h +SRCS += device_if.h bus_if.h pci_if.h +SRCS += opt_inet.h opt_inet6.h +SRCS += opt_netgraph.h +SRCS += opt_global.h +SRCS += opt_bpf.h + +opt_inet.h: + echo "#define INET 1" > ${.TARGET} +opt_inet6.h: + echo "#define INET6 0" > ${.TARGET} +opt_netgraph.h: + echo "#define NETGRAPH 1" > ${.TARGET} +opt_global.h: + echo "#define ALTQ 1" > ${.TARGET} + echo "#define DEVICE_POLLING 1" >> ${.TARGET} +opt_bpf.h: # FreeBSD-5: + echo "#define DEV_BPF 1" > ${.TARGET} + +.include <bsd.kmod.mk> |