NetBSD/sys/dev/pci/if_lmc.c

5523 lines
161 KiB
C
Raw Normal View History

/* $NetBSD: if_lmc.c,v 1.50 2010/11/13 13:52:06 uebayasi Exp $ */
/*-
2006-02-12 02:00:06 +03:00
* Copyright (c) 2002-2006 David Boggs. <boggs@boggs.palo-alto.ca.us>
2005-12-06 06:01:45 +03:00
* All rights reserved.
*
2006-04-20 20:51:57 +04:00
* 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.
2005-12-06 06:01:45 +03:00
* 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.
*
2005-12-06 06:01:45 +03:00
* 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.
*
2006-04-20 20:51:57 +04:00
* GNU GENERAL PUBLIC LICENSE:
2005-12-06 06:01:45 +03:00
*
2006-02-12 02:00:06 +03:00
* 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)
2005-12-06 06:01:45 +03:00
* any later version.
2006-02-12 02:00:06 +03:00
*
* 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
2005-12-06 06:01:45 +03:00
* more details.
*
* You should have received a copy of the GNU General Public License along with
2006-02-12 02:00:06 +03:00
* this program; if not, write to the Free Software Foundation, Inc., 59
2005-12-06 06:01:45 +03:00
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
2006-04-20 20:51:57 +04:00
* DESCRIPTION:
2005-12-06 06:01:45 +03:00
*
* 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.
2006-02-12 02:00:06 +03:00
* A generic PC plus Unix plus some LMC cards makes an OPEN router.
2005-12-06 06:01:45 +03:00
* This driver works with FreeBSD, NetBSD, OpenBSD, BSD/OS and Linux.
2006-02-12 02:00:06 +03:00
* It has been tested on i386 (32-bit little-end), PowerPC (32-bit
* big-end), Sparc (64-bit big-end), and Alpha (64-bit little-end)
* architectures.
*
2006-04-20 20:51:57 +04:00
* HISTORY AND AUTHORS:
2005-12-06 06:01:45 +03:00
*
* 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.
*
2006-04-20 20:51:57 +04:00
* RESPONSIBLE INDIVIDUAL:
2005-12-06 06:01:45 +03:00
*
* Send bug reports and improvements to <boggs@boggs.palo-alto.ca.us>.
*/
2005-12-06 06:01:45 +03:00
2007-12-11 14:25:46 +03:00
# include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_lmc.c,v 1.50 2010/11/13 13:52:06 uebayasi Exp $");
2005-12-06 06:01:45 +03:00
# include <sys/param.h> /* OS version */
# include "opt_inet.h" /* INET6, INET */
# include "opt_altq_enabled.h" /* ALTQ */
2006-04-20 20:51:57 +04:00
# define IOREF_CSR 1 /* 1=IO refs; 0=MEM refs */
# define IFNET 1
# define NETDEV 0
# define NAPI 0
# define SPPP 1
# define P2P 0
# define GEN_HDLC 0
# define SYNC_PPP 0
# define NETGRAPH 0
# define DEVICE_POLLING 0
2005-12-06 06:01:45 +03:00
#
# include <sys/systm.h>
# include <sys/kernel.h>
# include <sys/module.h>
2005-12-06 06:01:45 +03:00
# include <sys/mbuf.h>
# include <sys/socket.h>
# include <sys/sockio.h>
# include <sys/device.h>
2006-02-12 02:00:06 +03:00
# include <sys/reboot.h>
2006-05-15 01:42:26 +04:00
# include <sys/kauth.h>
# include <sys/proc.h>
2005-12-06 06:01:45 +03:00
# include <net/if.h>
# include <net/if_types.h>
# include <net/if_media.h>
# include <net/netisr.h>
# include <sys/bus.h>
# include <sys/intr.h>
2006-04-20 20:51:57 +04:00
# include <machine/lock.h>
# include <machine/types.h>
2005-12-06 06:01:45 +03:00
# include <dev/pci/pcivar.h>
2006-02-12 02:00:06 +03:00
# if INET || INET6
2005-12-06 06:01:45 +03:00
# include <netinet/in.h>
# include <netinet/in_var.h>
# endif
2006-02-12 02:00:06 +03:00
# if SPPP
# include <net/if_spppvar.h>
2005-12-06 06:01:45 +03:00
# endif
# include <net/bpf.h>
2006-04-20 20:51:57 +04:00
# if !defined(ALTQ)
2005-12-06 06:01:45 +03:00
# define ALTQ 0
# endif
/* and finally... */
# include "if_lmc.h"
/* 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. */
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
srom_shift_bits(softc_t *sc, u_int32_t data, u_int32_t len)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
u_int32_t csr = READ_CSR(sc, TLP_SROM_MII);
2005-12-06 06:01:45 +03:00
for (; len>0; len--)
{ /* MSB first */
if (data & (1<<(len-1)))
csr |= TLP_SROM_DIN; /* DIN setup */
else
csr &= ~TLP_SROM_DIN; /* DIN setup */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
csr |= TLP_SROM_CLK; /* CLK rising edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
csr &= ~TLP_SROM_CLK; /* CLK falling edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
}
}
/* Data is sampled on the RISING edge of CLK; CLK is parked low. */
2006-04-20 20:51:57 +04:00
static u_int16_t /* context: process */
2006-02-12 02:00:06 +03:00
srom_read(softc_t *sc, u_int8_t addr)
2005-12-06 06:01:45 +03:00
{
int i;
u_int32_t csr;
u_int16_t data;
/* Enable SROM access. */
csr = (TLP_SROM_SEL | TLP_SROM_RD | TLP_MII_MDOE);
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
/* CS rising edge prepares SROM for a new cycle. */
csr |= TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* assert CS */
2006-02-12 02:00:06 +03:00
srom_shift_bits(sc, 6, 4); /* issue read cmd */
srom_shift_bits(sc, addr, 6); /* issue address */
2005-12-06 06:01:45 +03:00
for (data=0, i=16; i>=0; i--) /* read ->17<- bits of data */
{ /* MSB first */
2006-04-20 20:51:57 +04:00
csr = READ_CSR(sc, TLP_SROM_MII); /* DOUT sampled */
2005-12-06 06:01:45 +03:00
data = (data<<1) | ((csr & TLP_SROM_DOUT) ? 1:0);
csr |= TLP_SROM_CLK; /* CLK rising edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
csr &= ~TLP_SROM_CLK; /* CLK falling edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
}
/* Disable SROM access. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, TLP_MII_MDOE);
2005-12-06 06:01:45 +03:00
return data;
}
2005-12-06 06:01:45 +03:00
/* The SROM is formatted by the mfgr and should NOT be written! */
/* But lmcconfig can rewrite it in case it gets overwritten somehow. */
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
srom_write(softc_t *sc, u_int8_t addr, u_int16_t data)
2005-12-06 06:01:45 +03:00
{
u_int32_t csr;
int i;
/* Enable SROM access. */
csr = (TLP_SROM_SEL | TLP_SROM_RD | TLP_MII_MDOE);
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
/* Issue write-enable command. */
csr |= TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* assert CS */
2006-02-12 02:00:06 +03:00
srom_shift_bits(sc, 4, 4); /* issue write enable cmd */
srom_shift_bits(sc, 63, 6); /* issue address */
2005-12-06 06:01:45 +03:00
csr &= ~TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* deassert CS */
2005-12-06 06:01:45 +03:00
/* Issue erase command. */
csr |= TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* assert CS */
2006-02-12 02:00:06 +03:00
srom_shift_bits(sc, 7, 4); /* issue erase cmd */
srom_shift_bits(sc, addr, 6); /* issue address */
2005-12-06 06:01:45 +03:00
csr &= ~TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* deassert CS */
2005-12-06 06:01:45 +03:00
/* Issue write command. */
csr |= TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* assert CS */
2005-12-06 06:01:45 +03:00
for (i=0; i<10; i++) /* 100 ms max wait */
2006-04-20 20:51:57 +04:00
if ((READ_CSR(sc, TLP_SROM_MII) & TLP_SROM_DOUT)==0) SLEEP(10000);
2006-02-12 02:00:06 +03:00
srom_shift_bits(sc, 5, 4); /* issue write cmd */
srom_shift_bits(sc, addr, 6); /* issue address */
srom_shift_bits(sc, data, 16); /* issue data */
2005-12-06 06:01:45 +03:00
csr &= ~TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* deassert CS */
2005-12-06 06:01:45 +03:00
/* Issue write-disable command. */
csr |= TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* assert CS */
2005-12-06 06:01:45 +03:00
for (i=0; i<10; i++) /* 100 ms max wait */
2006-04-20 20:51:57 +04:00
if ((READ_CSR(sc, TLP_SROM_MII) & TLP_SROM_DOUT)==0) SLEEP(10000);
2006-02-12 02:00:06 +03:00
srom_shift_bits(sc, 4, 4); /* issue write disable cmd */
srom_shift_bits(sc, 0, 6); /* issue address */
2005-12-06 06:01:45 +03:00
csr &= ~TLP_SROM_CS;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr); /* deassert CS */
2005-12-06 06:01:45 +03:00
/* Disable SROM access. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, TLP_MII_MDOE);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
/* Not all cards have BIOS roms. */
2005-12-06 06:01:45 +03:00
/* The BIOS ROM is an AMD 29F010 1Mbit (128K by 8) EEPROM. */
2006-04-20 20:51:57 +04:00
static u_int8_t /* context: process */
2006-02-12 02:00:06 +03:00
bios_read(softc_t *sc, u_int32_t addr)
2005-12-06 06:01:45 +03:00
{
u_int32_t srom_mii;
/* Load the BIOS rom address register. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_BIOS_ROM, addr);
2005-12-06 06:01:45 +03:00
/* Enable the BIOS rom. */
srom_mii = TLP_BIOS_SEL | TLP_BIOS_RD | TLP_MII_MDOE;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, srom_mii);
2005-12-06 06:01:45 +03:00
/* Wait at least 20 PCI cycles. */
DELAY(20);
/* Read the BIOS rom data. */
2006-04-20 20:51:57 +04:00
srom_mii = READ_CSR(sc, TLP_SROM_MII);
2005-12-06 06:01:45 +03:00
/* Disable the BIOS rom. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, TLP_MII_MDOE);
2005-12-06 06:01:45 +03:00
return (u_int8_t)srom_mii & 0xFF;
}
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
bios_write_phys(softc_t *sc, u_int32_t addr, u_int8_t data)
2005-12-06 06:01:45 +03:00
{
u_int32_t srom_mii;
2005-12-06 06:01:45 +03:00
/* Load the BIOS rom address register. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_BIOS_ROM, addr);
2005-12-06 06:01:45 +03:00
/* Enable the BIOS rom. */
srom_mii = TLP_BIOS_SEL | TLP_BIOS_WR | TLP_MII_MDOE;
2005-12-06 06:01:45 +03:00
/* Load the data into the data register. */
srom_mii = (srom_mii & 0xFFFFFF00) | (data & 0xFF);
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, srom_mii);
2005-12-06 06:01:45 +03:00
/* Wait at least 20 PCI cycles. */
DELAY(20);
2005-12-06 06:01:45 +03:00
/* Disable the BIOS rom. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, TLP_MII_MDOE);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
bios_write(softc_t *sc, u_int32_t addr, u_int8_t data)
2005-12-06 06:01:45 +03:00
{
u_int8_t read_data;
/* this sequence enables writing */
2006-02-12 02:00:06 +03:00
bios_write_phys(sc, 0x5555, 0xAA);
bios_write_phys(sc, 0x2AAA, 0x55);
bios_write_phys(sc, 0x5555, 0xA0);
bios_write_phys(sc, addr, data);
2005-12-06 06:01:45 +03:00
/* Wait for the write operation to complete. */
2006-02-12 02:00:06 +03:00
for (;;) /* interruptible syscall */
2005-12-06 06:01:45 +03:00
{
for (;;)
{
2006-02-12 02:00:06 +03:00
read_data = bios_read(sc, addr);
2005-12-06 06:01:45 +03:00
if ((read_data & 0x80) == (data & 0x80)) break;
if (read_data & 0x20)
{ /* Data sheet says read it again. */
2006-02-12 02:00:06 +03:00
read_data = bios_read(sc, addr);
2005-12-06 06:01:45 +03:00
if ((read_data & 0x80) == (data & 0x80)) break;
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
printf("%s: bios_write() failed; rom addr=0x%x\n",
2005-12-06 06:01:45 +03:00
NAME_UNIT, addr);
return;
}
}
2006-02-12 02:00:06 +03:00
read_data = bios_read(sc, addr);
2005-12-06 06:01:45 +03:00
if (read_data == data) break;
}
}
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
bios_erase(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
unsigned char read_data;
/* This sequence enables erasing: */
2006-02-12 02:00:06 +03:00
bios_write_phys(sc, 0x5555, 0xAA);
bios_write_phys(sc, 0x2AAA, 0x55);
bios_write_phys(sc, 0x5555, 0x80);
bios_write_phys(sc, 0x5555, 0xAA);
bios_write_phys(sc, 0x2AAA, 0x55);
bios_write_phys(sc, 0x5555, 0x10);
2005-12-06 06:01:45 +03:00
/* Wait for the erase operation to complete. */
2006-02-12 02:00:06 +03:00
for (;;) /* interruptible syscall */
2005-12-06 06:01:45 +03:00
{
for (;;)
{
2006-02-12 02:00:06 +03:00
read_data = bios_read(sc, 0);
2005-12-06 06:01:45 +03:00
if (read_data & 0x80) break;
if (read_data & 0x20)
{ /* Data sheet says read it again. */
2006-02-12 02:00:06 +03:00
read_data = bios_read(sc, 0);
2005-12-06 06:01:45 +03:00
if (read_data & 0x80) break;
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
printf("%s: bios_erase() failed\n", NAME_UNIT);
2005-12-06 06:01:45 +03:00
return;
}
}
2006-02-12 02:00:06 +03:00
read_data = bios_read(sc, 0);
2005-12-06 06:01:45 +03:00
if (read_data == 0xFF) break;
}
}
2005-12-06 06:01:45 +03:00
/* MDIO is 3-stated between tranactions. */
/* MDIO is set up before the RISING edge of MDC; MDC is parked low. */
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
mii_shift_bits(softc_t *sc, u_int32_t data, u_int32_t len)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
u_int32_t csr = READ_CSR(sc, TLP_SROM_MII);
2005-12-06 06:01:45 +03:00
for (; len>0; len--)
{ /* MSB first */
if (data & (1<<(len-1)))
csr |= TLP_MII_MDOUT; /* MDOUT setup */
else
csr &= ~TLP_MII_MDOUT; /* MDOUT setup */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
csr |= TLP_MII_MDC; /* MDC rising edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
csr &= ~TLP_MII_MDC; /* MDC falling edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
}
}
/* 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. */
2006-04-20 20:51:57 +04:00
static u_int16_t /* context: process */
2006-02-12 02:00:06 +03:00
mii_read(softc_t *sc, u_int8_t regad)
2005-12-06 06:01:45 +03:00
{
int i;
u_int32_t csr;
u_int16_t data = 0;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, TLP_MII_MDOUT);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
mii_shift_bits(sc, 0xFFFFF, 20); /* preamble */
mii_shift_bits(sc, 0xFFFFF, 20); /* preamble */
mii_shift_bits(sc, 1, 2); /* start symbol */
mii_shift_bits(sc, 2, 2); /* read op */
mii_shift_bits(sc, 0, 5); /* phyad=0 */
mii_shift_bits(sc, regad, 5); /* regad */
2006-04-20 20:51:57 +04:00
csr = READ_CSR(sc, TLP_SROM_MII);
2005-12-06 06:01:45 +03:00
csr |= TLP_MII_MDOE;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2006-02-12 02:00:06 +03:00
mii_shift_bits(sc, 0, 2); /* turn-around */
2005-12-06 06:01:45 +03:00
for (i=15; i>=0; i--) /* data */
{ /* MSB first */
2006-04-20 20:51:57 +04:00
csr = READ_CSR(sc, TLP_SROM_MII); /* MDIN sampled */
2005-12-06 06:01:45 +03:00
data = (data<<1) | ((csr & TLP_MII_MDIN) ? 1:0);
csr |= TLP_MII_MDC; /* MDC rising edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
csr &= ~TLP_MII_MDC; /* MDC falling edge */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, csr);
2005-12-06 06:01:45 +03:00
}
return data;
}
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
mii_write(softc_t *sc, u_int8_t regad, u_int16_t data)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, TLP_MII_MDOUT);
2006-02-12 02:00:06 +03:00
mii_shift_bits(sc, 0xFFFFF, 20); /* preamble */
mii_shift_bits(sc, 0xFFFFF, 20); /* preamble */
mii_shift_bits(sc, 1, 2); /* start symbol */
mii_shift_bits(sc, 1, 2); /* write op */
mii_shift_bits(sc, 0, 5); /* phyad=0 */
mii_shift_bits(sc, regad, 5); /* regad */
mii_shift_bits(sc, 2, 2); /* turn-around */
mii_shift_bits(sc, data, 16); /* data */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_SROM_MII, TLP_MII_MDOE);
2005-12-06 06:01:45 +03:00
if (regad == 16) sc->led_state = data; /* a small optimization */
}
2005-12-06 06:01:45 +03:00
static void
2006-02-12 02:00:06 +03:00
mii16_set_bits(softc_t *sc, u_int16_t bits)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
u_int16_t mii16 = mii_read(sc, 16);
2005-12-06 06:01:45 +03:00
mii16 |= bits;
2006-02-12 02:00:06 +03:00
mii_write(sc, 16, mii16);
2005-12-06 06:01:45 +03:00
}
static void
2006-02-12 02:00:06 +03:00
mii16_clr_bits(softc_t *sc, u_int16_t bits)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
u_int16_t mii16 = mii_read(sc, 16);
2005-12-06 06:01:45 +03:00
mii16 &= ~bits;
2006-02-12 02:00:06 +03:00
mii_write(sc, 16, mii16);
2005-12-06 06:01:45 +03:00
}
static void
2006-02-12 02:00:06 +03:00
mii17_set_bits(softc_t *sc, u_int16_t bits)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
u_int16_t mii17 = mii_read(sc, 17);
2005-12-06 06:01:45 +03:00
mii17 |= bits;
2006-02-12 02:00:06 +03:00
mii_write(sc, 17, mii17);
2005-12-06 06:01:45 +03:00
}
static void
2006-02-12 02:00:06 +03:00
mii17_clr_bits(softc_t *sc, u_int16_t bits)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
u_int16_t mii17 = mii_read(sc, 17);
2005-12-06 06:01:45 +03:00
mii17 &= ~bits;
2006-02-12 02:00:06 +03:00
mii_write(sc, 17, mii17);
2005-12-06 06:01:45 +03:00
}
/*
2005-12-06 06:01:45 +03:00
* 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.
*/
2005-12-06 06:01:45 +03:00
static void
led_off(softc_t *sc, u_int16_t led)
{
if ((led & sc->led_state) == led) return;
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, led);
2005-12-06 06:01:45 +03:00
}
2005-02-27 03:26:58 +03:00
static void
2005-12-06 06:01:45 +03:00
led_on(softc_t *sc, u_int16_t led)
{
if ((led & sc->led_state) == 0) return;
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, led);
2005-12-06 06:01:45 +03:00
}
static void
2005-12-06 06:01:45 +03:00
led_inv(softc_t *sc, u_int16_t led)
{
2006-02-12 02:00:06 +03:00
u_int16_t mii16 = mii_read(sc, 16);
2005-12-06 06:01:45 +03:00
mii16 ^= led;
2006-02-12 02:00:06 +03:00
mii_write(sc, 16, mii16);
2005-12-06 06:01:45 +03:00
}
/*
2005-12-06 06:01:45 +03:00
* 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
2006-02-12 02:00:06 +03:00
framer_write(softc_t *sc, u_int16_t addr, u_int8_t data)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
mii_write(sc, 17, addr);
mii_write(sc, 18, data);
2005-12-06 06:01:45 +03:00
}
static u_int8_t
2006-02-12 02:00:06 +03:00
framer_read(softc_t *sc, u_int16_t addr)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
mii_write(sc, 17, addr);
return (u_int8_t)mii_read(sc, 18);
2005-12-06 06:01:45 +03:00
}
/* 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.
*/
2005-12-06 06:01:45 +03:00
static void
2006-02-12 02:00:06 +03:00
gpio_make_input(softc_t *sc, u_int32_t bits)
2005-12-06 06:01:45 +03:00
{
sc->gpio_dir &= ~bits;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_GPIO, TLP_GPIO_DIR | (sc->gpio_dir));
2005-12-06 06:01:45 +03:00
}
2005-12-06 06:01:45 +03:00
static void
2006-02-12 02:00:06 +03:00
gpio_make_output(softc_t *sc, u_int32_t bits)
2005-12-06 06:01:45 +03:00
{
sc->gpio_dir |= bits;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_GPIO, TLP_GPIO_DIR | (sc->gpio_dir));
2005-12-06 06:01:45 +03:00
}
2005-12-06 06:01:45 +03:00
static u_int32_t
2006-02-12 02:00:06 +03:00
gpio_read(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
return READ_CSR(sc, TLP_GPIO);
2005-12-06 06:01:45 +03:00
}
static void
2006-02-12 02:00:06 +03:00
gpio_set_bits(softc_t *sc, u_int32_t bits)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_GPIO, (gpio_read(sc) | bits) & 0xFF);
2005-12-06 06:01:45 +03:00
}
static void
2006-02-12 02:00:06 +03:00
gpio_clr_bits(softc_t *sc, u_int32_t bits)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_GPIO, (gpio_read(sc) & ~bits) & 0xFF);
2005-12-06 06:01:45 +03:00
}
/* 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. */
2006-04-20 20:51:57 +04:00
static void /* context: kernel (boot) or process (syscall) */
2006-02-12 02:00:06 +03:00
xilinx_reset(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
/* Drive RESET low to force initialization. */
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_RESET);
gpio_make_output(sc, GPIO_RESET);
2005-12-06 06:01:45 +03:00
/* Hold RESET low for more than 10 uSec. */
DELAY(50);
/* Done with RESET; make it an input. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_RESET);
2005-12-06 06:01:45 +03:00
}
/* Load Xilinx gate array program from on-board rom. */
/* This changes the gate array programming. */
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
xilinx_load_from_rom(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
int i;
2005-12-06 06:01:45 +03:00
/* Drive MODE low to load from ROM rather than GPIO. */
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_MODE);
gpio_make_output(sc, GPIO_MODE);
2005-12-06 06:01:45 +03:00
/* Drive DP & RESET low to force configuration. */
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_RESET | GPIO_DP);
gpio_make_output(sc, GPIO_RESET | GPIO_DP);
2005-12-06 06:01:45 +03:00
/* Hold RESET & DP low for more than 10 uSec. */
DELAY(50);
2005-12-06 06:01:45 +03:00
/* Done with RESET & DP; make them inputs. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_DP | GPIO_RESET);
2005-12-06 06:01:45 +03:00
/* BUSY-WAIT for Xilinx chip to configure itself from ROM bits. */
for (i=0; i<100; i++) /* 1 sec max delay */
2006-02-12 02:00:06 +03:00
if ((gpio_read(sc) & GPIO_DP)==0) SLEEP(10000);
2005-12-06 06:01:45 +03:00
/* Done with MODE; make it an input. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_MODE);
2005-12-06 06:01:45 +03:00
}
/* Load the Xilinx gate array program from userland bits. */
/* This changes the gate array programming. */
2006-04-20 20:51:57 +04:00
static int /* context: process */
2006-02-12 02:00:06 +03:00
xilinx_load_from_file(softc_t *sc, char *addr, u_int32_t len)
2005-12-06 06:01:45 +03:00
{
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_TEMP, M_WAITOK);
if (data == NULL) return ENOMEM;
/* Copy the Xilinx bits from userland. */
if ((error = copyin(addr, data, len)))
{
free(data, M_TEMP);
return error;
}
/* Drive MODE high to load from GPIO rather than ROM. */
2006-02-12 02:00:06 +03:00
gpio_set_bits(sc, GPIO_MODE);
gpio_make_output(sc, GPIO_MODE);
2005-12-06 06:01:45 +03:00
/* Drive DP & RESET low to force configuration. */
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_RESET | GPIO_DP);
gpio_make_output(sc, GPIO_RESET | GPIO_DP);
2005-12-06 06:01:45 +03:00
/* Hold RESET & DP low for more than 10 uSec. */
DELAY(50);
2006-04-20 20:51:57 +04:00
2005-12-06 06:01:45 +03:00
/* Done with RESET & DP; make them inputs. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_RESET | GPIO_DP);
2005-12-06 06:01:45 +03:00
/* BUSY-WAIT for Xilinx chip to clear its config memory. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_INIT);
2005-12-06 06:01:45 +03:00
for (i=0; i<10000; i++) /* 1 sec max delay */
2006-02-12 02:00:06 +03:00
if ((gpio_read(sc) & GPIO_INIT)==0) SLEEP(10000);
2005-12-06 06:01:45 +03:00
/* Configure CLK and DATA as outputs. */
2006-02-12 02:00:06 +03:00
gpio_set_bits(sc, GPIO_CLK); /* park CLK high */
gpio_make_output(sc, GPIO_CLK | GPIO_DATA);
2005-12-06 06:01:45 +03:00
/* 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 */
2006-02-12 02:00:06 +03:00
if (data[i] & (1<<j))
gpio_set_bits(sc, GPIO_DATA); /* DATA setup */
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_DATA); /* DATA setup */
gpio_clr_bits(sc, GPIO_CLK); /* CLK falling edge */
gpio_set_bits(sc, GPIO_CLK); /* CLK rising edge */
2005-12-06 06:01:45 +03:00
}
/* Stop driving all Xilinx-related signals. */
/* Pullup and pulldown resistors take over. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_CLK | GPIO_DATA | GPIO_MODE);
2005-12-06 06:01:45 +03:00
free(data, M_TEMP);
2006-02-12 02:00:06 +03:00
2005-12-06 06:01:45 +03:00
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
2006-02-12 02:00:06 +03:00
synth_shift_bits(softc_t *sc, u_int32_t data, u_int32_t len)
2005-12-06 06:01:45 +03:00
{
int i;
for (i=0; i<len; i++)
{ /* LSB first */
2006-02-12 02:00:06 +03:00
if (data & (1<<i))
gpio_set_bits(sc, GPIO_DATA); /* DATA setup */
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_DATA); /* DATA setup */
gpio_set_bits(sc, GPIO_CLK); /* CLK rising edge */
gpio_clr_bits(sc, GPIO_CLK); /* CLK falling edge */
2005-12-06 06:01:45 +03:00
}
}
2005-12-06 06:01:45 +03:00
/* Write a command to the synthesized oscillator on SSI and HSSIc. */
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
synth_write(softc_t *sc, struct synth *synth)
2005-12-06 06:01:45 +03:00
{
/* SSI cards have a programmable prescaler */
2006-04-20 20:51:57 +04:00
if (sc->status.card_type == CSID_LMC_SSI)
2005-12-06 06:01:45 +03:00
{
if (synth->prescale == 9) /* divide by 512 */
2006-02-12 02:00:06 +03:00
mii17_set_bits(sc, MII17_SSI_PRESCALE);
2005-12-06 06:01:45 +03:00
else /* divide by 32 */
2006-02-12 02:00:06 +03:00
mii17_clr_bits(sc, MII17_SSI_PRESCALE);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_DATA | GPIO_CLK);
gpio_make_output(sc, GPIO_DATA | GPIO_CLK);
2005-12-06 06:01:45 +03:00
/* SYNTH is a low-true chip enable for the AV9110 chip. */
2006-02-12 02:00:06 +03:00
gpio_set_bits(sc, GPIO_SSI_SYNTH);
gpio_make_output(sc, GPIO_SSI_SYNTH);
gpio_clr_bits(sc, GPIO_SSI_SYNTH);
2005-12-06 06:01:45 +03:00
/* Serially shift the command into the AV9110 chip. */
2006-02-12 02:00:06 +03:00
synth_shift_bits(sc, synth->n, 7);
synth_shift_bits(sc, synth->m, 7);
synth_shift_bits(sc, synth->v, 1);
synth_shift_bits(sc, synth->x, 2);
synth_shift_bits(sc, synth->r, 2);
synth_shift_bits(sc, 0x16, 5); /* enable clk/x output */
2005-12-06 06:01:45 +03:00
/* SYNTH (chip enable) going high ends the command. */
2006-02-12 02:00:06 +03:00
gpio_set_bits(sc, GPIO_SSI_SYNTH);
gpio_make_input(sc, GPIO_SSI_SYNTH);
2005-12-06 06:01:45 +03:00
/* Stop driving serial-related signals; pullups/pulldowns take over. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_DATA | GPIO_CLK);
2005-12-06 06:01:45 +03:00
/* 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. */
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
dac_write(softc_t *sc, u_int16_t data)
2005-12-06 06:01:45 +03:00
{
int i;
/* Prepare to use DATA and CLK. */
2006-02-12 02:00:06 +03:00
gpio_set_bits(sc, GPIO_DATA | GPIO_CLK);
gpio_make_output(sc, GPIO_DATA | GPIO_CLK);
2005-12-06 06:01:45 +03:00
/* High-to-low transition prepares DAC for new value. */
2006-02-12 02:00:06 +03:00
gpio_set_bits(sc, GPIO_T3_DAC);
gpio_make_output(sc, GPIO_T3_DAC);
gpio_clr_bits(sc, GPIO_T3_DAC);
2005-12-06 06:01:45 +03:00
/* Serially shift command bits into DAC. */
for (i=0; i<16; i++)
{ /* MSB first */
2006-02-12 02:00:06 +03:00
if (data & (1<<(15-i)))
gpio_set_bits(sc, GPIO_DATA); /* DATA setup */
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_DATA); /* DATA setup */
gpio_clr_bits(sc, GPIO_CLK); /* CLK falling edge */
gpio_set_bits(sc, GPIO_CLK); /* CLK rising edge */
2005-12-06 06:01:45 +03:00
}
2005-12-06 06:01:45 +03:00
/* Done with DAC; make it an input; loads new value into DAC. */
2006-02-12 02:00:06 +03:00
gpio_set_bits(sc, GPIO_T3_DAC);
gpio_make_input(sc, GPIO_T3_DAC);
2005-12-06 06:01:45 +03:00
/* Stop driving serial-related signals; pullups/pulldowns take over. */
2006-02-12 02:00:06 +03:00
gpio_make_input(sc, GPIO_DATA | GPIO_CLK);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Begin HSSI card code */
2006-02-12 02:00:06 +03:00
static struct card hssi_card =
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
.ident = hssi_ident,
.watchdog = hssi_watchdog,
.ioctl = hssi_ioctl,
.attach = hssi_attach,
.detach = hssi_detach,
};
static void
hssi_ident(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
printf(", EIA-613");
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
2005-12-06 06:01:45 +03:00
hssi_watchdog(softc_t *sc)
{
2006-02-12 02:00:06 +03:00
u_int16_t mii16 = mii_read(sc, 16) & MII16_HSSI_MODEM;
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
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);
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_DOWN;
2005-12-06 06:01:45 +03:00
}
else
led_off(sc, MII16_HSSI_LED_UR);
/* Is the modem ready? */
2006-02-12 02:00:06 +03:00
if ((mii16 & MII16_HSSI_CA)==0)
2005-12-06 06:01:45 +03:00
{
led_off(sc, MII16_HSSI_LED_LR);
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_DOWN;
2005-12-06 06:01:45 +03:00
}
else
led_on(sc, MII16_HSSI_LED_LR);
/* Print the modem control signals if they changed. */
2006-02-12 02:00:06 +03:00
if ((sc->config.debug) && (mii16 != sc->last_mii16))
2005-12-06 06:01:45 +03:00
{
const 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);
}
2005-12-06 06:01:45 +03:00
/* SNMP one-second-report */
sc->status.snmp.hssi.sigs = mii16 & MII16_HSSI_MODEM;
2005-12-06 06:01:45 +03:00
/* 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)
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* context: process */
2005-12-06 06:01:45 +03:00
hssi_ioctl(softc_t *sc, struct ioctl *ioctl)
{
int error = 0;
if (ioctl->cmd == IOCTL_SNMP_SIGS)
{
2006-02-12 02:00:06 +03:00
u_int16_t mii16 = mii_read(sc, 16);
2005-12-06 06:01:45 +03:00
mii16 &= ~MII16_HSSI_MODEM;
mii16 |= (MII16_HSSI_MODEM & ioctl->data);
2006-02-12 02:00:06 +03:00
mii_write(sc, 16, mii16);
2005-12-06 06:01:45 +03:00
}
else if (ioctl->cmd == IOCTL_SET_STATUS)
{
2006-02-12 02:00:06 +03:00
if (ioctl->data)
mii16_set_bits(sc, MII16_HSSI_TA);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_HSSI_TA);
}
2005-12-06 06:01:45 +03:00
else
error = EINVAL;
2005-12-06 06:01:45 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
/* Must not sleep. */
static void
2006-02-12 02:00:06 +03:00
hssi_attach(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
if (config == NULL) /* startup config */
{
sc->status.card_type = READ_PCI_CFG(sc, TLP_CSID);
2005-12-06 06:01:45 +03:00
sc->config.crc_len = CFG_CRC_16;
sc->config.loop_back = CFG_LOOP_NONE;
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else if (config != &sc->config) /* change config */
{
u_int32_t *old_synth = (u_int32_t *)&sc->config.synth;
u_int32_t *new_synth = (u_int32_t *)&config->synth;
if ((sc->config.crc_len == config->crc_len) &&
(sc->config.loop_back == config->loop_back) &&
(sc->config.tx_clk_src == config->tx_clk_src) &&
(sc->config.dte_dce == config->dte_dce) &&
(*old_synth == *new_synth))
return; /* nothing changed */
sc->config.crc_len = config->crc_len;
sc->config.loop_back = config->loop_back;
sc->config.tx_clk_src = config->tx_clk_src;
sc->config.dte_dce = config->dte_dce;
*old_synth = *new_synth;
}
/* If (config == &sc->config) then the FPGA microcode
* was just initialized and the current config should
* be reloaded into the card.
*/
2006-02-12 02:00:06 +03:00
/* set CRC length */
if (sc->config.crc_len == CFG_CRC_32)
mii16_set_bits(sc, MII16_HSSI_CRC32);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_HSSI_CRC32);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Assert pin LA in HSSI conn: ask modem for local loop. */
if (sc->config.loop_back == CFG_LOOP_LL)
mii16_set_bits(sc, MII16_HSSI_LA);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_HSSI_LA);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Assert pin LB in HSSI conn: ask modem for remote loop. */
if (sc->config.loop_back == CFG_LOOP_RL)
mii16_set_bits(sc, MII16_HSSI_LB);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_HSSI_LB);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
if (sc->status.card_type == CSID_LMC_HSSI)
2006-02-12 02:00:06 +03:00
{
/* set TXCLK src */
if (sc->config.tx_clk_src == CFG_CLKMUX_ST)
gpio_set_bits(sc, GPIO_HSSI_TXCLK);
else
gpio_clr_bits(sc, GPIO_HSSI_TXCLK);
gpio_make_output(sc, GPIO_HSSI_TXCLK);
}
2006-04-20 20:51:57 +04:00
else if (sc->status.card_type == CSID_LMC_HSSIc)
2006-02-12 02:00:06 +03:00
{ /* cPCI HSSI rev C has extra features */
/* Set TXCLK source. */
u_int16_t mii16 = mii_read(sc, 16);
mii16 &= ~MII16_HSSI_CLKMUX;
mii16 |= (sc->config.tx_clk_src&3)<<13;
mii_write(sc, 16, mii16);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* cPCI HSSI implements loopback towards the net. */
if (sc->config.loop_back == CFG_LOOP_LINE)
mii16_set_bits(sc, MII16_HSSI_LOOP);
else
mii16_clr_bits(sc, MII16_HSSI_LOOP);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Set DTE/DCE mode. */
if (sc->config.dte_dce == CFG_DCE)
gpio_set_bits(sc, GPIO_HSSI_DCE);
else
gpio_clr_bits(sc, GPIO_HSSI_DCE);
gpio_make_output(sc, GPIO_HSSI_DCE);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Program the synthesized oscillator. */
synth_write(sc, &sc->config.synth);
}
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
static void
hssi_detach(softc_t *sc)
{
mii16_clr_bits(sc, MII16_HSSI_TA);
led_on(sc, MII16_LED_ALL);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* End HSSI card code */
/* Begin DS3 card code */
static struct card t3_card =
{
.ident = t3_ident,
.watchdog = t3_watchdog,
.ioctl = t3_ioctl,
.attach = t3_attach,
.detach = t3_detach,
};
2005-12-06 06:01:45 +03:00
static void
t3_ident(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
printf(", TXC03401 rev B");
}
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
2005-12-06 06:01:45 +03:00
t3_watchdog(softc_t *sc)
{
u_int16_t CV;
u_int8_t CERR, PERR, MERR, FERR, FEBE;
u_int8_t ctl1, stat16, feac;
u_int16_t mii16;
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
/* Read the alarm registers. */
2006-02-12 02:00:06 +03:00
ctl1 = framer_read(sc, T3CSR_CTL1);
stat16 = framer_read(sc, T3CSR_STAT16);
mii16 = mii_read(sc, 16);
2005-12-06 06:01:45 +03:00
/* Always ignore the RTLOC alarm bit. */
stat16 &= ~STAT16_RTLOC;
/* Software is alive. */
led_inv(sc, MII16_DS3_LED_GRN);
/* Receiving Alarm Indication Signal (AIS). */
2006-02-12 02:00:06 +03:00
if (stat16 & STAT16_RAIS) /* receiving ais */
2005-12-06 06:01:45 +03:00
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). */
2006-02-12 02:00:06 +03:00
if (stat16 & STAT16_XERR) /* receiving rai */
2005-12-06 06:01:45 +03:00
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. */
2006-02-12 02:00:06 +03:00
if (stat16 & ~(STAT16_FEAC | STAT16_SEF))
sc->status.link_state = STATE_DOWN;
2005-12-06 06:01:45 +03:00
/* Declare local Red Alarm if the link is down. */
2006-02-12 02:00:06 +03:00
if (sc->status.link_state == STATE_DOWN)
2005-12-06 06:01:45 +03:00
led_on(sc, MII16_DS3_LED_RED);
2006-02-12 02:00:06 +03:00
else if (sc->loop_timer) /* loopback is active */
2005-12-06 06:01:45 +03:00
led_inv(sc, MII16_DS3_LED_RED);
else
led_off(sc, MII16_DS3_LED_RED);
/* Print latched error bits if they changed. */
2006-02-12 02:00:06 +03:00
if ((sc->config.debug) && ((stat16 & ~STAT16_FEAC) != sc->last_stat16))
2005-12-06 06:01:45 +03:00
{
const 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. */
2006-02-12 02:00:06 +03:00
CV = framer_read(sc, T3CSR_CVHI)<<8;
CV += framer_read(sc, T3CSR_CVLO);
PERR = framer_read(sc, T3CSR_PERR);
CERR = framer_read(sc, T3CSR_CERR);
FERR = framer_read(sc, T3CSR_FERR);
MERR = framer_read(sc, T3CSR_MERR);
FEBE = framer_read(sc, T3CSR_FEBE);
2005-12-06 06:01:45 +03:00
/* CV is invalid during LOS. */
2006-02-12 02:00:06 +03:00
if (stat16 & STAT16_RLOS) CV = 0;
2005-12-06 06:01:45 +03:00
/* CERR & FEBE are invalid in M13 mode */
if (sc->config.format == CFG_FORMAT_T3M13) CERR = FEBE = 0;
/* FEBE is invalid during AIS. */
2006-02-12 02:00:06 +03:00
if (stat16 & STAT16_RAIS) FEBE = 0;
if (sc->config.debug && (CV || PERR || CERR || FERR || MERR || FEBE))
2005-12-06 06:01:45 +03:00
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
{
2006-02-12 02:00:06 +03:00
feac = framer_read(sc, T3CSR_FEAC_STK);
2005-12-06 06:01:45 +03:00
if ((feac & FEAC_STK_VALID)==0) break;
/* Ignore RxFEACs while a far end loopback has been requested. */
2006-02-12 02:00:06 +03:00
if (sc->status.snmp.t3.line & TLOOP_FAR_LINE) continue;
2005-12-06 06:01:45 +03:00
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)
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'line loopback deactivate' FEAC msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_DS3_LNLBK);
2005-12-06 06:01:45 +03:00
sc->loop_timer = 0;
}
if (sc->last_FEAC == T3BOP_LINE_UP)
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'line loopback activate' FEAC msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_LNLBK);
2005-12-06 06:01:45 +03:00
sc->loop_timer = 300;
}
break;
}
case T3BOP_OOF:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'far end LOF' FEAC msg\n", NAME_UNIT);
break;
}
2005-12-06 06:01:45 +03:00
case T3BOP_IDLE:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'far end IDL' FEAC msg\n", NAME_UNIT);
break;
}
2005-12-06 06:01:45 +03:00
case T3BOP_AIS:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'far end AIS' FEAC msg\n", NAME_UNIT);
break;
}
2005-12-06 06:01:45 +03:00
case T3BOP_LOS:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'far end LOS' FEAC msg\n", NAME_UNIT);
break;
}
2005-12-06 06:01:45 +03:00
default:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'type 0x%02X' FEAC msg\n", NAME_UNIT, feac & FEAC_STK_FEAC);
break;
}
2005-12-06 06:01:45 +03:00
}
sc->last_FEAC = feac & FEAC_STK_FEAC;
2006-02-12 02:00:06 +03:00
} while (feac & FEAC_STK_MORE);
2005-12-06 06:01:45 +03:00
stat16 &= ~STAT16_FEAC;
/* Send Service-Affecting priority FEAC messages */
if (((sc->last_stat16 ^ stat16) & 0xF0) && (FORMAT_T3CPAR))
{
/* Transmit continuous FEACs */
2006-02-12 02:00:06 +03:00
framer_write(sc, T3CSR_CTL14,
framer_read(sc, T3CSR_CTL14) & ~CTL14_FEAC10);
if (stat16 & STAT16_RLOS)
framer_write(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_LOS);
else if (stat16 & STAT16_ROOF)
framer_write(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_OOF);
else if (stat16 & STAT16_RAIS)
framer_write(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_AIS);
else if (stat16 & STAT16_RIDL)
framer_write(sc, T3CSR_TX_FEAC, 0xC0 + T3BOP_IDLE);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
framer_write(sc, T3CSR_TX_FEAC, CTL5_EMODE);
2005-12-06 06:01:45 +03:00
}
/* Start sending RAI, Remote Alarm Indication. */
2006-02-12 02:00:06 +03:00
if ((stat16 & STAT16_ROOF) && !(stat16 & STAT16_RLOS) &&
!(sc->last_stat16 & STAT16_ROOF))
framer_write(sc, T3CSR_CTL1, ctl1 &= ~CTL1_XTX);
2005-12-06 06:01:45 +03:00
/* Stop sending RAI, Remote Alarm Indication. */
2006-02-12 02:00:06 +03:00
else if (!(stat16 & STAT16_ROOF) && (sc->last_stat16 & STAT16_ROOF))
framer_write(sc, T3CSR_CTL1, ctl1 |= CTL1_XTX);
2005-12-06 06:01:45 +03:00
/* Start sending AIS, Alarm Indication Signal */
2006-02-12 02:00:06 +03:00
if ((stat16 & STAT16_RLOS) && !(sc->last_stat16 & STAT16_RLOS))
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_FRAME);
framer_write(sc, T3CSR_CTL1, ctl1 | CTL1_TXAIS);
2005-12-06 06:01:45 +03:00
}
/* Stop sending AIS, Alarm Indication Signal */
2006-02-12 02:00:06 +03:00
else if (!(stat16 & STAT16_RLOS) && (sc->last_stat16 & STAT16_RLOS))
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_DS3_FRAME);
framer_write(sc, T3CSR_CTL1, ctl1 & ~CTL1_TXAIS);
2005-12-06 06:01:45 +03:00
}
/* Time out loopback requests. */
2006-02-12 02:00:06 +03:00
if (sc->loop_timer)
2005-12-06 06:01:45 +03:00
if (--sc->loop_timer == 0)
2006-02-12 02:00:06 +03:00
if (mii16 & MII16_DS3_LNLBK)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Timeout: Loop Down after 300 seconds\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_DS3_LNLBK); /* line loopback off */
2005-12-06 06:01:45 +03:00
}
/* 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;
2006-02-12 02:00:06 +03:00
if (!(ctl1 & CTL1_XTX)) sc->status.snmp.t3.line |= TLINE_TX_RAI;
2005-12-06 06:01:45 +03:00
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 */
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static void /* context: process */
2005-12-06 06:01:45 +03:00
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. */
2006-02-12 02:00:06 +03:00
tx_feac = framer_read(sc, T3CSR_TX_FEAC);
2005-12-06 06:01:45 +03:00
/* Load second FEAC code and stop FEAC transmitter. */
2006-02-12 02:00:06 +03:00
framer_write(sc, T3CSR_TX_FEAC, CTL5_EMODE + feac2);
2005-12-06 06:01:45 +03:00
/* 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. */
2006-02-12 02:00:06 +03:00
framer_write(sc, T3CSR_DBL_FEAC, CTL13_DFEXEC + feac1);
2005-12-06 06:01:45 +03:00
/* Wait for double FEAC sequence to complete -- about 70 ms. */
for (i=0; i<10; i++) /* max delay 100 ms */
2006-02-12 02:00:06 +03:00
if (framer_read(sc, T3CSR_DBL_FEAC) & CTL13_DFEXEC) SLEEP(10000);
2006-04-20 20:51:57 +04:00
/* Flush received FEACS; do not respond to our own loop cmd! */
2006-02-12 02:00:06 +03:00
while (framer_read(sc, T3CSR_FEAC_STK) & FEAC_STK_VALID) DELAY(1);
2005-12-06 06:01:45 +03:00
/* Restore previous state of the FEAC transmitter. */
/* If it was sending a continous FEAC, it will resume. */
2006-02-12 02:00:06 +03:00
framer_write(sc, T3CSR_TX_FEAC, tx_feac);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* context: process */
2005-12-06 06:01:45 +03:00
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)
{
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_DS3_FRAME);
mii16_clr_bits(sc, MII16_DS3_TRLBK);
mii16_clr_bits(sc, MII16_DS3_LNLBK);
framer_write(sc, T3CSR_CTL1,
framer_read(sc, T3CSR_CTL1) & ~CTL1_3LOOP);
framer_write(sc, T3CSR_CTL12,
framer_read(sc, T3CSR_CTL12) & ~(CTL12_RTPLOOP | CTL12_RTPLLEN));
}
2005-12-06 06:01:45 +03:00
else if (ioctl->data == CFG_LOOP_LINE)
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_LNLBK);
2005-12-06 06:01:45 +03:00
else if (ioctl->data == CFG_LOOP_OTHER)
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_TRLBK);
2005-12-06 06:01:45 +03:00
else if (ioctl->data == CFG_LOOP_INWARD)
2006-02-12 02:00:06 +03:00
framer_write(sc, T3CSR_CTL1,
framer_read(sc, T3CSR_CTL1) | CTL1_3LOOP);
2005-12-06 06:01:45 +03:00
else if (ioctl->data == CFG_LOOP_DUAL)
{
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_LNLBK);
framer_write(sc, T3CSR_CTL1,
framer_read(sc, T3CSR_CTL1) | CTL1_3LOOP);
}
2005-12-06 06:01:45 +03:00
else if (ioctl->data == CFG_LOOP_PAYLOAD)
{
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_FRAME);
framer_write(sc, T3CSR_CTL12,
framer_read(sc, T3CSR_CTL12) | CTL12_RTPLOOP);
framer_write(sc, T3CSR_CTL12,
framer_read(sc, T3CSR_CTL12) | CTL12_RTPLLEN);
2005-12-06 06:01:45 +03:00
DELAY(25); /* at least two frames (22 uS) */
2006-02-12 02:00:06 +03:00
framer_write(sc, T3CSR_CTL12,
framer_read(sc, T3CSR_CTL12) & ~CTL12_RTPLLEN);
}
2005-12-06 06:01:45 +03:00
else
error = EINVAL;
break;
}
2006-02-12 02:00:06 +03:00
case IOCTL_SET_STATUS:
{
#if 0
if (ioctl->data)
framer_write(sc, T3CSR_CTL1,
framer_read(sc, T3CSR_CTL1) & ~CTL1_TXIDL);
else /* off */
framer_write(sc, T3CSR_CTL1,
framer_read(sc, T3CSR_CTL1) | CTL1_TXIDL);
#endif
break;
}
2005-12-06 06:01:45 +03:00
default:
error = EINVAL;
break;
}
2005-12-06 06:01:45 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
/* Must not sleep. */
static void
2006-02-12 02:00:06 +03:00
t3_attach(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
int i;
u_int8_t ctl1;
if (config == NULL) /* startup config */
{
2006-04-20 20:51:57 +04:00
sc->status.card_type = CSID_LMC_T3;
2005-12-06 06:01:45 +03:00
sc->config.crc_len = CFG_CRC_16;
sc->config.loop_back = CFG_LOOP_NONE;
2006-02-12 02:00:06 +03:00
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. */
dac_write(sc, 0x9002); /* set Vref = 2.048 volts */
dac_write(sc, 2048); /* range is 0..4095 */
}
else if (config != &sc->config) /* change config */
{
if ((sc->config.crc_len == config->crc_len) &&
(sc->config.loop_back == config->loop_back) &&
(sc->config.format == config->format) &&
(sc->config.cable_len == config->cable_len) &&
(sc->config.scrambler == config->scrambler) &&
(sc->config.tx_clk_src == config->tx_clk_src))
return; /* nothing changed */
sc->config.crc_len = config->crc_len;
sc->config.loop_back = config->loop_back;
sc->config.format = config->format;
sc->config.cable_len = config->cable_len;
sc->config.scrambler = config->scrambler;
sc->config.tx_clk_src = config->tx_clk_src;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Set cable length. */
if (sc->config.cable_len > 30)
mii16_clr_bits(sc, MII16_DS3_ZERO);
else
mii16_set_bits(sc, MII16_DS3_ZERO);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Set payload scrambler polynomial. */
if (sc->config.scrambler == CFG_SCRAM_LARS)
mii16_set_bits(sc, MII16_DS3_POLY);
else
mii16_clr_bits(sc, MII16_DS3_POLY);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Set payload scrambler on/off. */
if (sc->config.scrambler == CFG_SCRAM_OFF)
mii16_clr_bits(sc, MII16_DS3_SCRAM);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_SCRAM);
2005-12-06 06:01:45 +03:00
/* Set CRC length. */
if (sc->config.crc_len == CFG_CRC_32)
2006-02-12 02:00:06 +03:00
mii16_set_bits(sc, MII16_DS3_CRC32);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_DS3_CRC32);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Loopback towards host thru the line interface. */
if (sc->config.loop_back == CFG_LOOP_OTHER)
mii16_set_bits(sc, MII16_DS3_TRLBK);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_DS3_TRLBK);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Loopback towards network thru the line interface. */
if (sc->config.loop_back == CFG_LOOP_LINE)
mii16_set_bits(sc, MII16_DS3_LNLBK);
else if (sc->config.loop_back == CFG_LOOP_DUAL)
mii16_set_bits(sc, MII16_DS3_LNLBK);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_DS3_LNLBK);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Configure T3 framer chip; write EVERY writeable register. */
ctl1 = CTL1_SER | CTL1_XTX;
if (sc->config.format == CFG_FORMAT_T3M13) ctl1 |= CTL1_M13MODE;
if (sc->config.loop_back == CFG_LOOP_INWARD) ctl1 |= CTL1_3LOOP;
if (sc->config.loop_back == CFG_LOOP_DUAL) ctl1 |= CTL1_3LOOP;
framer_write(sc, T3CSR_CTL1, ctl1);
framer_write(sc, T3CSR_TX_FEAC, CTL5_EMODE);
framer_write(sc, T3CSR_CTL8, CTL8_FBEC);
framer_write(sc, T3CSR_CTL12, CTL12_DLCB1 | CTL12_C21 | CTL12_MCB1);
framer_write(sc, T3CSR_DBL_FEAC, 0);
framer_write(sc, T3CSR_CTL14, CTL14_RGCEN | CTL14_TGCEN);
framer_write(sc, T3CSR_INTEN, 0);
framer_write(sc, T3CSR_CTL20, CTL20_CVEN);
/* Clear error counters and latched error bits */
/* that may have happened while initializing. */
for (i=0; i<21; i++) framer_read(sc, i);
}
static void
t3_detach(softc_t *sc)
{
framer_write(sc, T3CSR_CTL1,
framer_read(sc, T3CSR_CTL1) | CTL1_TXIDL);
led_on(sc, MII16_LED_ALL);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* End DS3 card code */
/* Begin SSI card code */
static struct card ssi_card =
{
.ident = ssi_ident,
.watchdog = ssi_watchdog,
.ioctl = ssi_ioctl,
.attach = ssi_attach,
.detach = ssi_detach,
};
2005-12-06 06:01:45 +03:00
static void
ssi_ident(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
printf(", LTC1343/44");
}
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
2005-12-06 06:01:45 +03:00
ssi_watchdog(softc_t *sc)
{
u_int16_t cable;
2006-02-12 02:00:06 +03:00
u_int16_t mii16 = mii_read(sc, 16) & MII16_SSI_MODEM;
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
/* 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);
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_DOWN;
2005-12-06 06:01:45 +03:00
}
else
led_off(sc, MII16_SSI_LED_UR);
/* Check the external cable. */
2006-02-12 02:00:06 +03:00
cable = mii_read(sc, 17);
2005-12-06 06:01:45 +03:00
cable = cable & MII17_SSI_CABLE_MASK;
cable = cable >> MII17_SSI_CABLE_SHIFT;
if (cable == 7)
{
led_off(sc, MII16_SSI_LED_LL); /* no cable */
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_DOWN;
2005-12-06 06:01:45 +03:00
}
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 */
2006-02-12 02:00:06 +03:00
if (((sc->config.dte_dce == CFG_DTE) && !(mii16 & MII16_SSI_DCD)) ||
((sc->config.dte_dce == CFG_DCE) && !(mii16 & MII16_SSI_DSR)))
2005-12-06 06:01:45 +03:00
{
led_off(sc, MII16_SSI_LED_LR);
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_DOWN;
2005-12-06 06:01:45 +03:00
}
else
led_on(sc, MII16_SSI_LED_LR);
2006-02-12 02:00:06 +03:00
if (sc->config.debug && (cable != sc->status.cable_type))
2005-12-06 06:01:45 +03:00
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. */
2006-02-12 02:00:06 +03:00
if ((sc->config.debug) && (mii16 != sc->last_mii16))
2005-12-06 06:01:45 +03:00
{
const 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);
}
2005-12-06 06:01:45 +03:00
/* SNMP one-second report */
sc->status.snmp.ssi.sigs = mii16 & MII16_SSI_MODEM;
2005-12-06 06:01:45 +03:00
/* Remember this state until next time. */
sc->last_mii16 = mii16;
2005-12-06 06:01:45 +03:00
/* If a loop back is in effect, link status is UP */
if (sc->config.loop_back != CFG_LOOP_NONE)
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* context: process */
2005-12-06 06:01:45 +03:00
ssi_ioctl(softc_t *sc, struct ioctl *ioctl)
{
int error = 0;
if (ioctl->cmd == IOCTL_SNMP_SIGS)
{
2006-02-12 02:00:06 +03:00
u_int16_t mii16 = mii_read(sc, 16);
2005-12-06 06:01:45 +03:00
mii16 &= ~MII16_SSI_MODEM;
mii16 |= (MII16_SSI_MODEM & ioctl->data);
2006-02-12 02:00:06 +03:00
mii_write(sc, 16, mii16);
2005-12-06 06:01:45 +03:00
}
else if (ioctl->cmd == IOCTL_SET_STATUS)
{
2006-02-12 02:00:06 +03:00
if (ioctl->data)
mii16_set_bits(sc, (MII16_SSI_DTR | MII16_SSI_RTS | MII16_SSI_DCD));
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, (MII16_SSI_DTR | MII16_SSI_RTS | MII16_SSI_DCD));
2005-12-06 06:01:45 +03:00
}
else
error = EINVAL;
2005-12-06 06:01:45 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
/* Must not sleep. */
static void
2006-02-12 02:00:06 +03:00
ssi_attach(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
if (config == NULL) /* startup config */
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
sc->status.card_type = CSID_LMC_SSI;
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else if (config != &sc->config) /* change config */
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
u_int32_t *old_synth = (u_int32_t *)&sc->config.synth;
u_int32_t *new_synth = (u_int32_t *)&config->synth;
if ((sc->config.crc_len == config->crc_len) &&
(sc->config.loop_back == config->loop_back) &&
(sc->config.tx_clk_src == config->tx_clk_src) &&
(sc->config.dte_dce == config->dte_dce) &&
(*old_synth == *new_synth))
return; /* nothing changed */
sc->config.crc_len = config->crc_len;
sc->config.loop_back = config->loop_back;
sc->config.tx_clk_src = config->tx_clk_src;
sc->config.dte_dce = config->dte_dce;
*old_synth = *new_synth;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Disable the TX clock driver while programming the oscillator. */
gpio_clr_bits(sc, GPIO_SSI_DCE);
gpio_make_output(sc, GPIO_SSI_DCE);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Program the synthesized oscillator. */
synth_write(sc, &sc->config.synth);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Set DTE/DCE mode. */
/* If DTE mode then DCD & TXC are received. */
/* If DCE mode then DCD & TXC are driven. */
2006-04-20 20:51:57 +04:00
/* Boards with MII rev=4.0 do not drive DCD. */
2006-02-12 02:00:06 +03:00
if (sc->config.dte_dce == CFG_DCE)
gpio_set_bits(sc, GPIO_SSI_DCE);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
gpio_clr_bits(sc, GPIO_SSI_DCE);
gpio_make_output(sc, GPIO_SSI_DCE);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Set CRC length. */
if (sc->config.crc_len == CFG_CRC_32)
mii16_set_bits(sc, MII16_SSI_CRC32);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_SSI_CRC32);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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)
mii16_set_bits(sc, MII16_SSI_LOOP);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_SSI_LOOP);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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)
mii16_set_bits(sc, MII16_SSI_LL);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_SSI_LL);
2006-02-12 02:00:06 +03:00
/* Assert pin RL in modem conn: ask modem for remote loop. */
if (sc->config.loop_back == CFG_LOOP_RL)
mii16_set_bits(sc, MII16_SSI_RL);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
mii16_clr_bits(sc, MII16_SSI_RL);
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
static void
ssi_detach(softc_t *sc)
{
mii16_clr_bits(sc, (MII16_SSI_DTR | MII16_SSI_RTS | MII16_SSI_DCD));
led_on(sc, MII16_LED_ALL);
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* End SSI card code */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Begin T1E1 card code */
2006-02-12 02:00:06 +03:00
static struct card t1_card =
{
.ident = t1_ident,
.watchdog = t1_watchdog,
.ioctl = t1_ioctl,
.attach = t1_attach,
.detach = t1_detach,
};
2005-12-06 06:01:45 +03:00
static void
t1_ident(softc_t *sc)
{
printf(", Bt837%x rev %x",
2006-02-12 02:00:06 +03:00
framer_read(sc, Bt8370_DID)>>4,
framer_read(sc, Bt8370_DID)&0x0F);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
2005-12-06 06:01:45 +03:00
t1_watchdog(softc_t *sc)
{
u_int16_t LCV = 0, FERR = 0, CRC = 0, FEBE = 0;
u_int8_t alm1, alm3, loop, isr0;
int i;
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
/* Read the alarm registers */
2006-02-12 02:00:06 +03:00
alm1 = framer_read(sc, Bt8370_ALM1);
alm3 = framer_read(sc, Bt8370_ALM3);
loop = framer_read(sc, Bt8370_LOOP);
isr0 = framer_read(sc, Bt8370_ISR0);
2005-12-06 06:01:45 +03:00
/* 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). */
2006-02-12 02:00:06 +03:00
if (alm1 & ALM1_RAIS) /* receiving ais */
2005-12-06 06:01:45 +03:00
led_on(sc, MII16_T1_LED_BLU);
2006-02-12 02:00:06 +03:00
else if (alm1 & ALM1_RLOS) /* sending ais */
2005-12-06 06:01:45 +03:00
led_inv(sc, MII16_T1_LED_BLU);
else
led_off(sc, MII16_T1_LED_BLU);
/* Receiving Remote Alarm Indication (RAI). */
2006-02-12 02:00:06 +03:00
if (alm1 & (ALM1_RMYEL | ALM1_RYEL)) /* receiving rai */
2005-12-06 06:01:45 +03:00
led_on(sc, MII16_T1_LED_YEL);
2006-02-12 02:00:06 +03:00
else if (alm1 & ALM1_RLOF) /* sending rai */
2005-12-06 06:01:45 +03:00
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. */
2006-02-12 02:00:06 +03:00
if (alm1) sc->status.link_state = STATE_DOWN;
2005-12-06 06:01:45 +03:00
/* Declare local Red Alarm if the link is down. */
2006-02-12 02:00:06 +03:00
if (sc->status.link_state == STATE_DOWN)
2005-12-06 06:01:45 +03:00
led_on(sc, MII16_T1_LED_RED);
2006-02-12 02:00:06 +03:00
else if (sc->loop_timer) /* loopback is active */
2005-12-06 06:01:45 +03:00
led_inv(sc, MII16_T1_LED_RED);
else
led_off(sc, MII16_T1_LED_RED);
/* Print latched error bits if they changed. */
2006-02-12 02:00:06 +03:00
if ((sc->config.debug) && (alm1 != sc->last_alm1))
2005-12-06 06:01:45 +03:00
{
const 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. */
2006-02-12 02:00:06 +03:00
LCV = framer_read(sc, Bt8370_LCV_LO) +
(framer_read(sc, Bt8370_LCV_HI)<<8);
2005-12-06 06:01:45 +03:00
if (!FORMAT_E1NONE)
2006-02-12 02:00:06 +03:00
FERR = framer_read(sc, Bt8370_FERR_LO) +
(framer_read(sc, Bt8370_FERR_HI)<<8);
2005-12-06 06:01:45 +03:00
if (FORMAT_E1CRC || FORMAT_T1ESF)
2006-02-12 02:00:06 +03:00
CRC = framer_read(sc, Bt8370_CRC_LO) +
(framer_read(sc, Bt8370_CRC_HI)<<8);
2005-12-06 06:01:45 +03:00
if (FORMAT_E1CRC)
2006-02-12 02:00:06 +03:00
FEBE = framer_read(sc, Bt8370_FEBE_LO) +
(framer_read(sc, Bt8370_FEBE_HI)<<8);
2005-12-06 06:01:45 +03:00
/* Only LCV is valid if Out-Of-Frame */
if (FORMAT_E1NONE) FERR = CRC = FEBE = 0;
2006-02-12 02:00:06 +03:00
if ((sc->config.debug) && (LCV || FERR || CRC || FEBE))
2005-12-06 06:01:45 +03:00
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. */
2006-02-12 02:00:06 +03:00
if ((FORMAT_T1ESF) && (framer_read(sc, Bt8370_ISR1) & 0x80))
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
u_int8_t bop_code = framer_read(sc, Bt8370_RBOP) & 0x3F;
2005-12-06 06:01:45 +03:00
switch (bop_code)
{
case T1BOP_OOF:
{
2006-02-12 02:00:06 +03:00
if ((sc->config.debug) && !(sc->last_alm1 & ALM1_RMYEL))
2005-12-06 06:01:45 +03:00
printf("%s: Receiving a 'yellow alarm' BOP msg\n", NAME_UNIT);
break;
}
case T1BOP_LINE_UP:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'line loopback activate' BOP msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP, LOOP_LINE);
2005-12-06 06:01:45 +03:00
sc->loop_timer = 305;
break;
}
case T1BOP_LINE_DOWN:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'line loopback deactivate' BOP msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP,
framer_read(sc, Bt8370_LOOP) & ~LOOP_LINE);
2005-12-06 06:01:45 +03:00
sc->loop_timer = 0;
break;
}
case T1BOP_PAY_UP:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'payload loopback activate' BOP msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP, LOOP_PAYLOAD);
2005-12-06 06:01:45 +03:00
sc->loop_timer = 305;
break;
}
case T1BOP_PAY_DOWN:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'payload loopback deactivate' BOP msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP,
framer_read(sc, Bt8370_LOOP) & ~LOOP_PAYLOAD);
2005-12-06 06:01:45 +03:00
sc->loop_timer = 0;
break;
}
default:
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a type 0x%02X BOP msg\n", NAME_UNIT, bop_code);
break;
}
}
}
2005-12-06 06:01:45 +03:00
/* Check for HDLC pkts in the ESF Facility Data Link. */
2006-02-12 02:00:06 +03:00
if ((FORMAT_T1ESF) && (framer_read(sc, Bt8370_ISR2) & 0x70))
2005-12-06 06:01:45 +03:00
{
/* while (not fifo-empty && not start-of-msg) flush fifo */
2006-02-12 02:00:06 +03:00
while ((framer_read(sc, Bt8370_RDL1_STAT) & 0x0C)==0)
framer_read(sc, Bt8370_RDL1);
2005-12-06 06:01:45 +03:00
/* If (not fifo-empty), then begin processing fifo contents. */
2006-02-12 02:00:06 +03:00
if ((framer_read(sc, Bt8370_RDL1_STAT) & 0x0C) == 0x08)
2005-12-06 06:01:45 +03:00
{
u_int8_t msg[64];
2006-02-12 02:00:06 +03:00
u_int8_t stat = framer_read(sc, Bt8370_RDL1);
2005-12-06 06:01:45 +03:00
sc->status.cntrs.fdl_pkts++;
for (i=0; i<(stat & 0x3F); i++)
2006-02-12 02:00:06 +03:00
msg[i] = framer_read(sc, Bt8370_RDL1);
2005-12-06 06:01:45 +03:00
/* 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);
}
}
2005-12-06 06:01:45 +03:00
/* Check for inband loop up/down commands. */
if (FORMAT_T1ANY)
{
2006-02-12 02:00:06 +03:00
u_int8_t isr6 = framer_read(sc, Bt8370_ISR6);
u_int8_t alarm2 = framer_read(sc, Bt8370_ALM2);
u_int8_t tlb = framer_read(sc, Bt8370_TLB);
2005-12-06 06:01:45 +03:00
/* Inband Code == Loop Up && On Transition && Inband Tx Inactive */
2006-02-12 02:00:06 +03:00
if ((isr6 & 0x40) && (alarm2 & 0x40) && !(tlb & 1))
2005-12-06 06:01:45 +03:00
{ /* CSU loop up is 10000 10000 ... */
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'CSU Loop Up' inband msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP, LOOP_LINE); /* Loop up */
2005-12-06 06:01:45 +03:00
sc->loop_timer = 305;
}
/* Inband Code == Loop Down && On Transition && Inband Tx Inactive */
2006-02-12 02:00:06 +03:00
if ((isr6 & 0x80) && (alarm2 & 0x80) && !(tlb & 1))
2005-12-06 06:01:45 +03:00
{ /* CSU loop down is 100 100 100 ... */
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Received a 'CSU Loop Down' inband msg\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP,
framer_read(sc, Bt8370_LOOP) & ~LOOP_LINE); /* loop down */
2005-12-06 06:01:45 +03:00
sc->loop_timer = 0;
}
}
/* Manually send Yellow Alarm BOP msgs. */
if (FORMAT_T1ESF)
{
2006-02-12 02:00:06 +03:00
u_int8_t isr7 = framer_read(sc, Bt8370_ISR7);
2005-12-06 06:01:45 +03:00
if ((isr7 & 0x02) && (alm1 & 0x02)) /* RLOF on-transition */
{ /* Start sending continuous Yellow Alarm BOP messages. */
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_BOP, RBOP_25 | TBOP_CONT);
framer_write(sc, Bt8370_TBOP, 0x00); /* send BOP; order matters */
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else if ((isr7 & 0x02) && !(alm1 & 0x02)) /* RLOF off-transition */
2005-12-06 06:01:45 +03:00
{ /* Stop sending continuous Yellow Alarm BOP messages. */
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_BOP, RBOP_25 | TBOP_OFF);
2005-12-06 06:01:45 +03:00
}
}
/* Time out loopback requests. */
2006-02-12 02:00:06 +03:00
if (sc->loop_timer)
2005-12-06 06:01:45 +03:00
if (--sc->loop_timer == 0)
2006-02-12 02:00:06 +03:00
if (loop)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2005-12-06 06:01:45 +03:00
printf("%s: Timeout: Loop Down after 300 seconds\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP, loop & ~(LOOP_PAYLOAD | LOOP_LINE));
2005-12-06 06:01:45 +03:00
}
/* RX Test Pattern status */
2006-02-12 02:00:06 +03:00
if ((sc->config.debug) && (isr0 & 0x10))
2005-12-06 06:01:45 +03:00
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 */
2006-02-12 02:00:06 +03:00
sc->status.link_state = STATE_UP;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static void /* context: process */
2005-12-06 06:01:45 +03:00
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. */
2006-02-12 02:00:06 +03:00
bop = framer_read(sc, Bt8370_BOP);
framer_write(sc, Bt8370_BOP, RBOP_OFF | TBOP_OFF);
2005-12-06 06:01:45 +03:00
for (i=0; i<40; i++) /* max delay 400 ms. */
2006-02-12 02:00:06 +03:00
if (framer_read(sc, Bt8370_BOP_STAT) & 0x80) SLEEP(10000);
2005-12-06 06:01:45 +03:00
/* send 25 repetitions of bop_code */
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_BOP, RBOP_OFF | TBOP_25);
framer_write(sc, Bt8370_TBOP, bop_code); /* order matters */
2005-12-06 06:01:45 +03:00
/* wait for tx to stop */
for (i=0; i<40; i++) /* max delay 400 ms. */
2006-02-12 02:00:06 +03:00
if (framer_read(sc, Bt8370_BOP_STAT) & 0x80) SLEEP(10000);
2005-12-06 06:01:45 +03:00
/* Restore previous state of the BOP machine. */
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_BOP, bop);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* context: process */
2005-12-06 06:01:45 +03:00
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:
{
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_TPATT, 0x00); /* tx pattern generator off */
framer_write(sc, Bt8370_RPATT, 0x00); /* rx pattern detector off */
framer_write(sc, Bt8370_TLB, 0x00); /* tx inband generator off */
2005-12-06 06:01:45 +03:00
break;
}
case TSEND_LINE:
{
if (FORMAT_T1ESF)
t1_send_bop(sc, T1BOP_LINE_UP);
else if (FORMAT_T1SF)
{
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LBP, 0x08); /* 10000 10000 ... */
framer_write(sc, Bt8370_TLB, 0x05); /* 5 bits, framed, start */
2005-12-06 06:01:45 +03:00
}
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)
{
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LBP, 0x24); /* 100100 100100 ... */
framer_write(sc, Bt8370_TLB, 0x09); /* 6 bits, framed, start */
2005-12-06 06:01:45 +03:00
}
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:
{
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_TPATT, 0x1E); /* framed QRSS */
2005-12-06 06:01:45 +03:00
break;
}
default:
{
error = EINVAL;
break;
}
}
2005-12-06 06:01:45 +03:00
break;
}
case IOCTL_SNMP_LOOP: /* set opstatus = test? */
{
u_int8_t new_loop = 0;
2006-02-12 02:00:06 +03:00
if (ioctl->data == CFG_LOOP_NONE)
2005-12-06 06:01:45 +03:00
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;
2006-02-12 02:00:06 +03:00
if (!error)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_LOOP, new_loop);
2005-12-06 06:01:45 +03:00
sc->config.loop_back = ioctl->data;
}
break;
}
2006-02-12 02:00:06 +03:00
case IOCTL_SET_STATUS:
{
#if 0
if (ioctl->data)
mii16_set_bits(sc, MII16_T1_XOE);
else
mii16_clr_bits(sc, MII16_T1_XOE);
#endif
break;
}
2005-12-06 06:01:45 +03:00
default:
error = EINVAL;
break;
}
2005-12-06 06:01:45 +03:00
return error;
}
2006-02-12 02:00:06 +03:00
/* Must not sleep. */
static void
t1_attach(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
int i;
u_int8_t pulse, lbo, gain;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (config == NULL) /* startup config */
{
/* Disable transmitter output drivers. */
mii16_clr_bits(sc, MII16_T1_XOE);
/* Bt8370 occasionally powers up in a loopback mode. */
/* Data sheet says zero LOOP reg and do a sw-reset. */
framer_write(sc, Bt8370_LOOP, 0x00); /* no loopback */
framer_write(sc, Bt8370_CR0, 0x80); /* sw-reset */
for (i=0; i<10; i++) /* wait for sw-reset to clear; max 10 ms */
if (framer_read(sc, Bt8370_CR0) & 0x80) DELAY(1000);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
sc->status.card_type = CSID_LMC_T1E1;
2006-02-12 02:00:06 +03:00
sc->config.crc_len = CFG_CRC_16;
sc->config.loop_back = CFG_LOOP_NONE;
sc->config.tx_clk_src = CFG_CLKMUX_RT; /* loop timed */
#if 1 /* USA */ /* decide using time zone? */
sc->config.format = CFG_FORMAT_T1ESF;
#else /* REST OF PLANET */
sc->config.format = CFG_FORMAT_E1FASCRC;
#endif
sc->config.time_slots = 0xFFFFFFFF;
sc->config.cable_len = 10;
sc->config.tx_pulse = CFG_PULSE_AUTO;
sc->config.rx_gain_max = CFG_GAIN_AUTO;
sc->config.tx_lbo = CFG_LBO_AUTO;
}
else if (config != &sc->config) /* change config */
{
if ((sc->config.crc_len == config->crc_len) &&
(sc->config.loop_back == config->loop_back) &&
(sc->config.tx_clk_src == config->tx_clk_src) &&
(sc->config.format == config->format) &&
(sc->config.time_slots == config->time_slots) &&
(sc->config.cable_len == config->cable_len) &&
(sc->config.tx_pulse == config->tx_pulse) &&
(sc->config.rx_gain_max == config->rx_gain_max) &&
(sc->config.tx_lbo == config->tx_lbo))
return; /* nothing changed */
sc->config.crc_len = config->crc_len;
sc->config.loop_back = config->loop_back;
sc->config.tx_clk_src = config->tx_clk_src;
sc->config.format = config->format;
sc->config.cable_len = config->cable_len;
sc->config.time_slots = config->time_slots;
sc->config.tx_pulse = config->tx_pulse;
sc->config.rx_gain_max = config->rx_gain_max;
sc->config.tx_lbo = config->tx_lbo;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Set CRC length. */
if (sc->config.crc_len == CFG_CRC_32)
mii16_set_bits(sc, MII16_T1_CRC32);
else
mii16_clr_bits(sc, MII16_T1_CRC32);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Invert HDLC payload data in SF/AMI mode. */
/* HDLC stuff bits satisfy T1 pulse density. */
if (FORMAT_T1SF)
mii16_set_bits(sc, MII16_T1_INVERT);
else
mii16_clr_bits(sc, MII16_T1_INVERT);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Set the transmitter output impedance. */
if (FORMAT_E1ANY) mii16_set_bits(sc, MII16_T1_Z);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 001:CR0 -- Control Register 0 - T1/E1 and frame format */
framer_write(sc, Bt8370_CR0, sc->config.format);
2006-02-12 02:00:06 +03:00
/* 002:JAT_CR -- Jitter Attenuator Control Register */
if (sc->config.tx_clk_src == CFG_CLKMUX_RT) /* loop timing */
framer_write(sc, Bt8370_JAT_CR, 0xA3); /* JAT in RX path */
else
{ /* 64-bit elastic store; free-running JCLK and CLADO */
framer_write(sc, Bt8370_JAT_CR, 0x4B); /* assert jcenter */
framer_write(sc, Bt8370_JAT_CR, 0x43); /* release jcenter */
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* 00C-013:IERn -- Interrupt Enable Registers */
for (i=Bt8370_IER7; i<=Bt8370_IER0; i++)
framer_write(sc, i, 0); /* no interrupts; polled */
2006-02-12 02:00:06 +03:00
/* 014:LOOP -- loopbacks */
if (sc->config.loop_back == CFG_LOOP_PAYLOAD)
framer_write(sc, Bt8370_LOOP, LOOP_PAYLOAD);
else if (sc->config.loop_back == CFG_LOOP_LINE)
framer_write(sc, Bt8370_LOOP, LOOP_LINE);
else if (sc->config.loop_back == CFG_LOOP_OTHER)
framer_write(sc, Bt8370_LOOP, LOOP_ANALOG);
else if (sc->config.loop_back == CFG_LOOP_INWARD)
framer_write(sc, Bt8370_LOOP, LOOP_FRAMER);
else if (sc->config.loop_back == CFG_LOOP_DUAL)
framer_write(sc, Bt8370_LOOP, LOOP_DUAL);
else
framer_write(sc, Bt8370_LOOP, 0x00); /* no loopback */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 015:DL3_TS -- Data Link 3 */
framer_write(sc, Bt8370_DL3_TS, 0x00); /* disabled */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 018:PIO -- Programmable I/O */
framer_write(sc, Bt8370_PIO, 0xFF); /* all pins are outputs */
/* 019:POE -- Programmable Output Enable */
framer_write(sc, Bt8370_POE, 0x00); /* all outputs are enabled */
/* 01A;CMUX -- Clock Input Mux */
if (sc->config.tx_clk_src == CFG_CLKMUX_EXT)
framer_write(sc, Bt8370_CMUX, 0x0C); /* external timing */
else
framer_write(sc, Bt8370_CMUX, 0x0F); /* internal timing */
/* 020:LIU_CR -- Line Interface Unit Config Register */
framer_write(sc, Bt8370_LIU_CR, 0xC1); /* reset LIU, squelch */
/* 022:RLIU_CR -- RX Line Interface Unit Config Reg */
2006-04-20 20:51:57 +04:00
/* Errata sheet says do not use freeze-short, but we do anyway! */
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_RLIU_CR, 0xB1); /* AGC=2048, Long Eye */
/* Select Rx sensitivity based on cable length. */
if ((gain = sc->config.rx_gain_max) == CFG_GAIN_AUTO)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* 024:VGA_MAX -- Variable Gain Amplifier Max gain */
framer_write(sc, Bt8370_VGA_MAX, gain);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 028:PRE_EQ -- Pre Equalizer */
if (gain == CFG_GAIN_EXTEND)
framer_write(sc, Bt8370_PRE_EQ, 0xE6); /* ON; thresh 6 */
else
framer_write(sc, Bt8370_PRE_EQ, 0xA6); /* OFF; thresh 6 */
2006-02-12 02:00:06 +03:00
/* 038-03C:GAINn -- RX Equalizer gain thresholds */
framer_write(sc, Bt8370_GAIN0, 0x24);
framer_write(sc, Bt8370_GAIN1, 0x28);
framer_write(sc, Bt8370_GAIN2, 0x2C);
framer_write(sc, Bt8370_GAIN3, 0x30);
framer_write(sc, Bt8370_GAIN4, 0x34);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 040:RCR0 -- Receiver Control Register 0 */
if (FORMAT_T1ESF)
framer_write(sc, Bt8370_RCR0, 0x05); /* B8ZS, 2/5 FErrs */
else if (FORMAT_T1SF)
framer_write(sc, Bt8370_RCR0, 0x84); /* AMI, 2/5 FErrs */
else if (FORMAT_E1NONE)
framer_write(sc, Bt8370_RCR0, 0x41); /* HDB3, rabort */
else if (FORMAT_E1CRC)
framer_write(sc, Bt8370_RCR0, 0x09); /* HDB3, 3 FErrs or 915 CErrs */
else /* E1 no CRC */
framer_write(sc, Bt8370_RCR0, 0x19); /* HDB3, 3 FErrs */
2005-02-27 03:26:58 +03:00
2006-02-12 02:00:06 +03:00
/* 041:RPATT -- Receive Test Pattern configuration */
framer_write(sc, Bt8370_RPATT, 0x3E); /* looking for framed QRSS */
2006-02-12 02:00:06 +03:00
/* 042:RLB -- Receive Loop Back code detector config */
framer_write(sc, Bt8370_RLB, 0x09); /* 6 bits down; 5 bits up */
2006-02-12 02:00:06 +03:00
/* 043:LBA -- Loop Back Activate code */
framer_write(sc, Bt8370_LBA, 0x08); /* 10000 10000 10000 ... */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 044:LBD -- Loop Back Deactivate code */
framer_write(sc, Bt8370_LBD, 0x24); /* 100100 100100 100100 ... */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 045:RALM -- Receive Alarm signal configuration */
framer_write(sc, Bt8370_RALM, 0x0C); /* yel_intg rlof_intg */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 046:LATCH -- Alarm/Error/Counter Latch register */
framer_write(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)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
if (FORMAT_T1ANY)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
pulse = CFG_PULSE_T1DSX0;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
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)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
lbo = CFG_LBO_22DB;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else
lbo = 0;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* 068:TLIU_CR -- Transmit LIU Control Register */
framer_write(sc, Bt8370_TLIU_CR, (0x40 | (lbo & 0x30) | (pulse & 0x0E)));
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 070:TCR0 -- Transmit Framer Configuration */
framer_write(sc, Bt8370_TCR0, sc->config.format>>1);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 071:TCR1 -- Transmitter Configuration */
if (FORMAT_T1SF)
framer_write(sc, Bt8370_TCR1, 0x43); /* tabort, AMI PDV enforced */
else
framer_write(sc, Bt8370_TCR1, 0x41); /* tabort, B8ZS or HDB3 */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 072:TFRM -- Transmit Frame format MYEL YEL MF FE CRC FBIT */
if (sc->config.format == CFG_FORMAT_T1ESF)
framer_write(sc, Bt8370_TFRM, 0x0B); /* - YEL MF - CRC FBIT */
else if (sc->config.format == CFG_FORMAT_T1SF)
framer_write(sc, Bt8370_TFRM, 0x19); /* - YEL MF - - FBIT */
else if (sc->config.format == CFG_FORMAT_E1FAS)
framer_write(sc, Bt8370_TFRM, 0x11); /* - YEL - - - FBIT */
else if (sc->config.format == CFG_FORMAT_E1FASCRC)
framer_write(sc, Bt8370_TFRM, 0x1F); /* - YEL MF FE CRC FBIT */
else if (sc->config.format == CFG_FORMAT_E1FASCAS)
framer_write(sc, Bt8370_TFRM, 0x31); /* MYEL YEL - - - FBIT */
else if (sc->config.format == CFG_FORMAT_E1FASCRCCAS)
framer_write(sc, Bt8370_TFRM, 0x3F); /* MYEL YEL MF FE CRC FBIT */
else if (sc->config.format == CFG_FORMAT_E1NONE)
framer_write(sc, Bt8370_TFRM, 0x00); /* NO FRAMING BITS AT ALL! */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 073:TERROR -- Transmit Error Insert */
framer_write(sc, Bt8370_TERROR, 0x00); /* no errors, please! */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 074:TMAN -- Transmit Manual Sa-byte/FEBE configuration */
framer_write(sc, Bt8370_TMAN, 0x00); /* none */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 075:TALM -- Transmit Alarm Signal Configuration */
if (FORMAT_E1ANY)
framer_write(sc, Bt8370_TALM, 0x38); /* auto_myel auto_yel auto_ais */
else if (FORMAT_T1ANY)
framer_write(sc, Bt8370_TALM, 0x18); /* auto_yel auto_ais */
/* 076:TPATT -- Transmit Test Pattern Configuration */
framer_write(sc, Bt8370_TPATT, 0x00); /* disabled */
/* 077:TLB -- Transmit Inband Loopback Code Configuration */
framer_write(sc, Bt8370_TLB, 0x00); /* disabled */
/* 090:CLAD_CR -- Clack Rate Adapter Configuration */
if (FORMAT_T1ANY)
framer_write(sc, Bt8370_CLAD_CR, 0x06); /* loop filter gain 1/2^6 */
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_CLAD_CR, 0x08); /* loop filter gain 1/2^8 */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 091:CSEL -- CLAD frequency Select */
if (FORMAT_T1ANY)
framer_write(sc, Bt8370_CSEL, 0x55); /* 1544 kHz */
else
framer_write(sc, Bt8370_CSEL, 0x11); /* 2048 kHz */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 092:CPHASE -- CLAD Phase detector */
if (FORMAT_T1ANY)
framer_write(sc, Bt8370_CPHASE, 0x22); /* phase compare @ 386 kHz */
else
framer_write(sc, Bt8370_CPHASE, 0x00); /* phase compare @ 2048 kHz */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (FORMAT_T1ESF) /* BOP & PRM are enabled in T1ESF mode only. */
{
/* 0A0:BOP -- Bit Oriented Protocol messages */
framer_write(sc, Bt8370_BOP, RBOP_25 | TBOP_OFF);
/* 0A4:DL1_TS -- Data Link 1 Time Slot Enable */
framer_write(sc, Bt8370_DL1_TS, 0x40); /* FDL bits in odd frames */
/* 0A6:DL1_CTL -- Data Link 1 Control */
framer_write(sc, Bt8370_DL1_CTL, 0x03); /* FCS mode, TX on, RX on */
/* 0A7:RDL1_FFC -- Rx Data Link 1 Fifo Fill Control */
framer_write(sc, Bt8370_RDL1_FFC, 0x30); /* assert "near full" at 48 */
/* 0AA:PRM -- Performance Report Messages */
framer_write(sc, Bt8370_PRM, 0x80);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* 0D0:SBI_CR -- System Bus Interface Configuration Register */
if (FORMAT_T1ANY)
framer_write(sc, Bt8370_SBI_CR, 0x47); /* 1.544 with 24 TS +Fbits */
else
framer_write(sc, Bt8370_SBI_CR, 0x46); /* 2.048 with 32 TS */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 0D1:RSB_CR -- Receive System Bus Configuration Register */
/* Change RINDO & RFSYNC on falling edge of RSBCLKI. */
framer_write(sc, Bt8370_RSB_CR, 0x70);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 0D2,0D3:RSYNC_{TS,BIT} -- Receive frame Sync offset */
framer_write(sc, Bt8370_RSYNC_BIT, 0x00);
framer_write(sc, Bt8370_RSYNC_TS, 0x00);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 0D4:TSB_CR -- Transmit System Bus Configuration Register */
/* Change TINDO & TFSYNC on falling edge of TSBCLKI. */
framer_write(sc, Bt8370_TSB_CR, 0x30);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 0D5,0D6:TSYNC_{TS,BIT} -- Transmit frame Sync offset */
framer_write(sc, Bt8370_TSYNC_BIT, 0x00);
framer_write(sc, Bt8370_TSYNC_TS, 0x00);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* 0D7:RSIG_CR -- Receive SIGnalling Configuration Register */
2006-02-12 02:00:06 +03:00
framer_write(sc, Bt8370_RSIG_CR, 0x00);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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 (sc->status.time_slots = 0, i=0; i<32; i++)
{
/* 0E0-0FF:SBCn -- System Bus Per-Channel Control */
if (FORMAT_T1ANY && (i==0 || i>24))
framer_write(sc, Bt8370_SBCn +i, 0x00); /* not assigned in T1 mode */
else if (FORMAT_E1ANY && (i==0) && !FORMAT_E1NONE)
framer_write(sc, Bt8370_SBCn +i, 0x01); /* assigned, TS0 o/h bits */
else if (FORMAT_E1CAS && (i==16) && !FORMAT_E1NONE)
framer_write(sc, Bt8370_SBCn +i, 0x01); /* assigned, TS16 o/h bits */
else if ((sc->status.time_slots |= (sc->config.time_slots & (1<<i))))
framer_write(sc, Bt8370_SBCn +i, 0x0D); /* assigned, RINDO, TINDO */
else
framer_write(sc, Bt8370_SBCn +i, 0x01); /* assigned, idle */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 100-11F:TPCn -- Transmit Per-Channel Control */
if (FORMAT_E1CAS && (i==0))
framer_write(sc, Bt8370_TPCn +i, 0x30); /* tidle, sig=0000 (MAS) */
else if (FORMAT_E1CAS && (i==16))
framer_write(sc, Bt8370_TPCn +i, 0x3B); /* tidle, sig=1011 (XYXX) */
else if ((sc->config.time_slots & (1<<i)) == 0)
framer_write(sc, Bt8370_TPCn +i, 0x20); /* tidle: use TSLIP_LOn */
else
framer_write(sc, Bt8370_TPCn +i, 0x00); /* nothing special */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 140-15F:TSLIP_LOn -- Transmit PCM Slip Buffer */
framer_write(sc, Bt8370_TSLIP_LOn +i, 0x7F); /* idle chan data */
/* 180-19F:RPCn -- Receive Per-Channel Control */
framer_write(sc, Bt8370_RPCn +i, 0x00); /* nothing special */
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Enable transmitter output drivers. */
mii16_set_bits(sc, MII16_T1_XOE);
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
static void
t1_detach(softc_t *sc)
{
led_on(sc, MII16_LED_ALL);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* End T1E1 card code */
2006-04-20 20:51:57 +04:00
#if SYNC_PPP /* Linux */
2006-02-12 02:00:06 +03:00
2006-04-20 20:51:57 +04:00
static struct stack sync_ppp_stack =
2006-02-12 02:00:06 +03:00
{
2006-04-20 20:51:57 +04:00
.ioctl = sync_ppp_ioctl,
.type = sync_ppp_type,
.mtu = sync_ppp_mtu,
.watchdog = sync_ppp_watchdog,
.open = sync_ppp_open,
.attach = sync_ppp_attach,
.detach = sync_ppp_detach,
2006-02-12 02:00:06 +03:00
};
2006-04-20 20:51:57 +04:00
static int /* context: process */
sync_ppp_ioctl(softc_t *sc, struct ifreq *ifr, int cmd)
{
return sppp_do_ioctl(sc->netdev, ifr, cmd);
}
static int /* context: interrupt */
sync_ppp_type(softc_t *sc, struct sk_buff *skb)
{
return htons(ETH_P_WAN_PPP);
}
static int /* context: process */
sync_ppp_mtu(softc_t *sc, int mtu)
{
return ((mtu < 128) || (mtu > PPP_MTU)) ? -EINVAL : 0;
}
static void /* context: softirq */
sync_ppp_watchdog(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
/* Notice when the link comes up. */
if ((sc->last_link_state != STATE_UP) &&
(sc->status.link_state == STATE_UP))
sppp_reopen(sc->netdev);
/* Notice when the link goes down. */
if ((sc->last_link_state == STATE_UP) &&
(sc->status.link_state != STATE_UP))
sppp_close(sc->netdev);
/* Report current line protocol. */
2006-04-20 20:51:57 +04:00
sc->status.stack = STACK_SYNC_PPP;
2006-02-12 02:00:06 +03:00
if (sc->sppp->pp_flags & PP_CISCO)
2006-04-20 20:51:57 +04:00
sc->status.proto = PROTO_C_HDLC;
2005-12-06 06:01:45 +03:00
else
2006-04-20 20:51:57 +04:00
sc->status.proto = PROTO_PPP;
2006-02-12 02:00:06 +03:00
/* Report keep-alive status. */
sc->status.keep_alive = sc->sppp->pp_flags & PP_KEEPALIVE;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* never fails */
sync_ppp_open(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
/* Refresh the keep_alive flag. */
if (config->keep_alive)
sc->sppp->pp_flags |= PP_KEEPALIVE;
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
sc->sppp->pp_flags &= ~PP_KEEPALIVE;
sc->config.keep_alive = config->keep_alive;
2006-04-20 20:51:57 +04:00
/* Done if proto is not changing. */
if (config->proto == sc->config.proto)
2006-02-12 02:00:06 +03:00
return 0;
/* Close */
sppp_close(sc->netdev);
/* Change line protocol. */
2006-04-20 20:51:57 +04:00
switch (config->proto)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
case PROTO_PPP:
2006-02-12 02:00:06 +03:00
sc->sppp->pp_flags &= ~PP_CISCO;
2006-04-20 20:51:57 +04:00
sc->netdev->type = ARPHRD_PPP;
sc->config.proto = PROTO_PPP;
2006-02-12 02:00:06 +03:00
break;
default:
2006-04-20 20:51:57 +04:00
case PROTO_C_HDLC:
2006-02-12 02:00:06 +03:00
sc->sppp->pp_flags |= PP_CISCO;
2006-04-20 20:51:57 +04:00
sc->netdev->type = ARPHRD_CISCO;
sc->config.proto = PROTO_C_HDLC;
2006-02-12 02:00:06 +03:00
break;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Open */
sppp_open(sc->netdev);
return 0;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* never fails */
sync_ppp_attach(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
sc->ppd = &sc->ppp_dev; /* struct ppp_device* */
sc->netdev->priv = &sc->ppd; /* struct ppp_device** */
sc->ppp_dev.dev = sc->netdev;
sc->sppp = &sc->ppp_dev.sppp;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
sppp_attach(&sc->ppp_dev);
sc->netdev->do_ioctl = netdev_ioctl;
config->keep_alive = 1;
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_SYNC_PPP;
sc->stack = &sync_ppp_stack;
2006-02-12 02:00:06 +03:00
return 0;
}
2006-04-20 20:51:57 +04:00
static int /* never fails */
sync_ppp_detach(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
sppp_close(sc->netdev);
sppp_detach(sc->netdev);
netdev_setup(sc->netdev);
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_NONE;
sc->config.proto = PROTO_NONE;
sc->stack = NULL;
2006-02-12 02:00:06 +03:00
return 0;
}
#endif /* SYNC_PPP */
2006-04-20 20:51:57 +04:00
#if GEN_HDLC /* Linux only */
2006-02-12 02:00:06 +03:00
2006-04-20 20:51:57 +04:00
static struct stack gen_hdlc_stack =
2006-02-12 02:00:06 +03:00
{
2006-04-20 20:51:57 +04:00
.ioctl = gen_hdlc_ioctl,
.type = gen_hdlc_type,
.mtu = gen_hdlc_mtu,
.watchdog = gen_hdlc_watchdog,
.open = gen_hdlc_open,
.attach = gen_hdlc_attach,
.detach = gen_hdlc_detach,
2006-02-12 02:00:06 +03:00
};
2006-04-20 20:51:57 +04:00
static int /* context: process */
gen_hdlc_ioctl(softc_t *sc, struct ifreq *ifr, int cmd)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
te1_settings settings;
2005-12-06 06:01:45 +03:00
int error = 0;
2006-02-12 02:00:06 +03:00
if (cmd == SIOCWANDEV)
switch (ifr->ifr_settings.type)
{
case IF_GET_IFACE: /* get interface config */
{
unsigned int size;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* NOTE: This assumes struct sync_serial_settings has the */
/* same layout as the first part of struct te1_settings. */
2006-04-20 20:51:57 +04:00
if (sc->status.card_type == CSID_LMC_T1E1)
2006-02-12 02:00:06 +03:00
{
if (FORMAT_T1ANY) ifr->ifr_settings.type = IF_IFACE_T1;
if (FORMAT_E1ANY) ifr->ifr_settings.type = IF_IFACE_E1;
size = sizeof(te1_settings);
}
else
{
ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
size = sizeof(sync_serial_settings);
}
if (ifr->ifr_settings.size < size)
{
ifr->ifr_settings.size = size;
return -ENOBUFS;
}
ifr->ifr_settings.size = size;
if (sc->config.tx_clk_src == CFG_CLKMUX_ST)
settings.clock_type = CLOCK_EXT;
if (sc->config.tx_clk_src == CFG_CLKMUX_INT)
settings.clock_type = CLOCK_TXINT;
if (sc->config.tx_clk_src == CFG_CLKMUX_RT)
settings.clock_type = CLOCK_TXFROMRX;
settings.loopback = (sc->config.loop_back != CFG_LOOP_NONE) ? 1:0;
settings.clock_rate = sc->status.tx_speed;
2006-04-20 20:51:57 +04:00
if (sc->status.card_type == CSID_LMC_T1E1)
2006-02-12 02:00:06 +03:00
settings.slot_map = sc->status.time_slots;
error = copy_to_user(ifr->ifr_settings.ifs_ifsu.te1,
&settings, size);
break;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
case IF_IFACE_SYNC_SERIAL: /* set interface config */
case IF_IFACE_T1:
case IF_IFACE_E1:
2005-12-06 06:01:45 +03:00
{
struct config config = sc->config;
2006-02-12 02:00:06 +03:00
if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (ifr->ifr_settings.size > sizeof(te1_settings))
return -ENOBUFS;
error = copy_from_user(&settings,
ifr->ifr_settings.ifs_ifsu.te1, sizeof(te1_settings));
if (settings.clock_type == CLOCK_EXT)
config.tx_clk_src = CFG_CLKMUX_ST;
else if (settings.clock_type == CLOCK_TXINT)
config.tx_clk_src = CFG_CLKMUX_INT;
else if (settings.clock_type == CLOCK_TXFROMRX)
config.tx_clk_src = CFG_CLKMUX_RT;
if (settings.loopback)
config.loop_back = CFG_LOOP_TULIP;
else
config.loop_back = CFG_LOOP_NONE;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
tulip_loop(sc, &config);
sc->card->attach(sc, &config);
break;
}
default: /* Pass the rest to the line pkg. */
{
error = hdlc_ioctl(sc->netdev, ifr, cmd);
break;
}
}
else
error = -EINVAL;
2005-12-06 06:01:45 +03:00
return error;
}
2006-04-20 20:51:57 +04:00
static int /* context: interrupt */
gen_hdlc_type(softc_t *sc, struct sk_buff *skb)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
return hdlc_type_trans(skb, sc->netdev);
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static int /* context: process */
gen_hdlc_mtu(softc_t *sc, int mtu)
2006-02-12 02:00:06 +03:00
{
return ((mtu < 68) || (mtu > HDLC_MAX_MTU)) ? -EINVAL : 0;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
gen_hdlc_watchdog(softc_t *sc)
{
/* Notice when the link comes up. */
if ((sc->last_link_state != STATE_UP) &&
(sc->status.link_state == STATE_UP))
hdlc_set_carrier(1, sc->netdev);
/* Notice when the link goes down. */
if ((sc->last_link_state == STATE_UP) &&
(sc->status.link_state != STATE_UP))
hdlc_set_carrier(0, sc->netdev);
/* Report current line protocol. */
sc->status.stack = STACK_GEN_HDLC;
switch (sc->hdlcdev->proto.id)
{
case IF_PROTO_PPP:
{
struct sppp* sppp = &sc->hdlcdev->state.ppp.pppdev.sppp;
sc->status.keep_alive = sppp->pp_flags & PP_KEEPALIVE;
sc->status.proto = PROTO_PPP;
break;
}
case IF_PROTO_CISCO:
sc->status.proto = PROTO_C_HDLC;
break;
case IF_PROTO_FR:
sc->status.proto = PROTO_FRM_RLY;
break;
case IF_PROTO_HDLC:
sc->status.proto = PROTO_IP_HDLC;
break;
case IF_PROTO_X25:
sc->status.proto = PROTO_X25;
break;
case IF_PROTO_HDLC_ETH:
sc->status.proto = PROTO_ETH_HDLC;
break;
default:
sc->status.proto = PROTO_NONE;
break;
}
}
2006-02-12 02:00:06 +03:00
static int
2006-04-20 20:51:57 +04:00
gen_hdlc_open(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
int error = 0;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Refresh the keep_alive flag. */
if (sc->hdlcdev->proto.id == IF_PROTO_PPP)
{
struct sppp* sppp = &sc->hdlcdev->state.ppp.pppdev.sppp;
if (config->keep_alive)
sppp->pp_flags |= PP_KEEPALIVE;
else
sppp->pp_flags &= ~PP_KEEPALIVE;
sc->config.keep_alive = config->keep_alive;
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Done if proto is not changing. */
if (config->proto == sc->config.proto)
2006-02-12 02:00:06 +03:00
return 0;
/* Close */
hdlc_close(sc->netdev);
/* Generic-HDLC gets protocol params using copy_from_user().
* This is a problem for a kernel-resident device driver.
2006-04-20 20:51:57 +04:00
* Luckily, PPP does not need any params so no copy_from_user().
2006-02-12 02:00:06 +03:00
*/
/* Change line protocol. */
2006-04-20 20:51:57 +04:00
if (config->proto == PROTO_PPP)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
struct ifreq ifr;
ifr.ifr_settings.size = 0;
ifr.ifr_settings.type = IF_PROTO_PPP;
hdlc_ioctl(sc->netdev, &ifr, SIOCWANDEV);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Changing to any protocol other than PPP */
/* requires using the 'sethdlc' program. */
/* Open */
if ((error = hdlc_open(sc->netdev)))
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
printk("%s: hdlc_open(): error %d\n", NAME_UNIT, error);
if (error == -ENOSYS)
printk("%s: Try 'sethdlc %s hdlc|ppp|cisco|fr'\n",
NAME_UNIT, NAME_UNIT);
2006-04-20 20:51:57 +04:00
sc->config.proto = PROTO_NONE;
2005-12-06 06:01:45 +03:00
}
else
2006-04-20 20:51:57 +04:00
sc->config.proto = config->proto;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static int /* never fails */
gen_hdlc_attach(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
sc->netdev->priv = sc->hdlcdev;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* hdlc_attach(sc->netdev); */
sc->netdev->mtu = HDLC_MAX_MTU;
sc->hdlcdev->attach = gen_hdlc_card_params;
sc->hdlcdev->xmit = netdev_start;
config->keep_alive = 1;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_GEN_HDLC;
sc->stack = &gen_hdlc_stack;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* never fails */
gen_hdlc_detach(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
hdlc_close(sc->netdev);
/* hdlc_detach(sc->netdev); */
hdlc_proto_detach(sc->hdlcdev);
2006-04-20 20:51:57 +04:00
memset(&sc->hdlcdev->proto, 0, sizeof sc->hdlcdev->proto);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
netdev_setup(sc->netdev);
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_NONE;
sc->config.proto = PROTO_NONE;
sc->stack = NULL;
2005-12-06 06:01:45 +03:00
return 0;
}
2006-02-12 02:00:06 +03:00
static int
gen_hdlc_card_params(struct net_device *netdev,
unsigned short encoding, unsigned short parity)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
softc_t *sc = NETDEV2SC(netdev);
struct config config = sc->config;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Encoding does not seem to apply to synchronous interfaces, */
2006-02-12 02:00:06 +03:00
/* but Parity seems to be generic-HDLC's name for CRC. */
if (parity == PARITY_CRC32_PR1_CCITT)
config.crc_len = CFG_CRC_32;
if (parity == PARITY_CRC16_PR1_CCITT)
config.crc_len = CFG_CRC_16;
sc->card->attach(sc, &config);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
#endif /* GEN_HDLC */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
#if P2P /* BSD/OS */
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static struct stack p2p_stack =
2006-02-12 02:00:06 +03:00
{
2006-04-20 20:51:57 +04:00
.ioctl = p2p_stack_ioctl,
.input = p2p_stack_input,
.output = p2p_stack_output,
.watchdog = p2p_stack_watchdog,
.open = p2p_stack_open,
.attach = p2p_stack_attach,
.detach = p2p_stack_detach,
2006-02-12 02:00:06 +03:00
};
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static int /* context: process */
p2p_stack_ioctl(softc_t *sc, u_long cmd, void *data)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
return p2p_ioctl(sc->ifp, cmd, data);
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static void /* context: interrupt */
p2p_stack_input(softc_t *sc, struct mbuf *mbuf)
2006-02-12 02:00:06 +03:00
{
struct mbuf *new_mbuf = mbuf;
while (new_mbuf)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
sc->p2p->p2p_hdrinput(sc->p2p, new_mbuf->m_data, new_mbuf->m_len);
new_mbuf = new_mbuf->m_next;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
sc->p2p->p2p_input(sc->p2p, NULL);
m_freem(mbuf);
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static void /* context: interrupt */
p2p_stack_output(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
if (!IFQ_IS_EMPTY(&sc->p2p->p2p_isnd))
IFQ_DEQUEUE(&sc->p2p->p2p_isnd, sc->tx_mbuf);
2005-12-06 06:01:45 +03:00
else
2006-02-12 02:00:06 +03:00
IFQ_DEQUEUE(&sc->ifp->if_snd, sc->tx_mbuf);
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
p2p_stack_watchdog(softc_t *sc)
{
/* Notice change in link status. */
if ((sc->last_link_state != sc->status.link_state) &&
/* if_slowtimo() can run before raw_init() has inited rawcb. */
(sc->p2p->p2p_modem != NULL) && (rawcb.rcb_next != NULL))
(*sc->p2p->p2p_modem)(sc->p2p, sc->status.link_state==STATE_UP);
/* Report current line protocol. */
sc->status.stack = STACK_P2P;
switch (sc->ifp->if_type)
{
case IFT_PPP:
sc->status.proto = PROTO_PPP;
break;
case IFT_PTPSERIAL:
sc->status.proto = PROTO_C_HDLC;
break;
case IFT_FRELAY:
sc->status.proto = PROTO_FRM_RLY;
break;
default:
sc->status.proto = PROTO_NONE;
break;
}
}
2006-02-12 02:00:06 +03:00
static int
2006-04-20 20:51:57 +04:00
p2p_stack_open(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
int error = 0;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Done if proto is not changing. */
if (config->proto == sc->config.proto)
2006-02-12 02:00:06 +03:00
return 0;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
if (error = p2p_stack_detach(sc))
2006-02-12 02:00:06 +03:00
return error;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Change line protocol. */
2006-04-20 20:51:57 +04:00
switch (config->proto)
2006-02-12 02:00:06 +03:00
{
2006-04-20 20:51:57 +04:00
case PROTO_PPP:
2006-02-12 02:00:06 +03:00
sc->ifp->if_type = IFT_PPP;
2006-04-20 20:51:57 +04:00
sc->config.proto = PROTO_PPP;
2006-02-12 02:00:06 +03:00
break;
2006-04-20 20:51:57 +04:00
case PROTO_C_HDLC:
2006-02-12 02:00:06 +03:00
sc->ifp->if_type = IFT_PTPSERIAL;
2006-04-20 20:51:57 +04:00
sc->config.proto = PROTO_C_HDLC;
2006-02-12 02:00:06 +03:00
break;
2006-04-20 20:51:57 +04:00
case PROTO_FRM_RLY:
2006-02-12 02:00:06 +03:00
sc->ifp->if_type = IFT_FRELAY;
2006-04-20 20:51:57 +04:00
sc->config.proto = PROTO_FRM_RLY;
2006-02-12 02:00:06 +03:00
break;
default:
2006-04-20 20:51:57 +04:00
case PROTO_NONE:
2006-02-12 02:00:06 +03:00
sc->ifp->if_type = IFT_NONE;
2006-04-20 20:51:57 +04:00
sc->config.proto = PROTO_NONE;
2006-02-12 02:00:06 +03:00
return 0;
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
error = p2p_stack_attach(sc, config);
2005-12-06 06:01:45 +03:00
return error;
}
2006-02-12 02:00:06 +03:00
static int
2006-04-20 20:51:57 +04:00
p2p_stack_attach(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
int error;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
sc->p2p = &sc->p2pcom;
sc->p2p->p2p_proto = 0; /* force p2p_attach to re-init */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if ((error = p2p_attach(sc->p2p))) /* calls bpfattach() */
{
if (sc->config.debug)
printf("%s: p2p_attach(): error %d\n", NAME_UNIT, error);
if (error == EPFNOSUPPORT)
printf("%s: Try 'ifconfig %s linktype ppp|frelay|chdlc'\n",
NAME_UNIT, NAME_UNIT);
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_NONE; /* not attached to P2P */
2006-02-12 02:00:06 +03:00
return error;
}
sc->p2p->p2p_mdmctl = p2p_mdmctl;
sc->p2p->p2p_getmdm = p2p_getmdm;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_P2P;
sc->stack = &p2p_stack;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
static int
2006-04-20 20:51:57 +04:00
p2p_stack_detach(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
int error = 0;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if ((error = p2p_detach(sc->p2p))) /* calls bfpdetach() */
{
if (sc->config.debug)
printf("%s: p2p_detach(): error %d\n", NAME_UNIT, error);
if (error == EBUSY)
printf("%s: Try 'ifconfig %s down -remove'\n",
NAME_UNIT, NAME_UNIT);
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_P2P; /* still attached to P2P */
2006-02-12 02:00:06 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
ifnet_setup(sc->ifp);
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_NONE;
sc->config.proto = PROTO_NONE;
sc->stack = NULL;
2006-02-12 02:00:06 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
/* Callout from P2P: */
/* Get the state of DCD (Data Carrier Detect). */
2006-04-20 20:51:57 +04:00
static int /* never fails */
p2p_getmdm(struct p2pcom *p2p, void *result)
2005-12-06 06:01:45 +03:00
{
softc_t *sc = IFP2SC(&p2p->p2p_if);
2006-04-20 20:51:57 +04:00
/* Non-zero is not good enough; TIOCM_CAR is 0x40. */
2006-02-12 02:00:06 +03:00
*(int *)result = (sc->status.link_state==STATE_UP) ? TIOCM_CAR : 0;
2005-12-06 06:01:45 +03:00
return 0;
}
/* Callout from P2P: */
/* Set the state of DTR (Data Terminal Ready). */
2006-04-20 20:51:57 +04:00
static int /* never fails */
2005-12-06 06:01:45 +03:00
p2p_mdmctl(struct p2pcom *p2p, int flag)
{
softc_t *sc = IFP2SC(&p2p->p2p_if);
2006-02-12 02:00:06 +03:00
set_ready(sc, flag);
2005-12-06 06:01:45 +03:00
return 0;
}
2006-02-12 02:00:06 +03:00
#endif /* P2P */
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
#if SPPP /* FreeBSD, NetBSD, OpenBSD */
2006-02-12 02:00:06 +03:00
2006-04-20 20:51:57 +04:00
static struct stack sppp_stack =
2006-02-12 02:00:06 +03:00
{
2006-04-20 20:51:57 +04:00
.ioctl = sppp_stack_ioctl,
.input = sppp_stack_input,
.output = sppp_stack_output,
.watchdog = sppp_stack_watchdog,
.open = sppp_stack_open,
.attach = sppp_stack_attach,
.detach = sppp_stack_detach,
2006-02-12 02:00:06 +03:00
};
2006-04-20 20:51:57 +04:00
# if !defined(PP_FR)
2006-02-12 02:00:06 +03:00
# define PP_FR 0
# endif
2006-04-20 20:51:57 +04:00
# if !defined(DLT_C_HDLC)
2006-02-12 02:00:06 +03:00
# define DLT_C_HDLC DLT_PPP
# endif
2006-04-20 20:51:57 +04:00
# if !defined(DLT_FRELAY)
2006-02-12 02:00:06 +03:00
# define DLT_FRELAY DLT_PPP
# endif
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static int /* context: process */
sppp_stack_ioctl(softc_t *sc, u_long cmd, void *data)
2006-04-20 20:51:57 +04:00
{
return sppp_ioctl(sc->ifp, cmd, data);
}
static void /* context: interrupt */
sppp_stack_input(softc_t *sc, struct mbuf *mbuf)
{
sppp_input(sc->ifp, mbuf);
}
static void /* context: interrupt */
sppp_stack_output(softc_t *sc)
{
sc->tx_mbuf = sppp_dequeue(sc->ifp);
}
static void /* context: softirq */
sppp_stack_watchdog(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
/* Notice when the link comes up. */
if ((sc->last_link_state != STATE_UP) &&
(sc->status.link_state == STATE_UP))
sppp_tls(sc->sppp);
/* Notice when the link goes down. */
if ((sc->last_link_state == STATE_UP) &&
(sc->status.link_state != STATE_UP))
sppp_tlf(sc->sppp);
/* Report current line protocol. */
2006-04-20 20:51:57 +04:00
sc->status.stack = STACK_SPPP;
2006-02-12 02:00:06 +03:00
if (sc->sppp->pp_flags & PP_CISCO)
2006-04-20 20:51:57 +04:00
sc->status.proto = PROTO_C_HDLC;
2006-02-12 02:00:06 +03:00
else
2006-04-20 20:51:57 +04:00
sc->status.proto = PROTO_PPP;
2006-02-12 02:00:06 +03:00
/* Report keep-alive status. */
sc->status.keep_alive = sc->sppp->pp_flags & PP_KEEPALIVE;
}
2006-04-20 20:51:57 +04:00
static int /* never fails */
sppp_stack_open(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
/* Refresh the keep_alive flag. */
if (config->keep_alive)
sc->sppp->pp_flags |= PP_KEEPALIVE;
else
sc->sppp->pp_flags &= ~PP_KEEPALIVE;
sc->config.keep_alive = config->keep_alive;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Done if proto is not changing. */
if (config->proto == sc->config.proto)
2006-02-12 02:00:06 +03:00
return 0;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Close */
sc->ifp->if_flags &= ~IFF_UP; /* down */
sppp_ioctl(sc->ifp, SIOCSIFFLAGS, NULL);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Change line protocol. */
2006-04-20 20:51:57 +04:00
LMC_BPF_DETACH(sc);
switch (config->proto)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
case PROTO_PPP:
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
sc->sppp->pp_flags &= ~PP_CISCO;
2006-04-20 20:51:57 +04:00
LMC_BPF_ATTACH(sc, DLT_PPP, 4);
sc->config.proto = PROTO_PPP;
2006-02-12 02:00:06 +03:00
break;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
default:
2006-04-20 20:51:57 +04:00
case PROTO_C_HDLC:
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
sc->sppp->pp_flags |= PP_CISCO;
2006-04-20 20:51:57 +04:00
LMC_BPF_ATTACH(sc, DLT_C_HDLC, 4);
sc->config.proto = PROTO_C_HDLC;
2006-02-12 02:00:06 +03:00
break;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
} /* switch(config->proto) */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Open */
sc->ifp->if_flags |= IFF_UP; /* up and not running */
sppp_ioctl(sc->ifp, SIOCSIFFLAGS, NULL);
return 0;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* never fails */
sppp_stack_attach(softc_t *sc, struct config *config)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
sc->sppp = &sc->spppcom;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
LMC_BPF_ATTACH(sc, DLT_RAW, 0);
2006-02-12 02:00:06 +03:00
sppp_attach(sc->ifp);
sc->sppp->pp_tls = sppp_tls;
sc->sppp->pp_tlf = sppp_tlf;
config->keep_alive = 1;
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_SPPP;
sc->stack = &sppp_stack;
2006-02-12 02:00:06 +03:00
2005-12-06 06:01:45 +03:00
return 0;
}
2006-04-20 20:51:57 +04:00
static int /* never fails */
sppp_stack_detach(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
sc->ifp->if_flags &= ~IFF_UP; /* down */
sppp_ioctl(sc->ifp, SIOCSIFFLAGS, NULL); /* close() */
sppp_detach(sc->ifp);
2006-04-20 20:51:57 +04:00
LMC_BPF_DETACH(sc);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
ifnet_setup(sc->ifp);
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_NONE;
sc->config.proto = PROTO_NONE;
sc->stack = NULL;
2005-12-06 06:01:45 +03:00
return 0;
}
2006-02-12 02:00:06 +03:00
/* Callout from SPPP: */
static void
sppp_tls(struct sppp *sppp)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
/* Calling pp_up/down() required by PPP mode in OpenBSD. */
/* Calling pp_up/down() panics PPP mode in NetBSD. */
/* Calling pp_up/down() breaks Cisco mode in FreeBSD. */
if (!(sppp->pp_flags & PP_CISCO)) /* not Cisco */
sppp->pp_up(sppp);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Callout from SPPP: */
static void
sppp_tlf(struct sppp *sppp)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
/* Calling pp_up/down() required by PPP mode in OpenBSD. */
/* Calling pp_up/down() panics PPP mode in NetBSD. */
/* Calling pp_up/down() breaks Cisco mode in FreeBSD. */
if (!(sppp->pp_flags & PP_CISCO)) /* not Cisco */
sppp->pp_down(sppp);
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
#endif /* SPPP */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* RawIP is built into the driver. */
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static struct stack rawip_stack =
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
#if IFNET
2006-04-20 20:51:57 +04:00
.ioctl = rawip_ioctl,
.input = rawip_input,
.output = rawip_output,
2006-02-12 02:00:06 +03:00
#elif NETDEV
2006-04-20 20:51:57 +04:00
.ioctl = rawip_ioctl,
.type = rawip_type,
.mtu = rawip_mtu,
2006-02-12 02:00:06 +03:00
#endif
2006-04-20 20:51:57 +04:00
.watchdog = rawip_watchdog,
.open = rawip_open,
.attach = rawip_attach,
.detach = rawip_detach,
2006-02-12 02:00:06 +03:00
};
#if IFNET
2006-04-20 20:51:57 +04:00
static int /* context: process */
rawip_ioctl(softc_t *sc, u_long cmd, void *data)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
struct ifreq *ifr = (struct ifreq *) data;
int error = 0;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
switch (cmd)
{
case SIOCADDMULTI:
case SIOCDELMULTI:
if (sc->config.debug)
printf("%s: rawip_ioctl: SIOCADD/DELMULTI\n", NAME_UNIT);
case SIOCSIFFLAGS:
*** Summary *** When a link-layer address changes (e.g., ifconfig ex0 link 02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor Advertisement to update the network-/link-layer address bindings on our LAN peers. Refuse a change of ethernet address to the address 00:00:00:00:00:00 or to any multicast/broadcast address. (Thanks matt@.) Reorder ifnet ioctl operations so that driver ioctls may inherit the functions of their "class"---ether_ioctl(), fddi_ioctl(), et cetera---and the class ioctls may inherit from the generic ioctl, ifioctl_common(), but both driver- and class-ioctls may override the generic behavior. Make network drivers share more code. Distinguish a "factory" link-layer address from others for the purposes of both protecting that address from deletion and computing EUI64. Return consistent, appropriate error codes from network drivers. Improve readability. KNF. *** Details *** In if_attach(), always initialize the interface ioctl routine, ifnet->if_ioctl, if the driver has not already initialized it. Delete if_ioctl == NULL tests everywhere else, because it cannot happen. In the ioctl routines of network interfaces, inherit common ioctl behaviors by calling either ifioctl_common() or whichever ioctl routine is appropriate for the class of interface---e.g., ether_ioctl() for ethernets. Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In the user->kernel interface, SIOCSIFADDR's argument was an ifreq, but on the protocol->ifnet interface, SIOCSIFADDR's argument was an ifaddr. That was confusing, and it would work against me as I make it possible for a network interface to overload most ioctls. On the protocol->ifnet interface, replace SIOCSIFADDR with SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to invoke SIOCINITIFADDR. In ifioctl(), give the interface the first shot at handling most interface ioctls, and give the protocol the second shot, instead of the other way around. Finally, let compatibility code (COMPAT_OSOCK) take a shot. Pull device initialization out of switch statements under SIOCINITIFADDR. For example, pull ..._init() out of any switch statement that looks like this: switch (...->sa_family) { case ...: ..._init(); ... break; ... default: ..._init(); ... break; } Rewrite many if-else clauses that handle all permutations of IFF_UP and IFF_RUNNING to use a switch statement, switch (x & (IFF_UP|IFF_RUNNING)) { case 0: ... break; case IFF_RUNNING: ... break; case IFF_UP: ... break; case IFF_UP|IFF_RUNNING: ... break; } unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and #ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4). In ipw(4), remove an if_set_sadl() call that is out of place. In nfe(4), reuse the jumbo MTU logic in ether_ioctl(). Let ethernets register a callback for setting h/w state such as promiscuous mode and the multicast filter in accord with a change in the if_flags: ether_set_ifflags_cb() registers a callback that returns ENETRESET if the caller should reset the ethernet by calling if_init(), 0 on success, != 0 on failure. Pull common code from ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(), and register if_flags callbacks for those drivers. Return ENOTTY instead of EINVAL for inappropriate ioctls. In zyd(4), use ENXIO instead of ENOTTY to indicate that the device is not any longer attached. Add to if_set_sadl() a boolean 'factory' argument that indicates whether a link-layer address was assigned by the factory or some other source. In a comment, recommend using the factory address for generating an EUI64, and update in6_get_hw_ifid() to prefer a factory address to any other link-layer address. Add a routing message, RTM_LLINFO_UPD, that tells protocols to update the binding of network-layer addresses to link-layer addresses. Implement this message in IPv4 and IPv6 by sending a gratuitous ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD messages on a change of an interface's link-layer address. In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address that is broadcast/multicast or equal to 00:00:00:00:00:00. Make ether_ioctl() call ifioctl_common() to handle ioctls that it does not understand. In gif(4), initialize if_softc and use it, instead of assuming that the gif_softc and ifp overlap. Let ifioctl_common() handle SIOCGIFADDR. Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels that certain invariants on a struct route are satisfied. In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit about the ioctls that we do not allow on an agr(4) member interface. bzero -> memset. Delete unnecessary casts to void *. Use sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with NULL instead of "testing truth". Replace some instances of (type *)0 with NULL. Change some K&R prototypes to ANSI C, and join lines.
2008-11-07 03:20:01 +03:00
error = ifioctl_common(sc->ifp, cmd, data);
break;
case SIOCAIFADDR:
2006-04-20 20:51:57 +04:00
case SIOCSIFDSTADDR:
break;
*** Summary *** When a link-layer address changes (e.g., ifconfig ex0 link 02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor Advertisement to update the network-/link-layer address bindings on our LAN peers. Refuse a change of ethernet address to the address 00:00:00:00:00:00 or to any multicast/broadcast address. (Thanks matt@.) Reorder ifnet ioctl operations so that driver ioctls may inherit the functions of their "class"---ether_ioctl(), fddi_ioctl(), et cetera---and the class ioctls may inherit from the generic ioctl, ifioctl_common(), but both driver- and class-ioctls may override the generic behavior. Make network drivers share more code. Distinguish a "factory" link-layer address from others for the purposes of both protecting that address from deletion and computing EUI64. Return consistent, appropriate error codes from network drivers. Improve readability. KNF. *** Details *** In if_attach(), always initialize the interface ioctl routine, ifnet->if_ioctl, if the driver has not already initialized it. Delete if_ioctl == NULL tests everywhere else, because it cannot happen. In the ioctl routines of network interfaces, inherit common ioctl behaviors by calling either ifioctl_common() or whichever ioctl routine is appropriate for the class of interface---e.g., ether_ioctl() for ethernets. Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In the user->kernel interface, SIOCSIFADDR's argument was an ifreq, but on the protocol->ifnet interface, SIOCSIFADDR's argument was an ifaddr. That was confusing, and it would work against me as I make it possible for a network interface to overload most ioctls. On the protocol->ifnet interface, replace SIOCSIFADDR with SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to invoke SIOCINITIFADDR. In ifioctl(), give the interface the first shot at handling most interface ioctls, and give the protocol the second shot, instead of the other way around. Finally, let compatibility code (COMPAT_OSOCK) take a shot. Pull device initialization out of switch statements under SIOCINITIFADDR. For example, pull ..._init() out of any switch statement that looks like this: switch (...->sa_family) { case ...: ..._init(); ... break; ... default: ..._init(); ... break; } Rewrite many if-else clauses that handle all permutations of IFF_UP and IFF_RUNNING to use a switch statement, switch (x & (IFF_UP|IFF_RUNNING)) { case 0: ... break; case IFF_RUNNING: ... break; case IFF_UP: ... break; case IFF_UP|IFF_RUNNING: ... break; } unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and #ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4). In ipw(4), remove an if_set_sadl() call that is out of place. In nfe(4), reuse the jumbo MTU logic in ether_ioctl(). Let ethernets register a callback for setting h/w state such as promiscuous mode and the multicast filter in accord with a change in the if_flags: ether_set_ifflags_cb() registers a callback that returns ENETRESET if the caller should reset the ethernet by calling if_init(), 0 on success, != 0 on failure. Pull common code from ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(), and register if_flags callbacks for those drivers. Return ENOTTY instead of EINVAL for inappropriate ioctls. In zyd(4), use ENXIO instead of ENOTTY to indicate that the device is not any longer attached. Add to if_set_sadl() a boolean 'factory' argument that indicates whether a link-layer address was assigned by the factory or some other source. In a comment, recommend using the factory address for generating an EUI64, and update in6_get_hw_ifid() to prefer a factory address to any other link-layer address. Add a routing message, RTM_LLINFO_UPD, that tells protocols to update the binding of network-layer addresses to link-layer addresses. Implement this message in IPv4 and IPv6 by sending a gratuitous ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD messages on a change of an interface's link-layer address. In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address that is broadcast/multicast or equal to 00:00:00:00:00:00. Make ether_ioctl() call ifioctl_common() to handle ioctls that it does not understand. In gif(4), initialize if_softc and use it, instead of assuming that the gif_softc and ifp overlap. Let ifioctl_common() handle SIOCGIFADDR. Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels that certain invariants on a struct route are satisfied. In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit about the ioctls that we do not allow on an agr(4) member interface. bzero -> memset. Delete unnecessary casts to void *. Use sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with NULL instead of "testing truth". Replace some instances of (type *)0 with NULL. Change some K&R prototypes to ANSI C, and join lines.
2008-11-07 03:20:01 +03:00
case SIOCINITIFADDR:
2006-04-20 20:51:57 +04:00
sc->ifp->if_flags |= IFF_UP; /* a Unix tradition */
break;
case SIOCSIFMTU:
if ((ifr->ifr_mtu < 72) || (ifr->ifr_mtu > 65535))
error = EINVAL;
else if ((error = ifioctl_common(sc->ifp, cmd, data)) == ENETRESET)
error = 0;
2006-04-20 20:51:57 +04:00
break;
default:
error = EINVAL;
break;
}
return error;
}
static void /* context: interrupt */
rawip_input(softc_t *sc, struct mbuf *mbuf)
{
ifnet_input(sc->ifp, mbuf);
}
static void /* context: interrupt */
rawip_output(softc_t *sc)
{
IFQ_DEQUEUE(&sc->ifp->if_snd, sc->tx_mbuf);
}
#elif NETDEV
static int /* context: process */
rawip_ioctl(softc_t *sc, struct ifreq *ifr, int cmd)
{
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
2006-04-20 20:51:57 +04:00
printk("%s: rawip_ioctl; cmd=0x%08x\n", NAME_UNIT, cmd);
2006-02-12 02:00:06 +03:00
return -EINVAL;
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static int /* context: interrupt */
rawip_type(softc_t *sc, struct sk_buff *skb)
2006-02-12 02:00:06 +03:00
{
if (skb->data[0]>>4 == 4)
return htons(ETH_P_IP);
else if (skb->data[0]>>4 == 6)
return htons(ETH_P_IPV6);
else
return htons(ETH_P_HDLC);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* Process Context */
rawip_mtu(softc_t *sc, int mtu)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
return ((mtu < 72) || (mtu > 65535)) ? -EINVAL : 0;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
#endif /* IFNET */
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
rawip_watchdog(softc_t *sc)
{
#if IFNET
if ((sc->status.link_state == STATE_UP) &&
(sc->ifp->if_flags & IFF_UP))
sc->ifp->if_flags |= IFF_RUNNING;
if ((sc->status.link_state != STATE_UP) ||
!(sc->ifp->if_flags & IFF_UP))
sc->ifp->if_flags &= ~IFF_RUNNING;
#endif /* IFNET */
/* Report current line protocol. */
sc->status.stack = STACK_RAWIP;
sc->status.proto = PROTO_IP_HDLC;
}
2006-02-12 02:00:06 +03:00
static int
rawip_open(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
2006-04-20 20:51:57 +04:00
sc->config.proto = PROTO_IP_HDLC;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
static int
rawip_attach(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
#if IFNET
2006-04-20 20:51:57 +04:00
LMC_BPF_ATTACH(sc, DLT_RAW, 0);
2006-02-12 02:00:06 +03:00
#endif
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_RAWIP;
sc->stack = &rawip_stack;
2005-12-06 06:01:45 +03:00
return 0;
}
2006-02-12 02:00:06 +03:00
static int
2006-04-20 20:51:57 +04:00
rawip_detach(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
#if IFNET
2006-04-20 20:51:57 +04:00
LMC_BPF_DETACH(sc);
2006-02-12 02:00:06 +03:00
ifnet_setup(sc->ifp);
#elif NETDEV
netdev_setup(sc->netdev);
#endif
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
sc->config.stack = STACK_NONE;
sc->config.proto = PROTO_NONE;
sc->stack = NULL;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
#if IFNET
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Called to give a newly arrived pkt to higher levels. */
/* Called from rxintr_cleanup() with bottom_lock held. */
2006-04-20 20:51:57 +04:00
/* This is only used with rawip_stack on a BSD. */
2006-02-12 02:00:06 +03:00
static void
2006-04-20 20:51:57 +04:00
ifnet_input(struct ifnet *ifp, struct mbuf *mbuf)
2006-02-12 02:00:06 +03:00
{
softc_t *sc = IFP2SC(ifp);
struct ifqueue *intrq;
int isr = 0;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
intrq = NULL; /* surpress compiler warning */
# if INET
if (mbuf->m_data[0]>>4 == 4)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
isr = NETISR_IP;
intrq = &ipintrq;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
# endif /* INET */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
# if INET6
if (mbuf->m_data[0]>>4 == 6)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
isr = NETISR_IPV6;
intrq = &ip6intrq;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
# endif /* INET6 */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (isr)
{
if (!IF_QFULL(intrq))
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
/* ifnet_input() ENQUEUES in a hard interrupt. */
2006-02-12 02:00:06 +03:00
/* ip_input() DEQUEUES in a soft interrupt. */
/* Some BSD QUEUE routines are not interrupt-safe. */
DISABLE_INTR; /* noop in FreeBSD */
IF_ENQUEUE(intrq, mbuf);
ENABLE_INTR; /* noop in FreeBSD */
schednetisr(isr); /* wake up the network code */
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else /* intrq is full */
{
m_freem(mbuf);
sc->status.cntrs.idrops++;
if (sc->config.debug)
2006-04-20 20:51:57 +04:00
printf("%s: ifnet_input: rx pkt dropped: intrq full\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else /* isr is zero */
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
m_freem(mbuf);
sc->status.cntrs.idrops++;
if (sc->config.debug)
2006-04-20 20:51:57 +04:00
printf("%s: ifnet_input: rx pkt dropped: not IPv4 or IPv6\n", NAME_UNIT);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* sppp and p2p replace this with their own proc.
2006-04-20 20:51:57 +04:00
* This is only used with rawip_stack on a BSD.
2006-02-12 02:00:06 +03:00
* This procedure is very similar to ng_rcvdata().
*/
2006-04-20 20:51:57 +04:00
static int /* context: process */
ifnet_output(struct ifnet *ifp, struct mbuf *m,
const struct sockaddr *dst, struct rtentry *rt)
2006-02-12 02:00:06 +03:00
{
softc_t *sc = IFP2SC(ifp);
int error = 0;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Fail if the link is down. */
if (sc->status.link_state != STATE_UP)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
m_freem(m);
sc->status.cntrs.odrops++;
if (sc->config.debug)
2006-04-20 20:51:57 +04:00
printf("%s: ifnet_output: tx pkt dropped: link down\n", NAME_UNIT);
2006-02-12 02:00:06 +03:00
return ENETDOWN;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
/* ifnet_output() ENQUEUEs in a syscall or softirq. */
2006-02-12 02:00:06 +03:00
/* txintr_setup() DEQUEUEs in a hard interrupt. */
/* Some BSD QUEUE routines are not interrupt-safe. */
{
DISABLE_INTR; /* noop in FreeBSD */
IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
ENABLE_INTR; /* noop in FreeBSD */
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (error)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
sc->status.cntrs.odrops++;
if (sc->config.debug)
2006-04-20 20:51:57 +04:00
printf("%s: ifnet_output: tx pkt dropped: IFQ_ENQUEUE(): error %d\n",
2006-02-12 02:00:06 +03:00
NAME_UNIT, error);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else
2006-04-20 20:51:57 +04:00
/* Process tx pkts; do not process rx pkts. */
lmc_interrupt(sc, 0, 0);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return error;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static int /* context: process */
ifnet_ioctl(struct ifnet *ifp, u_long cmd, void *data)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
softc_t *sc = IFP2SC(ifp);
struct ifreq *ifr = (struct ifreq *) data;
int error = 0;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Aquire ioctl/watchdog interlock. */
if ((error = TOP_LOCK(sc))) return error;
2006-02-12 02:00:06 +03:00
switch (cmd)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
/* Catch the IOCTLs used by lmcconfig. */
case LMCIOCGSTAT:
case LMCIOCGCFG:
case LMCIOCSCFG:
case LMCIOCREAD:
case LMCIOCWRITE:
case LMCIOCTL:
error = lmc_ioctl(sc, cmd, data);
break;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
case SIOCSIFCAP:
# if DEVICE_POLLING
if ((ifr->ifr_reqcap & IFCAP_POLLING) &&
!(ifp->if_capenable & IFCAP_POLLING) &&
!(error = ether_poll_register(bsd_poll, ifp)))
{ /* enable polling */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_DISABLE);
2006-02-12 02:00:06 +03:00
ifp->if_capenable |= IFCAP_POLLING;
}
else if (!(ifr->ifr_reqcap & IFCAP_POLLING) &&
(ifp->if_capenable & IFCAP_POLLING) &&
!(error = ether_poll_deregister(ifp)))
{ /* disable polling */
ifp->if_capenable &= ~IFCAP_POLLING;
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_TXRX);
2006-02-12 02:00:06 +03:00
}
else
error = EINVAL;
# endif /* DEVICE_POLLING */
break;
2005-12-06 06:01:45 +03:00
case SIOCSIFMEDIA: /* calls lmc_ifmedia_change() */
2006-02-12 02:00:06 +03:00
case SIOCGIFMEDIA: /* calls ifmedia_status() */
error = ifmedia_ioctl(ifp, ifr, &sc->ifm, cmd);
break;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Pass the rest to the line protocol. */
default:
2006-04-20 20:51:57 +04:00
if (sc->stack)
error = sc->stack->ioctl(sc, cmd, data);
2006-02-12 02:00:06 +03:00
else
error = ENOSYS;
break;
}
2006-04-20 20:51:57 +04:00
/* release ioctl/watchdog interlock */
TOP_UNLOCK(sc);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (error && sc->config.debug)
printf("%s: ifnet_ioctl: op=IO%s%s len=%3lu grp='%c' num=%3lu err=%d\n",
NAME_UNIT, cmd&IOC_IN ? "W":"", cmd&IOC_OUT ? "R":"",
IOCPARM_LEN(cmd), (char)IOCGROUP(cmd), cmd&0xFF, error);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static void /* context: process */
2006-02-12 02:00:06 +03:00
ifnet_start(struct ifnet *ifp)
{
softc_t *sc = IFP2SC(ifp);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Process tx pkts; do not process rx pkts. */
lmc_interrupt(sc, 0, 0);
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
2006-02-12 02:00:06 +03:00
ifnet_watchdog(struct ifnet *ifp)
{
softc_t *sc = IFP2SC(ifp);
struct cntrs *cntrs = &sc->status.cntrs;
lmc_watchdog(sc); /* updates link_state */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (sc->status.link_state == STATE_UP)
ifp->if_link_state = LINK_STATE_UP;
else
ifp->if_link_state = LINK_STATE_DOWN;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Copy statistics from sc to ifp. */
ifp->if_baudrate = sc->status.tx_speed;
ifp->if_ibytes = cntrs->ibytes;
ifp->if_obytes = cntrs->obytes;
ifp->if_ipackets = cntrs->ipackets;
ifp->if_opackets = cntrs->opackets;
ifp->if_ierrors = cntrs->ierrors;
ifp->if_oerrors = cntrs->oerrors;
ifp->if_iqdrops = cntrs->idrops;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* If the interface debug flag is set, set the driver debug flag. */
if (sc->ifp->if_flags & IFF_DEBUG)
sc->config.debug = 1;
/* Call this procedure again after one second. */
ifp->if_timer = 1;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
/* This setup is for RawIP; SPPP and P2P change many items. */
/* Note the similarity to linux's netdev_setup(). */
static void
ifnet_setup(struct ifnet *ifp)
{
ifp->if_flags = IFF_POINTOPOINT;
ifp->if_flags |= IFF_SIMPLEX;
ifp->if_flags |= IFF_NOARP;
ifp->if_input = ifnet_input;
ifp->if_output = ifnet_output;
ifp->if_start = ifnet_start;
ifp->if_ioctl = ifnet_ioctl;
ifp->if_watchdog = ifnet_watchdog;
ifp->if_timer = 1;
ifp->if_type = IFT_PTPSERIAL;
ifp->if_addrlen = 0;
ifp->if_hdrlen = 0;
ifp->if_mtu = MAX_DESC_LEN;
}
/* Attach the ifnet kernel interface. */
/* context: kernel (boot) or process (syscall) */
static int
ifnet_attach(softc_t *sc)
{
# if SPPP
2006-04-20 20:51:57 +04:00
sc->ifp = &sc->spppcom.pp_if;
# elif P2P
sc->ifp = &sc->p2pcom.p2p_if;
# else
sc->ifp = &sc->ifnet;
# endif
sc->ifp->if_softc = sc;
ifnet_setup(sc->ifp);
# if DEVICE_POLLING
sc->ifp->if_capabilities |= IFCAP_POLLING;
# endif
/* Every OS does it differently! */
2008-04-10 23:13:36 +04:00
strlcpy(sc->ifp->if_xname, device_xname(&sc->dev), IFNAMSIZ);
2006-04-20 20:51:57 +04:00
IFQ_SET_MAXLEN(&sc->ifp->if_snd, SNDQ_MAXLEN);
IFQ_SET_READY(&sc->ifp->if_snd);
if_attach(sc->ifp);
if_alloc_sadl(sc->ifp);
ifmedia_setup(sc);
return 0;
}
/* Detach the ifnet kernel interface. */
/* context: kernel (boot) or process (syscall). */
static void
ifnet_detach(softc_t *sc)
{
ifmedia_delete_instance(&sc->ifm, IFM_INST_ANY);
# if DEVICE_POLLING
if (sc->ifp->if_capenable & IFCAP_POLLING)
ether_poll_deregister(sc->ifp);
# endif
IFQ_PURGE(&sc->ifp->if_snd);
if_free_sadl(sc->ifp);
if_detach(sc->ifp);
}
2006-02-12 02:00:06 +03:00
static void
ifmedia_setup(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
/* Initialize ifmedia mechanism. */
ifmedia_init(&sc->ifm, IFM_OMASK | IFM_GMASK | IFM_IMASK,
lmc_ifmedia_change, ifmedia_status);
2006-02-12 02:00:06 +03:00
ifmedia_add(&sc->ifm, IFM_ETHER | IFM_NONE, 0, NULL);
ifmedia_set(&sc->ifm, IFM_ETHER | IFM_NONE);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
/* SIOCSIFMEDIA: context: process. */
2006-02-12 02:00:06 +03:00
static int
lmc_ifmedia_change(struct ifnet *ifp)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
softc_t *sc = IFP2SC(ifp);
struct config config = sc->config;
int media = sc->ifm.ifm_media;
int error;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* ifconfig lmc0 mediaopt loopback */
if (media & IFM_LOOP)
config.loop_back = CFG_LOOP_TULIP;
else
config.loop_back = CFG_LOOP_NONE;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
error = open_proto(sc, &config);
tulip_loop(sc, &config);
sc->card->attach(sc, &config);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
return error;
2006-02-12 02:00:06 +03:00
}
2006-04-20 20:51:57 +04:00
/* SIOCGIFMEDIA: context: process. */
2006-02-12 02:00:06 +03:00
static void
2006-04-20 20:51:57 +04:00
ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
2006-02-12 02:00:06 +03:00
{
2006-04-20 20:51:57 +04:00
softc_t *sc = IFP2SC(ifp);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
ifmr->ifm_status = IFM_AVALID;
if (sc->status.link_state == STATE_UP)
ifmr->ifm_status |= IFM_ACTIVE;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
if (sc->config.loop_back != CFG_LOOP_NONE)
ifmr->ifm_active |= IFM_LOOP;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
#endif /* IFNET */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
#if NETDEV
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This net_device method is called when IFF_UP goes true. */
2006-04-20 20:51:57 +04:00
static int /* context: process */
2006-02-12 02:00:06 +03:00
netdev_open(struct net_device *netdev)
{
softc_t *sc = NETDEV2SC(netdev);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_TXRX);
2006-02-12 02:00:06 +03:00
netif_start_queue(sc->netdev);
set_ready(sc, 1);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This net_device method is called when IFF_UP goes false. */
2006-04-20 20:51:57 +04:00
static int /* context: process */
2006-02-12 02:00:06 +03:00
netdev_stop(struct net_device *netdev)
{
softc_t *sc = NETDEV2SC(netdev);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
set_ready(sc, 0);
netif_stop_queue(sc->netdev);
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_DISABLE);
2005-12-06 06:01:45 +03:00
return 0;
}
2006-02-12 02:00:06 +03:00
/* This net_device method hands outgoing packets to the transmitter. */
/* With txintr_setup(), it implements output flow control. */
2006-04-20 20:51:57 +04:00
static int /* context: netdev->xmit_lock held; BHs disabled */
2006-02-12 02:00:06 +03:00
netdev_start(struct sk_buff *skb, struct net_device *netdev)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
softc_t *sc = NETDEV2SC(netdev);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (sc->tx_skb == NULL)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
/* Put this skb where the transmitter will see it. */
sc->tx_skb = skb;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Process tx pkts; do not process rx pkts. */
lmc_interrupt(sc, 0, 0);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
return NETDEV_TX_OK;
2006-02-12 02:00:06 +03:00
}
else
2006-04-20 20:51:57 +04:00
{
/* txintr_setup() calls netif_wake_queue(). */
2006-02-12 02:00:06 +03:00
netif_stop_queue(netdev);
2006-04-20 20:51:57 +04:00
return NETDEV_TX_BUSY;
}
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
# if NAPI
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This net_device method services the card without interrupts. */
/* With rxintr_cleanup(), it implements input flow control. */
2006-04-20 20:51:57 +04:00
static int /* context: softirq */
2006-02-12 02:00:06 +03:00
netdev_poll(struct net_device *netdev, int *budget)
{
softc_t *sc = NETDEV2SC(netdev);
int received;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Handle the card interrupt with kernel ints enabled. */
2006-02-12 02:00:06 +03:00
/* Allow processing up to netdev->quota incoming packets. */
/* This is the ONLY time lmc_interrupt() may process rx pkts. */
/* Otherwise (sc->quota == 0) and rxintr_cleanup() is a NOOP. */
2006-04-20 20:51:57 +04:00
lmc_interrupt(sc, min(netdev->quota, *budget), 0);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Report number of rx packets processed. */
received = netdev->quota - sc->quota;
netdev->quota -= received;
*budget -= received;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* If quota prevented processing all rx pkts, leave rx ints disabled. */
if (sc->quota == 0) /* this is off by one...but harmless */
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_TX);
2006-02-12 02:00:06 +03:00
return 1; /* more pkts to handle -- reschedule */
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
/* Remove self from poll list. */
netif_rx_complete(netdev);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Enable card interrupts. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_TXRX);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0; /* all pkts handled -- success */
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
# endif /* NAPI */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This net_device method handles IOCTL syscalls. */
2006-04-20 20:51:57 +04:00
static int /* context: process */
2006-02-12 02:00:06 +03:00
netdev_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
softc_t *sc = NETDEV2SC(netdev);
int error = 0;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Aquire ioctl/watchdog interlock. */
if ((error = TOP_LOCK(sc))) return error;
2006-02-12 02:00:06 +03:00
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 = NULL;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (iohdr->cookie != NGM_LMC_COOKIE)
error = -EINVAL;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Emulate a BSD-style IOCTL syscall. */
if (!error)
error = (kern_addr = kmalloc(length, GFP_KERNEL)) ? 0: -ENOMEM;
if (!error && (direction & DIR_IOW))
error = copy_from_user(kern_addr, user_addr, length);
if (!error)
error = -lmc_ioctl(sc, (unsigned long)cmd, kern_addr);
if (!error && (direction & DIR_IOR))
error = copy_to_user(user_addr, kern_addr, length);
kfree(kern_addr);
}
2006-04-20 20:51:57 +04:00
else if (sc->stack)
error = sc->stack->ioctl(sc, ifr, cmd);
2006-02-12 02:00:06 +03:00
else
error = -ENOSYS;
# if GEN_HDLC
/* If generic-HDLC is present but not the currently-attached
2006-04-20 20:51:57 +04:00
* stack, call hdlc_ioctl() anyway because proto must
2006-02-12 02:00:06 +03:00
* be set using SIOCWANDEV or hdlc_open() will fail.
*/
2006-04-20 20:51:57 +04:00
if (sc->stack != &gen_hdlc_stack)
2006-02-12 02:00:06 +03:00
hdlc_ioctl(sc->netdev, ifr, cmd); /* ignore error result */
# endif
2006-04-20 20:51:57 +04:00
/* Release ioctl/watchdog interlock. */
TOP_UNLOCK(sc);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (error && sc->config.debug)
printk("%s: netdev_ioctl; cmd=0x%08x error=%d\n",
NAME_UNIT, cmd, error);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return error;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* This net_device method sets the Maximum Tranmit Unit. */
2006-04-20 20:51:57 +04:00
/* This driver does not limit MTU; stacks and protos do. */
2006-02-12 02:00:06 +03:00
static int
netdev_mtu(struct net_device *netdev, int mtu)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
softc_t *sc = NETDEV2SC(netdev);
int error = 0;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
if (sc->stack)
error = sc->stack->mtu(sc, mtu);
2006-02-12 02:00:06 +03:00
else
error = -ENOSYS;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (!error)
netdev->mtu = mtu;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return error;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This net_device method restarts the transmitter if it hangs. */
2006-04-20 20:51:57 +04:00
static void /* BHs disabled */
2006-02-12 02:00:06 +03:00
netdev_timeout(struct net_device *netdev)
{
softc_t *sc = NETDEV2SC(netdev);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Process tx pkts; do not process rx pkts. */
lmc_interrupt(sc, 0, 0);
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This net_device method returns a pointer to device statistics. */
2006-04-20 20:51:57 +04:00
static struct net_device_stats * /* context: process */
2006-02-12 02:00:06 +03:00
netdev_stats(struct net_device *netdev)
{
softc_t *sc = NETDEV2SC(netdev);
# if GEN_HDLC
return &sc->hdlcdev->stats;
# else
return &sc->netdev_stats;
# endif
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
2006-02-12 02:00:06 +03:00
netdev_watchdog(unsigned long softc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
softc_t *sc = (softc_t *)softc;
struct cntrs *cntrs = &sc->status.cntrs;
struct net_device_stats *stats = netdev_stats(sc->netdev);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
lmc_watchdog(sc); /* updates link_state */
/* Notice when the link comes up. */
if ((sc->last_link_state != STATE_UP) &&
(sc->status.link_state == STATE_UP))
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
netif_wake_queue(sc->netdev);
netif_carrier_on(sc->netdev);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Notice when the link goes down. */
if ((sc->last_link_state == STATE_UP) &&
(sc->status.link_state != STATE_UP))
{
2006-04-20 20:51:57 +04:00
netif_tx_disable(sc->netdev);
2006-02-12 02:00:06 +03:00
netif_carrier_off(sc->netdev);
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Copy statistics from sc to netdev. */
stats->rx_bytes = cntrs->ibytes;
stats->tx_bytes = cntrs->obytes;
stats->rx_packets = cntrs->ipackets;
stats->tx_packets = cntrs->opackets;
stats->rx_errors = cntrs->ierrors;
stats->tx_errors = cntrs->oerrors;
stats->rx_dropped = cntrs->idrops;
stats->rx_missed_errors = cntrs->missed;
stats->tx_dropped = cntrs->odrops;
stats->rx_fifo_errors = cntrs->fifo_over;
stats->rx_over_errors = cntrs->overruns;
stats->tx_fifo_errors = cntrs->fifo_under;
/*stats->tx_under_errors = cntrs=>underruns; */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* If the interface debug flag is set, set the driver debug flag. */
if (sc->netdev->flags & IFF_DEBUG)
sc->config.debug = 1;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Call this procedure again after one second. */
sc->wd_timer.expires = jiffies + HZ -8; /* -8 is a FUDGE factor */
add_timer(&sc->wd_timer);
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This setup is for RawIP; Generic-HDLC changes many items. */
/* Note the similarity to BSD's ifnet_setup(). */
static void
netdev_setup(struct net_device *netdev)
{
netdev->flags = IFF_POINTOPOINT;
netdev->flags |= IFF_NOARP;
netdev->open = netdev_open;
netdev->stop = netdev_stop;
netdev->hard_start_xmit = netdev_start;
# if NAPI
netdev->poll = netdev_poll;
2006-04-20 20:51:57 +04:00
netdev->weight = 32; /* sc->rxring.num_descs; */
2006-02-12 02:00:06 +03:00
# endif
2006-04-20 20:51:57 +04:00
netdev->rebuild_header = NULL; /* no arp */
netdev->hard_header = NULL; /* no arp */
2006-02-12 02:00:06 +03:00
netdev->do_ioctl = netdev_ioctl;
netdev->change_mtu = netdev_mtu;
netdev->tx_timeout = netdev_timeout;
netdev->get_stats = netdev_stats;
netdev->watchdog_timeo = 1 * HZ;
netdev->mtu = MAX_DESC_LEN;
netdev->type = ARPHRD_HDLC;
netdev->hard_header_len = 16;
netdev->addr_len = 0;
netdev->tx_queue_len = SNDQ_MAXLEN;
/* 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 */
2006-04-20 20:51:57 +04:00
/* does not compute soft checksums. All that work for nothing! */
2006-02-12 02:00:06 +03:00
/*netdev->features |= NETIF_F_SG; */
/*netdev->features |= NETIF_F_FRAGLIST; */
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Attach the netdevice kernel interface. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall). */
2005-12-06 06:01:45 +03:00
static int
2006-02-12 02:00:06 +03:00
netdev_attach(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
int error;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
# if GEN_HDLC /* generic-hdlc line protocol pkg configured */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Allocate space for the HDLC network device struct. */
/* Allocating a netdev and attaching to generic-HDLC should be separate. */
if ((sc->netdev = alloc_hdlcdev(sc)) == NULL)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
printk("%s: netdev_attach: alloc_hdlcdev() failed\n", DEVICE_NAME);
return -ENOMEM;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Initialize the basic network device struct. */
/* This clobbers some netdev stuff set by alloc_hdlcdev(). */
/* Our get_stats() and change_mtu() do the right thing. */
netdev_setup(sc->netdev);
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* HACK: make the private eco-net pointer -> struct softc. */
sc->netdev->ec_ptr = sc;
/* Cross-link pcidev and netdev. */
SET_NETDEV_DEV(sc->netdev, &sc->pcidev->dev);
sc->netdev->mem_end = pci_resource_end(sc->pcidev, 1);
sc->netdev->mem_start = pci_resource_start(sc->pcidev, 1);
sc->netdev->base_addr = pci_resource_start(sc->pcidev, 0);
sc->netdev->irq = sc->pcidev->irq;
2006-02-12 02:00:06 +03:00
/* Initialize the HDLC extension to the network device. */
sc->hdlcdev = sc->netdev->priv;
sc->hdlcdev->attach = gen_hdlc_card_params;
sc->hdlcdev->xmit = netdev_start; /* the REAL hard_start_xmit() */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if ((error = register_hdlc_device(sc->netdev)))
{
printk("%s: register_hdlc_device(): error %d\n", DEVICE_NAME, error);
free_netdev(sc->netdev);
return error;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
# else
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Allocate and initialize the basic network device struct. */
if ((sc->netdev = alloc_netdev(0, DEVICE_NAME"%d", netdev_setup)) == NULL)
{
printk("%s: netdev_attach: alloc_netdev() failed\n", DEVICE_NAME);
return -ENOMEM;
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
2006-02-12 02:00:06 +03:00
/* HACK: make the private eco-net pointer -> struct softc. */
sc->netdev->ec_ptr = sc;
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Cross-link pcidev and netdev. */
SET_NETDEV_DEV(sc->netdev, &sc->pcidev->dev);
sc->netdev->mem_end = pci_resource_end(sc->pcidev, 1);
sc->netdev->mem_start = pci_resource_start(sc->pcidev, 1);
sc->netdev->base_addr = pci_resource_start(sc->pcidev, 0);
sc->netdev->irq = sc->pcidev->irq;
2006-02-12 02:00:06 +03:00
if ((error = register_netdev(sc->netdev)))
{
printk("%s: register_netdev(): error %d\n", DEVICE_NAME, error);
free_netdev(sc->netdev);
return error;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
# endif /* GEN_HDLC */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Arrange to call netdev_watchdog() once a second. */
init_timer(&sc->wd_timer);
sc->wd_timer.expires = jiffies + HZ; /* now plus one second */
sc->wd_timer.function = &netdev_watchdog;
sc->wd_timer.data = (unsigned long) sc;
add_timer(&sc->wd_timer);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0; /* success */
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Detach the netdevice kernel interface. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall). */
2005-12-06 06:01:45 +03:00
static void
2006-02-12 02:00:06 +03:00
netdev_detach(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
if (sc->pcidev == NULL) return;
if (sc->netdev == NULL) return;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
netdev_stop(sc->netdev); /* check for not inited */
del_timer(&sc->wd_timer);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
# if GEN_HDLC
unregister_hdlc_device(sc->netdev);
# else
unregister_netdev(sc->netdev);
# endif
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
free_netdev(sc->netdev);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
#endif /* NETDEV */
2005-12-06 06:01:45 +03:00
#if BSD
2005-12-06 06:01:45 +03:00
/* 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
* lmc_interrupt() does not 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.
*/
2005-12-06 06:01:45 +03:00
/* Initialize a DMA descriptor ring. */
/* context: kernel (boot) or process (syscall) */
static int /* BSD version */
create_ring(softc_t *sc, struct desc_ring *ring, int num_descs)
2006-02-12 02:00:06 +03:00
{
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)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
printf("%s: DMA descriptor array > PAGE_SIZE (%d)\n", NAME_UNIT,
(u_int)PAGE_SIZE);
return EINVAL;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Use the DMA tag passed to attach() for descriptors and buffers. */
ring->tag = sc->pa_dmat;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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(): error %d\n", NAME_UNIT, error);
return error;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Map physical address to kernel virtual address. */
if ((error = bus_dmamem_map(ring->tag, ring->segs, ring->nsegs,
size_descs, (void **)&ring->first, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)))
2006-02-12 02:00:06 +03:00
{
printf("%s: bus_dmamem_map(): error %d\n", NAME_UNIT, error);
return error;
}
descs = ring->first; /* suppress compiler warning about aliasing */
memset(descs, 0, size_descs);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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(): error %d\n", NAME_UNIT, error);
return error;
}
2005-12-06 06:01:45 +03:00
2006-04-20 20:51:57 +04:00
/* Map kernel virt addr to PCI bus addr for DMA descriptor array. */
2006-02-12 02:00:06 +03:00
if ((error = bus_dmamap_load(ring->tag, ring->map, descs, size_descs,
0, BUS_DMA_NOWAIT)))
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
printf("%s: bus_dmamap_load(): error %d\n", NAME_UNIT, error);
return error;
}
ring->dma_addr = ring->map->dm_segs[0].ds_addr;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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)))
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
printf("%s: bus_dmamap_create(): error %d\n", NAME_UNIT, error);
return error;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Destroy a DMA descriptor ring */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
2006-02-12 02:00:06 +03:00
static void /* BSD version */
destroy_ring(softc_t *sc, struct desc_ring *ring)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
struct dma_desc *desc;
struct mbuf *m;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Free queued mbufs. */
while ((m = mbuf_dequeue(ring)))
m_freem(m);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* TX may have one pkt that is not on any queue. */
if (sc->tx_mbuf)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
m_freem(sc->tx_mbuf);
sc->tx_mbuf = NULL;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Unmap active DMA descriptors. */
while (ring->read != ring->write)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
bus_dmamap_unload(ring->tag, ring->read->map);
if (ring->read++ == ring->last) ring->read = ring->first;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* Free the dmamaps of all DMA descriptors. */
for (desc=ring->first; desc!=ring->last+1; desc++)
if (desc->map)
bus_dmamap_destroy(ring->tag, desc->map);
/* Unmap PCI address for DMA descriptor array. */
if (ring->dma_addr)
bus_dmamap_unload(ring->tag, ring->map);
/* Free dmamap for DMA descriptor array. */
if (ring->map)
bus_dmamap_destroy(ring->tag, ring->map);
/* Unmap kernel address for DMA descriptor array. */
if (ring->first)
bus_dmamem_unmap(ring->tag, (void *)ring->first, ring->size_descs);
2006-02-12 02:00:06 +03:00
/* Free kernel memory for DMA descriptor array. */
if (ring->segs[0].ds_addr)
bus_dmamem_free(ring->tag, ring->segs, ring->nsegs);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* 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.
*/
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
m->m_nextpkt = NULL;
if (ring->tail == NULL)
ring->head = m;
else
ring->tail->m_nextpkt = m;
ring->tail = m;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Get an mbuf (chain) from the head of the descriptor ring queue. */
static struct mbuf* /* BSD version */
mbuf_dequeue(struct desc_ring *ring)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
struct mbuf *m = ring->head;
if (m)
if ((ring->head = m->m_nextpkt) == NULL)
ring->tail = NULL;
return m;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Clean up after a packet has been received. */
static int /* BSD version */
rxintr_cleanup(softc_t *sc)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Input packet flow control (livelock prevention): */
/* Give pkts to higher levels only if quota is > 0. */
if (sc->quota <= 0) return 0;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* This looks complicated, but remember: typically packets up */
/* to 2048 bytes long fit in one mbuf and use one descriptor. */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
first_desc = last_desc = ring->read;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* 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) && /* hardware done */
!(ring->read->status & TLP_DSTS_RX_FIRST_DESC)) /* 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 (;;)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
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);
2006-04-20 20:51:57 +04:00
/* Unmap kernel virtual address to PCI bus address. */
2006-02-12 02:00:06 +03:00
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
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
else /* 2) link mbufs. */
2005-12-06 06:01:45 +03:00
{
KASSERT(last_mbuf != NULL);
2006-02-12 02:00:06 +03:00
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 drop this packet. */
/* RxHDLC sets MIIERR for bad CRC, abort and partial byte at pkt end. */
if (!(last_desc->status & TLP_DSTS_RX_BAD) &&
(sc->status.link_state == STATE_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)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
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;
2005-12-06 06:01:45 +03:00
}
}
2006-02-12 02:00:06 +03:00
/* 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++;
/* Berkeley Packet Filter */
2006-04-20 20:51:57 +04:00
LMC_BPF_MTAP(sc, first_mbuf);
2006-02-12 02:00:06 +03:00
/* Give this good packet to the network stacks. */
sc->quota--;
2006-04-20 20:51:57 +04:00
if (sc->stack)
sc->stack->input(sc, first_mbuf);
2006-02-12 02:00:06 +03:00
else
{
m_freem(first_mbuf);
sc->status.cntrs.idrops++;
}
}
else if (sc->status.link_state != STATE_UP)
{
/* If the link is down, this packet is probably noise. */
m_freem(first_mbuf);
sc->status.cntrs.idrops++;
if (sc->config.debug)
printf("%s: rxintr_cleanup: rx pkt dropped: link down\n", NAME_UNIT);
}
else /* Log and drop this bad packet. */
{
if (sc->config.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);
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
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.rxbuf++;
if (sc->config.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.rxbuf++;
if (sc->config.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. */
2006-04-20 20:51:57 +04:00
/* Hardware will not see it until the OWNER bit is set. */
2006-02-12 02:00:06 +03:00
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;
2006-04-20 20:51:57 +04:00
/* Map kernel virt addr to PCI bus addr. */
2006-02-12 02:00:06 +03:00
if ((error = DMA_LOAD(desc->map, m->m_data, desc_len)))
printf("%s: bus_dmamap_load(rx): 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. */
desc->address1 = desc->map->dm_segs[0].ds_addr;
desc->length1 = desc_len>>1;
desc->address2 = desc->address1 + desc->length1;
desc->length2 = desc_len>>1;
2006-04-20 20:51:57 +04:00
/* Before setting the OWNER bit, flush cache backing DMA descriptors. */
2006-02-12 02:00:06 +03:00
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. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_RX_POLL, 1);
2006-02-12 02:00:06 +03:00
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))
{
/* 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);
2006-04-20 20:51:57 +04:00
/* Unmap kernel virtual address to PCI bus address. */
2006-02-12 02:00:06 +03:00
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)
{
struct mbuf *m;
if ((m = mbuf_dequeue(ring)) == NULL)
panic("%s: txintr_cleanup: expected an mbuf\n", NAME_UNIT);
/* The only bad TX status is fifo underrun. */
if (desc->status & TLP_DSTS_TX_UNDERRUN)
{
if (sc->config.debug)
printf("%s: txintr_cleanup: tx fifo underrun\n", NAME_UNIT);
sc->status.cntrs.fifo_under++;
sc->status.cntrs.oerrors++;
}
else
{
/* 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++;
/* Berkeley Packet Filter */
2006-04-20 20:51:57 +04:00
LMC_BPF_MTAP(sc, m);
2006-02-12 02:00:06 +03:00
}
m_freem(m);
return 1; /* did something */
}
}
return 0;
}
/* Build DMA descriptors for a transmit packet mbuf chain. */
2006-04-20 20:51:57 +04:00
static int /* 0=success; 1=error */ /* BSD version */
2006-02-12 02:00:06 +03:00
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)
{
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++; /* IFF_OACTIVE? */
return 1;
}
/* Provisionally, write a descriptor into the ring. */
2006-04-20 20:51:57 +04:00
/* But do not change the REAL ring write pointer. */
/* Hardware will not see it until the OWNER bit is set. */
2006-02-12 02:00:06 +03:00
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;
2006-04-20 20:51:57 +04:00
/* Do not pad short packets up to 64 bytes. */
2006-02-12 02:00:06 +03:00
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;
2006-04-20 20:51:57 +04:00
/* Map kernel virt addr to PCI bus addr. */
2006-02-12 02:00:06 +03:00
if ((error = DMA_LOAD(desc->map, data, desc_len)))
printf("%s: bus_dmamap_load(tx): 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;
{
bus_dma_segment_t *segs = desc->map->dm_segs;
int nsegs = desc->map->dm_nsegs;
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;
}
}
data += desc_len;
length -= desc_len;
} /* while (length > 0) */
m = m->m_next;
} /* while (m) */
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;
2006-04-20 20:51:57 +04:00
/* Protect against half-up links: Do not transmit */
/* if the receiver can not hear the far end. */
2006-02-12 02:00:06 +03:00
if (sc->status.link_state != STATE_UP) return 0;
/* Pick a packet to transmit. */
2006-04-20 20:51:57 +04:00
if ((sc->tx_mbuf == NULL) && sc->stack)
sc->stack->output(sc);
2006-02-12 02:00:06 +03:00
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)) 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;
2006-04-20 20:51:57 +04:00
/* Before setting the OWNER bit, flush cache backing DMA descriptors. */
2006-02-12 02:00:06 +03:00
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. */
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, TLP_TX_POLL, 1);
2006-02-12 02:00:06 +03:00
return 1; /* did something */
}
2006-04-20 20:51:57 +04:00
/* BSD kernels call this when a hardware interrupt happens. */
static intr_return_t /* context: interrupt */
2006-02-12 02:00:06 +03:00
bsd_interrupt(void *arg)
{
softc_t *sc = arg;
# if DEVICE_POLLING
if (sc->ifp->if_capenable & IFCAP_POLLING)
return IRQ_NONE;
# endif
/* Cut losses early if this is not our interrupt. */
2006-04-20 20:51:57 +04:00
if ((READ_CSR(sc, TLP_STATUS) & TLP_INT_TXRX)==0)
2006-02-12 02:00:06 +03:00
return IRQ_NONE;
2006-04-20 20:51:57 +04:00
/* Process tx and rx pkts. */
lmc_interrupt(sc, sc->rxring.num_descs, 0);
2006-02-12 02:00:06 +03:00
return IRQ_HANDLED;
}
#endif /* BSD */
# if DEVICE_POLLING
2006-04-20 20:51:57 +04:00
2006-02-12 02:00:06 +03:00
/* This procedure services the card without interrupts. */
/* With rxintr_cleanup(), it implements input flow control. */
2006-04-20 20:51:57 +04:00
static void /* context: softirq */
2006-02-12 02:00:06 +03:00
bsd_poll(struct ifnet *ifp, enum poll_cmd cmd, int quota)
{
softc_t *sc = IFP2SC(ifp);
/* Cut losses early if this is not our interrupt. */
2006-04-20 20:51:57 +04:00
if ((READ_CSR(sc, TLP_STATUS) & TLP_INT_TXRX)==0)
2006-02-12 02:00:06 +03:00
return;
2006-04-20 20:51:57 +04:00
/* Process all tx pkts and up to quota rx pkts. */
lmc_interrupt(sc, quota, (cmd==POLL_AND_CHECK_STATUS));
2006-02-12 02:00:06 +03:00
}
2006-04-20 20:51:57 +04:00
# endif /* DEVICE_POLLING */
2006-02-12 02:00:06 +03:00
/* Open a line protocol. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
static int
open_proto(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
int error = 0;
2006-02-12 02:00:06 +03:00
if (sc->stack)
error = sc->stack->open(sc, config);
else
error = BSD ? ENOSYS : -ENOSYS;
2006-02-12 02:00:06 +03:00
return error;
2006-02-12 02:00:06 +03:00
}
/* Attach a line protocol stack. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
static int
attach_stack(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
int error = 0;
struct stack *stack = NULL;
2006-02-12 02:00:06 +03:00
/* Done if stack is not changing. */
if (sc->config.stack == config->stack)
return 0;
2006-02-12 02:00:06 +03:00
/* Detach the current stack. */
if (sc->stack && ((error = sc->stack->detach(sc))))
return error;
2006-02-12 02:00:06 +03:00
switch (config->stack)
2006-02-12 02:00:06 +03:00
{
case STACK_RAWIP: /* built-in */
stack = &rawip_stack;
break;
2006-02-12 02:00:06 +03:00
#if SPPP
case STACK_SPPP:
stack = &sppp_stack;
break;
#endif
2006-02-12 02:00:06 +03:00
#if P2P
case STACK_P2P:
stack = &p2p_stack;
break;
#endif
2006-02-12 02:00:06 +03:00
#if GEN_HDLC
case STACK_GEN_HDLC:
stack = &gen_hdlc_stack;
break;
#endif
2006-02-12 02:00:06 +03:00
#if SYNC_PPP
case STACK_SYNC_PPP:
stack = &sync_ppp_stack;
break;
#endif
2006-02-12 02:00:06 +03:00
default:
stack = NULL;
break;
2006-02-12 02:00:06 +03:00
}
if (stack)
error = stack->attach(sc, config);
else
error = BSD ? ENOSYS : -ENOSYS;
2006-02-12 02:00:06 +03:00
return error;
}
2006-02-12 02:00:06 +03:00
/*
* This handles IOCTLs from lmcconfig(8).
* Must not run when card watchdogs run.
* Always called with top_lock held.
*/
static int /* context: process */
lmc_ioctl(softc_t *sc, u_long cmd, void *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;
2006-02-12 02:00:06 +03:00
/* All structs start with a string and a cookie. */
if (iohdr->cookie != NGM_LMC_COOKIE)
return EINVAL;
2006-02-12 02:00:06 +03:00
switch (cmd)
{
case LMCIOCGSTAT:
2006-02-12 02:00:06 +03:00
{
*status = sc->status;
iohdr->cookie = NGM_LMC_COOKIE;
break;
2006-02-12 02:00:06 +03:00
}
case LMCIOCGCFG:
2006-02-12 02:00:06 +03:00
{
*config = sc->config;
iohdr->cookie = NGM_LMC_COOKIE;
break;
2006-02-12 02:00:06 +03:00
}
case LMCIOCSCFG:
2006-04-20 20:51:57 +04:00
{
if ((error = CHECK_CAP)) break;
if ((error = attach_stack(sc, config)));
else error = open_proto(sc, config);
tulip_loop(sc, config);
sc->card->attach(sc, config);
sc->config.debug = config->debug;
break;
2006-04-20 20:51:57 +04:00
}
case LMCIOCREAD:
2006-02-12 02:00:06 +03:00
{
if (ioctl->cmd == IOCTL_RW_PCI)
2006-02-12 02:00:06 +03:00
{
if (ioctl->address > 252) { error = EFAULT; break; }
ioctl->data = READ_PCI_CFG(sc, ioctl->address);
2006-02-12 02:00:06 +03:00
}
else if (ioctl->cmd == IOCTL_RW_CSR)
2006-02-12 02:00:06 +03:00
{
if (ioctl->address > 15) { error = EFAULT; break; }
ioctl->data = READ_CSR(sc, ioctl->address*TLP_CSR_STRIDE);
2006-02-12 02:00:06 +03:00
}
else if (ioctl->cmd == IOCTL_RW_SROM)
{
if (ioctl->address > 63) { error = EFAULT; break; }
ioctl->data = srom_read(sc, ioctl->address);
}
else if (ioctl->cmd == IOCTL_RW_BIOS)
ioctl->data = bios_read(sc, ioctl->address);
else if (ioctl->cmd == IOCTL_RW_MII)
ioctl->data = mii_read(sc, ioctl->address);
else if (ioctl->cmd == IOCTL_RW_FRAME)
ioctl->data = framer_read(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; }
2006-04-20 20:51:57 +04:00
WRITE_CSR(sc, ioctl->address*TLP_CSR_STRIDE, ioctl->data);
2006-02-12 02:00:06 +03:00
}
else if (ioctl->cmd == IOCTL_RW_SROM)
{
if (ioctl->address > 63) { error = EFAULT; break; }
srom_write(sc, ioctl->address, ioctl->data); /* can sleep */
}
else if (ioctl->cmd == IOCTL_RW_BIOS)
{
if (ioctl->address == 0) bios_erase(sc);
bios_write(sc, ioctl->address, ioctl->data); /* can sleep */
}
else if (ioctl->cmd == IOCTL_RW_MII)
mii_write(sc, ioctl->address, ioctl->data);
else if (ioctl->cmd == IOCTL_RW_FRAME)
framer_write(sc, ioctl->address, ioctl->data);
else if (ioctl->cmd == IOCTL_WO_SYNTH)
synth_write(sc, (struct synth *)&ioctl->data);
else if (ioctl->cmd == IOCTL_WO_DAC)
{
dac_write(sc, 0x9002); /* set Vref = 2.048 volts */
dac_write(sc, ioctl->data & 0xFFF);
}
else
error = EINVAL;
break;
}
case LMCIOCTL:
{
if ((error = CHECK_CAP)) break;
if (ioctl->cmd == IOCTL_XILINX_RESET)
{
xilinx_reset(sc);
sc->card->attach(sc, &sc->config);
}
else if (ioctl->cmd == IOCTL_XILINX_ROM)
{
xilinx_load_from_rom(sc);
sc->card->attach(sc, &sc->config);
}
else if (ioctl->cmd == IOCTL_XILINX_FILE)
{
error = xilinx_load_from_file(sc, ioctl->ucode, ioctl->data);
if (error) xilinx_load_from_rom(sc); /* try the rom */
sc->card->attach(sc, &sc->config);
}
else if (ioctl->cmd == IOCTL_RESET_CNTRS)
reset_cntrs(sc);
else
error = sc->card->ioctl(sc, ioctl);
break;
}
default:
error = EINVAL;
break;
2006-02-12 02:00:06 +03:00
}
return error;
2006-02-12 02:00:06 +03:00
}
/* This is the core watchdog procedure.
* ioctl syscalls and card watchdog routines must be interlocked.
* Called by ng_watchdog(), ifnet_watchdog() and netdev_watchdog().
*/
static void /* context: softirq */
lmc_watchdog(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
/* Read and restart the Tulip timer. */
u_int32_t tx_speed = READ_CSR(sc, TLP_TIMER);
WRITE_CSR(sc, TLP_TIMER, 0xFFFF);
2006-02-12 02:00:06 +03:00
/* 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;
2006-02-12 02:00:06 +03:00
/* Call the card-specific watchdog routine. */
if (TOP_TRYLOCK(sc))
{
/* Remember link_state before updating it. */
sc->last_link_state = sc->status.link_state;
/* Update status.link_state. */
sc->card->watchdog(sc);
2006-02-12 02:00:06 +03:00
/* Increment a counter which tells user-land */
/* observers that SNMP state has been updated. */
sc->status.ticks++;
2006-02-12 02:00:06 +03:00
TOP_UNLOCK(sc);
2005-12-06 06:01:45 +03:00
}
else
sc->status.cntrs.lck_watch++;
2006-02-12 02:00:06 +03:00
/* Kernel date/time can take up to 5 seconds to start running. */
if ((sc->status.ticks > 3) && /* h/w should be stable by now */
(sc->status.cntrs.reset_time.tv_sec < 1000))
2005-12-06 06:01:45 +03:00
{
microtime(&sc->status.cntrs.reset_time);
if (sc->status.cntrs.reset_time.tv_sec > 1000)
reset_cntrs(sc);
2005-12-06 06:01:45 +03:00
}
/* Call the stack-specific watchdog routine. */
if (sc->stack)
sc->stack->watchdog(sc);
/* In case an interrupt gets lost, process tx and rx pkts */
lmc_interrupt(sc, sc->rxring.num_descs, 1);
2005-12-06 06:01:45 +03:00
}
/* Administrative status of the driver (UP or DOWN) has changed.
* A card-specific action is taken:
* HSSI: drop TA.
* (T3: send T3 idle ckt signal. )
* SSI: drop RTS, DTR and DCD
* (T1: disable line interface tx; )
2006-02-12 02:00:06 +03:00
*/
static void
set_ready(softc_t *sc, int status)
2005-12-06 06:01:45 +03:00
{
struct ioctl ioctl;
2005-12-06 06:01:45 +03:00
ioctl.cmd = IOCTL_SET_STATUS;
ioctl.data = status;
2005-12-06 06:01:45 +03:00
sc->card->ioctl(sc, &ioctl);
}
2005-12-06 06:01:45 +03:00
static void
reset_cntrs(softc_t *sc)
{
memset(&sc->status.cntrs, 0, sizeof(struct cntrs));
microtime(&sc->status.cntrs.reset_time);
}
2005-12-06 06:01:45 +03:00
static void /* context: process, softirq, interrupt! */
lmc_interrupt(void *arg, int quota, int check_status)
{
softc_t *sc = arg;
int activity;
2005-12-06 06:01:45 +03:00
/* Do this FIRST! Otherwise UPs deadlock and MPs spin. */
WRITE_CSR(sc, TLP_STATUS, READ_CSR(sc, TLP_STATUS));
2005-12-06 06:01:45 +03:00
/* If any CPU is inside this critical section, then */
/* other CPUs should go away without doing anything. */
if (BOTTOM_TRYLOCK(sc) == 0)
2006-02-12 02:00:06 +03:00
{
sc->status.cntrs.lck_intr++;
return;
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
/* In Linux, pci_alloc_consistent() means DMA */
/* descriptors do not 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);
2005-12-06 06:01:45 +03:00
}
#endif
/* This is the main loop for interrupt processing. */
sc->quota = quota;
do
{
activity = txintr_cleanup(sc);
activity += txintr_setup(sc);
activity += rxintr_cleanup(sc);
activity += rxintr_setup(sc);
} while (activity);
2005-12-06 06:01:45 +03:00
#if BSD
2005-12-06 06:01:45 +03:00
{
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
2006-02-12 02:00:06 +03:00
/* As the interrupt is dismissed, check for four unusual events. */
if (check_status) check_intr_status(sc);
2006-02-12 02:00:06 +03:00
BOTTOM_UNLOCK(sc);
2006-02-12 02:00:06 +03:00
}
/* 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
*/
2006-04-20 20:51:57 +04:00
static void
check_intr_status(softc_t *sc)
2006-04-20 20:51:57 +04:00
{
u_int32_t status, cfcs, op_mode;
u_int32_t missed, overruns;
/* 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(sc, TLP_STATUS);
if (status & TLP_STAT_FATAL_ERROR)
2006-04-20 20:51:57 +04:00
{
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);
2006-04-20 20:51:57 +04:00
}
2006-02-12 02:00:06 +03:00
/* 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)
{
op_mode = READ_CSR(sc, 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 not overflow */
WRITE_CSR(sc, 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(sc, TLP_STATUS) & TLP_STAT_TX_FSM); /* XXX HANG */
WRITE_CSR(sc, TLP_OP_MODE, op_mode); /* restart tx */
2006-02-12 02:00:06 +03:00
if (sc->config.debug)
printf("%s: tx fifo underrun; threshold now %d bytes\n",
NAME_UNIT, 128<<((op_mode>>TLP_OP_TR_SHIFT)&3));
sc->status.cntrs.underruns++;
}
}
2006-02-12 02:00:06 +03:00
/* 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(sc, TLP_MISSED);
if ((overruns = ((missed & TLP_MISS_OVERRUN)>>TLP_OVERRUN_SHIFT)))
2005-12-06 06:01:45 +03:00
{
if ((READ_PCI_CFG(sc, TLP_CFRV) & 0xFF) <= 0x22)
{
op_mode = READ_CSR(sc, TLP_OP_MODE);
WRITE_CSR(sc, 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(sc, TLP_STATUS) & TLP_STAT_RX_FSM); /* XXX HANG */
WRITE_CSR(sc, TLP_OP_MODE, op_mode); /* restart rx */
}
if (sc->config.debug)
printf("%s: rx fifo overruns=%d\n", NAME_UNIT, overruns);
sc->status.cntrs.overruns += overruns;
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
/* 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)))
{
if (sc->config.debug)
printf("%s: rx missed %d pkts\n", NAME_UNIT, missed);
sc->status.cntrs.missed += missed;
}
2006-02-12 02:00:06 +03:00
}
/* Initialize the driver. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
2006-02-12 02:00:06 +03:00
static int
lmc_attach(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
int error = 0;
struct config config;
2005-12-06 06:01:45 +03:00
/* Attach the Tulip PCI bus interface. */
if ((error = tulip_attach(sc))) return error;
2005-12-06 06:01:45 +03:00
/* Reset the Xilinx Field Programmable Gate Array. */
xilinx_reset(sc); /* side effect: turns on all four LEDs */
2006-02-12 02:00:06 +03:00
/* Attach card-specific stuff. */
sc->card->attach(sc, NULL); /* changes sc->config */
2005-12-06 06:01:45 +03:00
/* Reset the FIFOs between Gate array and Tulip chip. */
mii16_set_bits(sc, MII16_FIFO);
mii16_clr_bits(sc, MII16_FIFO);
2006-02-12 02:00:06 +03:00
#if IFNET
/* Attach the ifnet kernel interface. */
if ((error = ifnet_attach(sc))) return error;
#endif
2006-02-12 02:00:06 +03:00
#if NETDEV
/* Attach the netdevice kernel interface. */
if ((error = netdev_attach(sc))) return error;
#endif
2005-12-06 06:01:45 +03:00
/* Attach a protocol stack and open a line protocol. */
config = sc->config;
config.stack = STACK_RAWIP;
attach_stack(sc, &config);
config.proto = PROTO_IP_HDLC;
open_proto(sc, &config);
2005-12-06 06:01:45 +03:00
/* Print obscure card information. */
if (BOOT_VERBOSE)
{
u_int32_t cfrv = READ_PCI_CFG(sc, TLP_CFRV);
u_int16_t mii3 = mii_read(sc, 3);
u_int16_t srom[3];
u_int8_t *ieee = (u_int8_t *)srom;
int i;
2005-12-06 06:01:45 +03:00
printf("%s", NAME_UNIT);
printf(": PCI rev %d.%d", (cfrv>>4) & 0xF, cfrv & 0xF);
printf(", MII rev %d.%d", (mii3>>4) & 0xF, mii3 & 0xF);
for (i=0; i<3; i++) srom[i] = srom_read(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);
}
2005-12-06 06:01:45 +03:00
/* BSDs enable card interrupts and appear "ready" here. */
/* Linux does this in netdev_open(). */
#if BSD
set_ready(sc, 1);
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_TXRX);
#endif
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
return 0;
}
2005-12-06 06:01:45 +03:00
/* context: kernel (boot) or process (syscall) */
2006-02-12 02:00:06 +03:00
static void
lmc_detach(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
/* Disable card interrupts and appear "not ready". */
set_ready(sc, 0);
WRITE_CSR(sc, TLP_INT_ENBL, TLP_INT_DISABLE);
2005-12-06 06:01:45 +03:00
/* Detach the line protocol package. */
if (sc->stack)
sc->stack->detach(sc);
2005-12-06 06:01:45 +03:00
#if IFNET
/* Detach the ifnet kernel interface. */
ifnet_detach(sc);
#endif
2005-12-06 06:01:45 +03:00
#if NETDEV
/* Detach the netdevice kernel interface. */
netdev_detach(sc);
#endif
2005-12-06 06:01:45 +03:00
/* Detach framers, line interfaces, etc. on the card. */
sc->card->detach(sc);
2005-12-06 06:01:45 +03:00
/* Detach the Tulip PCI bus interface. */
tulip_detach(sc);
}
2005-12-06 06:01:45 +03:00
/* 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.
*/
static void
tulip_loop(softc_t *sc, struct config *config)
2006-02-12 02:00:06 +03:00
{
/* 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(sc, TLP_OP_MODE);
op_mode |= TLP_OP_INT_LOOP;
WRITE_CSR(sc, TLP_OP_MODE, op_mode);
config->crc_len = CFG_CRC_0;
}
2006-02-12 02:00:06 +03:00
/* Check for disabling loopback thru Tulip chip. */
if ((sc->config.loop_back == CFG_LOOP_TULIP) &&
(config->loop_back != CFG_LOOP_TULIP))
2005-12-06 06:01:45 +03:00
{
u_int32_t op_mode = READ_CSR(sc, TLP_OP_MODE);
op_mode &= ~TLP_OP_LOOP_MODE;
WRITE_CSR(sc, TLP_OP_MODE, op_mode);
config->crc_len = CFG_CRC_16;
2005-12-06 06:01:45 +03:00
}
sc->config.loop_back = config->loop_back;
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
/* Attach the Tulip PCI bus interface.
* Allocate DMA descriptors and enable DMA.
* Returns 0 on success; error code on failure.
* context: kernel (boot) or process (syscall)
*/
static int
tulip_attach(softc_t *sc)
2006-02-12 02:00:06 +03:00
{
int num_rx_descs, error = 0;
u_int32_t bus_pbl, bus_cal, op_tr;
u_int32_t cfdd, cfcs, cflt, csid, cfit;
2005-12-06 06:01:45 +03:00
/* Make sure the COMMAND bits are reasonable. */
cfcs = READ_PCI_CFG(sc, TLP_CFCS);
cfcs &= ~TLP_CFCS_MWI_ENABLE;
cfcs |= TLP_CFCS_BUS_MASTER;
cfcs |= TLP_CFCS_MEM_ENABLE;
cfcs |= TLP_CFCS_IO_ENABLE;
cfcs |= TLP_CFCS_PAR_ERROR;
cfcs |= TLP_CFCS_SYS_ERROR;
WRITE_PCI_CFG(sc, TLP_CFCS, cfcs);
2005-12-06 06:01:45 +03:00
/* Set the LATENCY TIMER to the recommended value, */
/* and make sure the CACHE LINE SIZE is reasonable. */
cfit = READ_PCI_CFG(sc, TLP_CFIT);
cflt = READ_PCI_CFG(sc, TLP_CFLT);
cflt &= ~TLP_CFLT_LATENCY;
cflt |= (cfit & TLP_CFIT_MAX_LAT)>>16;
/* "prgmbl burst length" and "cache alignment" used below. */
switch(cflt & TLP_CFLT_CACHE)
2006-02-12 02:00:06 +03:00
{
case 8: /* 8 bytes per cache line */
{ bus_pbl = 32; bus_cal = 1; break; }
case 16:
{ bus_pbl = 32; bus_cal = 2; break; }
case 32:
{ bus_pbl = 32; bus_cal = 3; break; }
2006-04-20 20:51:57 +04:00
default:
{
bus_pbl = 32; bus_cal = 1;
cflt &= ~TLP_CFLT_CACHE;
cflt |= 8;
break;
}
2006-02-12 02:00:06 +03:00
}
WRITE_PCI_CFG(sc, TLP_CFLT, cflt);
2005-12-06 06:01:45 +03:00
/* Make sure SNOOZE and SLEEP modes are disabled. */
cfdd = READ_PCI_CFG(sc, TLP_CFDD);
cfdd &= ~TLP_CFDD_SLEEP;
cfdd &= ~TLP_CFDD_SNOOZE;
WRITE_PCI_CFG(sc, TLP_CFDD, cfdd);
DELAY(11*1000); /* Tulip wakes up in 10 ms max */
2005-12-06 06:01:45 +03:00
/* Software Reset the Tulip chip; stops DMA and Interrupts. */
/* This does not change the PCI config regs just set above. */
WRITE_CSR(sc, TLP_BUS_MODE, TLP_BUS_RESET); /* self-clearing */
DELAY(5); /* Tulip is dead for 50 PCI cycles after reset. */
2005-12-06 06:01:45 +03:00
/* 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 do not enable it! */
WRITE_CSR(sc, TLP_BUS_MODE,
(bus_cal ? TLP_BUS_READ_LINE : 0) |
(bus_cal ? TLP_BUS_READ_MULT : 0) |
(bus_pbl<<TLP_BUS_PBL_SHIFT) |
(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 */
csid = READ_PCI_CFG(sc, TLP_CSID);
switch(csid)
2006-02-12 02:00:06 +03:00
{
case CSID_LMC_HSSI: /* 52 Mb/s */
case CSID_LMC_HSSIc: /* 52 Mb/s */
case CSID_LMC_T3: /* 45 Mb/s */
{ num_rx_descs = 48; op_tr = 2; break; }
case CSID_LMC_SSI: /* 10 Mb/s */
{ num_rx_descs = 32; op_tr = 1; break; }
case CSID_LMC_T1E1: /* 2 Mb/s */
{ num_rx_descs = 16; op_tr = 0; break; }
default:
{ num_rx_descs = 16; op_tr = 0; break; }
2006-02-12 02:00:06 +03:00
}
2005-12-06 06:01:45 +03:00
/* Create DMA descriptors and initialize list head registers. */
if ((error = create_ring(sc, &sc->txring, NUM_TX_DESCS))) return error;
WRITE_CSR(sc, TLP_TX_LIST, sc->txring.dma_addr);
if ((error = create_ring(sc, &sc->rxring, num_rx_descs))) return error;
WRITE_CSR(sc, TLP_RX_LIST, sc->rxring.dma_addr);
2005-12-06 06:01:45 +03:00
/* Initialize the operating mode register. */
WRITE_CSR(sc, TLP_OP_MODE, TLP_OP_INIT | (op_tr<<TLP_OP_TR_SHIFT));
/* Read the missed frame register (result ignored) to zero it. */
error = READ_CSR(sc, TLP_MISSED); /* error is used as a bit-dump */
/* Disable rx watchdog and tx jabber features. */
WRITE_CSR(sc, TLP_WDOG, TLP_WDOG_INIT);
return 0;
2005-12-06 06:01:45 +03:00
}
/* Detach the Tulip PCI bus interface. */
/* Disable DMA and free DMA descriptors. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
static void
tulip_detach(void *arg)
2005-12-06 06:01:45 +03:00
{
softc_t *sc = arg;
2005-12-06 06:01:45 +03:00
/* Software reset the Tulip chip; stops DMA and Interrupts. */
WRITE_CSR(sc, TLP_BUS_MODE, TLP_BUS_RESET); /* self-clearing */
DELAY(5); /* Tulip is dead for 50 PCI cycles after reset. */
2005-12-06 06:01:45 +03:00
/* 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);
2005-12-06 06:01:45 +03:00
/* Free the DMA descriptor rings. */
destroy_ring(sc, &sc->txring);
destroy_ring(sc, &sc->rxring);
}
/* Called during config probing -- softc does not yet exist. */
static void
print_driver_info(void)
{
/* Print driver information once only. */
if (driver_announced++ == 0)
{
printf("LMC driver version %d/%d/%d; options",
VER_YEAR, VER_MONTH, VER_DAY);
if (ALTQ) printf(" ALTQ");
printf(" BPF"); /* always defined */
if (NAPI) printf(" NAPI");
if (DEVICE_POLLING) printf(" POLL");
if (P2P) printf(" P2P");
if (SPPP) printf(" SPPP");
if (GEN_HDLC) printf(" GEN_HDLC");
if (SYNC_PPP) printf(" SYNC_PPP");
if (NETGRAPH) printf(" NETGRAPH");
printf(".\n");
}
2005-12-06 06:01:45 +03:00
}
/* This is the I/O configuration interface for NetBSD. */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Looking for a DEC 21140A chip on any Lan Media Corp card. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
2005-12-06 06:01:45 +03:00
static int
nbsd_match(struct device *parent, cfdata_t match,
void *aux)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
struct pci_attach_args *pa = aux;
u_int32_t cfid = pci_conf_read(pa->pa_pc, pa->pa_tag, TLP_CFID);
2006-04-20 20:51:57 +04:00
u_int32_t csid = pci_conf_read(pa->pa_pc, pa->pa_tag, TLP_CSID);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
if (cfid != TLP_CFID_TULIP) return 0;
switch (csid)
2005-12-06 06:01:45 +03:00
{
2006-04-20 20:51:57 +04:00
case CSID_LMC_HSSI:
case CSID_LMC_HSSIc:
case CSID_LMC_T3:
case CSID_LMC_SSI:
case CSID_LMC_T1E1:
print_driver_info();
return 100;
2006-02-12 02:00:06 +03:00
default:
return 0;
}
}
2005-12-06 06:01:45 +03:00
/* NetBSD bottom-half initialization. */
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
2006-02-12 02:00:06 +03:00
static void
nbsd_attach(struct device *parent, struct device *self, void *aux)
2006-02-12 02:00:06 +03:00
{
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;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* for READ/WRITE_PCI_CFG() */
sc->pa_pc = pa->pa_pc;
sc->pa_tag = pa->pa_tag;
sc->pa_dmat = pa->pa_dmat;
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* What kind of card are we driving? */
switch (READ_PCI_CFG(sc, TLP_CSID))
{
2006-04-20 20:51:57 +04:00
case CSID_LMC_HSSI:
case CSID_LMC_HSSIc:
2006-02-12 02:00:06 +03:00
sc->dev_desc = HSSI_DESC;
sc->card = &hssi_card;
break;
2006-04-20 20:51:57 +04:00
case CSID_LMC_T3:
2006-02-12 02:00:06 +03:00
sc->dev_desc = T3_DESC;
sc->card = &t3_card;
break;
2006-04-20 20:51:57 +04:00
case CSID_LMC_SSI:
2006-02-12 02:00:06 +03:00
sc->dev_desc = SSI_DESC;
sc->card = &ssi_card;
break;
2006-04-20 20:51:57 +04:00
case CSID_LMC_T1E1:
2006-02-12 02:00:06 +03:00
sc->dev_desc = T1E1_DESC;
sc->card = &t1_card;
break;
default:
return;
2005-12-06 06:01:45 +03:00
}
2006-02-12 02:00:06 +03:00
/* 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)))
{
aprint_error("%s: bus_space_map(): error %d\n", NAME_UNIT, error);
2006-02-12 02:00:06 +03:00
return;
}
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Allocate PCI interrupt resources. */
2006-04-20 20:51:57 +04:00
if (pci_intr_map(pa, &sc->intr_handle))
2006-02-12 02:00:06 +03:00
{
aprint_error("%s: pci_intr_map() failed\n", NAME_UNIT);
nbsd_detach(self, 0);
2006-02-12 02:00:06 +03:00
return;
}
2006-04-20 20:51:57 +04:00
if ((sc->irq_cookie = pci_intr_establish(pa->pa_pc, sc->intr_handle,
IPL_NET, bsd_interrupt, sc)) == NULL)
2006-02-12 02:00:06 +03:00
{
aprint_error("%s: pci_intr_establish() failed\n", NAME_UNIT);
nbsd_detach(self, 0);
2006-02-12 02:00:06 +03:00
return;
}
intrstr = pci_intr_string(pa->pa_pc, sc->intr_handle);
aprint_normal(" %s: %s\n", intrstr, sc->dev_desc);
aprint_naive(": %s\n", sc->dev_desc);
2006-02-12 02:00:06 +03:00
/* Install a shutdown hook. */
2006-04-20 20:51:57 +04:00
if ((sc->sdh_cookie = shutdownhook_establish(tulip_detach, sc)) == NULL)
2006-02-12 02:00:06 +03:00
{
aprint_error("%s: shutdown_hook_establish() failed\n", NAME_UNIT);
nbsd_detach(self, 0);
2006-02-12 02:00:06 +03:00
return;
}
/* Initialize the top-half and bottom-half locks. */
mutex_init(&sc->top_lock, MUTEX_DEFAULT, IPL_VM);
2006-04-20 20:51:57 +04:00
__cpu_simple_lock_init(&sc->bottom_lock);
2006-02-12 02:00:06 +03:00
2006-04-20 20:51:57 +04:00
/* Initialize the driver. */
if ((error = lmc_attach(sc))) nbsd_detach(self, 0);
2005-12-06 06:01:45 +03:00
}
2006-04-20 20:51:57 +04:00
/* context: kernel (boot) or process (syscall) */
2006-02-12 02:00:06 +03:00
static int
nbsd_detach(struct device *self, int flags)
2005-12-06 06:01:45 +03:00
{
2006-02-12 02:00:06 +03:00
softc_t *sc = (softc_t *)self; /* device is first in softc */
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
/* Detach from the bus and the kernel. */
lmc_detach(sc);
/* Release resources. */
if (sc->sdh_cookie)
shutdownhook_disestablish(sc->sdh_cookie);
if (sc->irq_cookie)
pci_intr_disestablish(sc->pa_pc, sc->irq_cookie);
if (sc->csr_handle)
bus_space_unmap(sc->csr_tag, sc->csr_handle, TLP_CSR_SIZE);
/* Destroy locks. */
mutex_destroy(&sc->top_lock);
2006-02-12 02:00:06 +03:00
return 0;
2005-12-06 06:01:45 +03:00
}
CFATTACH_DECL(lmc, sizeof(softc_t), /* lmc_ca */
nbsd_match, nbsd_attach, nbsd_detach, NULL);
2005-12-06 06:01:45 +03:00
2006-02-12 02:00:06 +03:00
2005-12-06 06:01:45 +03:00