mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-20 11:11:24 +00:00
46783fb897
This means that we will not have to have a bpf and a non-bpf version of our driver modules. This does not open any security hole, because the bpf core isn't loadable The drivers left unchanged are the "cross platform" drivers where the respective maintainers are urged to DTRT, whatever that may be. Add a couple of missing FreeBSD tags.
2026 lines
52 KiB
C
2026 lines
52 KiB
C
/*-
|
|
* Copyright (c) 1994-1998
|
|
* Paul Richards. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer,
|
|
* verbatim and that no modifications are made prior to this
|
|
* point in the file.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Paul Richards.
|
|
* 4. The name Paul Richards may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``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 PAUL RICHARDS 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.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
#define DIAGNOSTIC
|
|
#define DEBUG
|
|
*
|
|
* TODO ----
|
|
*
|
|
* This driver will need bounce buffer support when dma'ing to mbufs above the
|
|
* 16Mb mark.
|
|
*
|
|
* Check all the XXX comments -- some of them are just things I've left
|
|
* unfinished rather than "difficult" problems that were hacked around.
|
|
*
|
|
* Check log settings.
|
|
*
|
|
* Check how all the arpcom flags get set and used.
|
|
*
|
|
* Re-inline and re-static all routines after debugging.
|
|
*
|
|
* Remember to assign iobase in SHMEM probe routines.
|
|
*
|
|
* Replace all occurences of LANCE-controller-card etc in prints by the name
|
|
* strings of the appropriate type -- nifty window dressing
|
|
*
|
|
* Add DEPCA support -- mostly done.
|
|
*
|
|
*/
|
|
|
|
#include "pci.h"
|
|
#include "lnc.h"
|
|
#if NLNC > 0
|
|
|
|
#include "opt_inet.h"
|
|
|
|
/* Some defines that should really be in generic locations */
|
|
#define FCS_LEN 4
|
|
#define MULTICAST_FILTER_LEN 8
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syslog.h>
|
|
|
|
#include <net/ethernet.h>
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_types.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#include "opt_bdg.h"
|
|
#ifdef BRIDGE
|
|
#include <net/bridge.h>
|
|
#endif
|
|
|
|
#ifdef PC98
|
|
#include <machine/clock.h>
|
|
#endif
|
|
#include <machine/md_var.h>
|
|
|
|
#include <i386/isa/isa_device.h>
|
|
#include <i386/isa/if_lnc.h>
|
|
|
|
struct lnc_softc {
|
|
struct arpcom arpcom; /* see ../../net/if_arp.h */
|
|
struct nic_info nic; /* NIC specific info */
|
|
int nrdre;
|
|
struct host_ring_entry *recv_ring; /* start of alloc'd mem */
|
|
int recv_next;
|
|
int ntdre;
|
|
struct host_ring_entry *trans_ring;
|
|
int trans_next;
|
|
struct init_block *init_block; /* Initialisation block */
|
|
int pending_transmits; /* No. of transmit descriptors in use */
|
|
int next_to_send;
|
|
struct mbuf *mbufs;
|
|
int mbuf_count;
|
|
int flags;
|
|
int rap;
|
|
int rdp;
|
|
int bdp;
|
|
#ifdef DEBUG
|
|
int lnc_debug;
|
|
#endif
|
|
LNCSTATS_STRUCT
|
|
};
|
|
|
|
static struct lnc_softc lnc_softc[NLNC];
|
|
|
|
static char const * const nic_ident[] = {
|
|
"Unknown",
|
|
"BICC",
|
|
"NE2100",
|
|
"DEPCA",
|
|
"CNET98S", /* PC-98 */
|
|
};
|
|
|
|
static char const * const ic_ident[] = {
|
|
"Unknown",
|
|
"LANCE",
|
|
"C-LANCE",
|
|
"PCnet-ISA",
|
|
"PCnet-ISA+",
|
|
"PCnet-ISA II",
|
|
"PCnet-32 VL-Bus",
|
|
"PCnet-PCI",
|
|
"PCnet-PCI II",
|
|
"PCnet-FAST",
|
|
"PCnet-FAST+",
|
|
"PCnet-Home",
|
|
};
|
|
|
|
static void lnc_setladrf __P((struct lnc_softc *sc));
|
|
static void lnc_stop __P((struct lnc_softc *sc));
|
|
static void lnc_reset __P((struct lnc_softc *sc));
|
|
static void lnc_free_mbufs __P((struct lnc_softc *sc));
|
|
static __inline int alloc_mbuf_cluster __P((struct lnc_softc *sc,
|
|
struct host_ring_entry *desc));
|
|
static __inline struct mbuf *chain_mbufs __P((struct lnc_softc *sc,
|
|
int start_of_packet,
|
|
int pkt_len));
|
|
static __inline struct mbuf *mbuf_packet __P((struct lnc_softc *sc,
|
|
int start_of_packet,
|
|
int pkt_len));
|
|
static __inline void lnc_rint __P((struct lnc_softc *sc));
|
|
static __inline void lnc_tint __P((struct lnc_softc *sc));
|
|
static int lnc_probe __P((struct isa_device *isa_dev));
|
|
#ifdef PC98
|
|
static int cnet98s_probe __P((struct lnc_softc *sc, unsigned iobase));
|
|
#endif
|
|
static int ne2100_probe __P((struct lnc_softc *sc, unsigned iobase));
|
|
static int bicc_probe __P((struct lnc_softc *sc, unsigned iobase));
|
|
static int dec_macaddr_extract __P((u_char ring[], struct lnc_softc *sc));
|
|
static int depca_probe __P((struct lnc_softc *sc, unsigned iobase));
|
|
static int lance_probe __P((struct lnc_softc *sc));
|
|
static int pcnet_probe __P((struct lnc_softc *sc));
|
|
static int lnc_attach_sc __P((struct lnc_softc *sc, int unit));
|
|
static int lnc_attach __P((struct isa_device *isa_dev));
|
|
static void lnc_init __P((void *));
|
|
static ointhand2_t lncintr;
|
|
static __inline int mbuf_to_buffer __P((struct mbuf *m, char *buffer));
|
|
static __inline struct mbuf *chain_to_cluster __P((struct mbuf *m));
|
|
static void lnc_start __P((struct ifnet *ifp));
|
|
static int lnc_ioctl __P((struct ifnet *ifp, u_long command, caddr_t data));
|
|
static void lnc_watchdog __P((struct ifnet *ifp));
|
|
#ifdef DEBUG
|
|
void lnc_dump_state __P((struct lnc_softc *sc));
|
|
void mbuf_dump_chain __P((struct mbuf *m));
|
|
#endif
|
|
|
|
#if NPCI > 0
|
|
void *lnc_attach_ne2100_pci __P((int unit, unsigned iobase));
|
|
#endif
|
|
void lncintr_sc __P((struct lnc_softc *sc));
|
|
|
|
struct isa_driver lncdriver = {lnc_probe, lnc_attach, "lnc"};
|
|
|
|
static __inline void
|
|
write_csr(struct lnc_softc *sc, u_short port, u_short val)
|
|
{
|
|
outw(sc->rap, port);
|
|
outw(sc->rdp, val);
|
|
}
|
|
|
|
static __inline u_short
|
|
read_csr(struct lnc_softc *sc, u_short port)
|
|
{
|
|
outw(sc->rap, port);
|
|
return (inw(sc->rdp));
|
|
}
|
|
|
|
static __inline void
|
|
write_bcr(struct lnc_softc *sc, u_short port, u_short val)
|
|
{
|
|
outw(sc->rap, port);
|
|
outw(sc->bdp, val);
|
|
}
|
|
|
|
static __inline u_short
|
|
read_bcr(struct lnc_softc *sc, u_short port)
|
|
{
|
|
outw(sc->rap, port);
|
|
return (inw(sc->bdp));
|
|
}
|
|
|
|
static __inline u_long
|
|
ether_crc(const u_char *ether_addr)
|
|
{
|
|
#define POLYNOMIAL 0xEDB88320UL
|
|
u_char i, j, addr;
|
|
u_int crc = 0xFFFFFFFFUL;
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
addr = *ether_addr++;
|
|
for (j = 0; j < MULTICAST_FILTER_LEN; j++) {
|
|
crc = (crc >> 1) ^ (((crc ^ addr) & 1) ? POLYNOMIAL : 0);
|
|
addr >>= 1;
|
|
}
|
|
}
|
|
return crc;
|
|
#undef POLYNOMIAL
|
|
}
|
|
|
|
/*
|
|
* Set up the logical address filter for multicast packets
|
|
*/
|
|
static __inline void
|
|
lnc_setladrf(struct lnc_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
struct ifmultiaddr *ifma;
|
|
u_long index;
|
|
int i;
|
|
|
|
if (sc->flags & IFF_ALLMULTI) {
|
|
for (i=0; i < MULTICAST_FILTER_LEN; i++)
|
|
sc->init_block->ladrf[i] = 0xFF;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* For each multicast address, calculate a crc for that address and
|
|
* then use the high order 6 bits of the crc as a hash code where
|
|
* bits 3-5 select the byte of the address filter and bits 0-2 select
|
|
* the bit within that byte.
|
|
*/
|
|
|
|
bzero(sc->init_block->ladrf, MULTICAST_FILTER_LEN);
|
|
for (ifma = ifp->if_multiaddrs.lh_first; ifma;
|
|
ifma = ifma->ifma_link.le_next) {
|
|
if (ifma->ifma_addr->sa_family != AF_LINK)
|
|
continue;
|
|
|
|
index = ether_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr))
|
|
>> 26;
|
|
sc->init_block->ladrf[index >> 3] |= 1 << (index & 7);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lnc_stop(struct lnc_softc *sc)
|
|
{
|
|
write_csr(sc, CSR0, STOP);
|
|
}
|
|
|
|
static void
|
|
lnc_reset(struct lnc_softc *sc)
|
|
{
|
|
lnc_init(sc);
|
|
}
|
|
|
|
static void
|
|
lnc_free_mbufs(struct lnc_softc *sc)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* We rely on other routines to keep the buff.mbuf field valid. If
|
|
* it's not NULL then we assume it points to an allocated mbuf.
|
|
*/
|
|
|
|
for (i = 0; i < NDESC(sc->nrdre); i++)
|
|
if ((sc->recv_ring + i)->buff.mbuf)
|
|
m_free((sc->recv_ring + i)->buff.mbuf);
|
|
|
|
for (i = 0; i < NDESC(sc->ntdre); i++)
|
|
if ((sc->trans_ring + i)->buff.mbuf)
|
|
m_free((sc->trans_ring + i)->buff.mbuf);
|
|
|
|
if (sc->mbuf_count)
|
|
m_freem(sc->mbufs);
|
|
}
|
|
|
|
static __inline int
|
|
alloc_mbuf_cluster(struct lnc_softc *sc, struct host_ring_entry *desc)
|
|
{
|
|
register struct mds *md = desc->md;
|
|
struct mbuf *m=0;
|
|
int addr;
|
|
|
|
/* Try and get cluster off local cache */
|
|
if (sc->mbuf_count) {
|
|
sc->mbuf_count--;
|
|
m = sc->mbufs;
|
|
sc->mbufs = m->m_next;
|
|
/* XXX m->m_data = m->m_ext.ext_buf;*/
|
|
} else {
|
|
MGET(m, M_DONTWAIT, MT_DATA);
|
|
if (!m)
|
|
return(1);
|
|
MCLGET(m, M_DONTWAIT);
|
|
if (!m->m_ext.ext_buf) {
|
|
m_free(m);
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
desc->buff.mbuf = m;
|
|
addr = kvtop(m->m_data);
|
|
md->md0 = addr;
|
|
md->md1= ((addr >> 16) & 0xff) | OWN;
|
|
md->md2 = -(short)(MCLBYTES - sizeof(struct pkthdr));
|
|
md->md3 = 0;
|
|
return(0);
|
|
}
|
|
|
|
static __inline struct mbuf *
|
|
chain_mbufs(struct lnc_softc *sc, int start_of_packet, int pkt_len)
|
|
{
|
|
struct mbuf *head, *m;
|
|
struct host_ring_entry *desc;
|
|
|
|
/*
|
|
* Turn head into a pkthdr mbuf --
|
|
* assumes a pkthdr type mbuf was
|
|
* allocated to the descriptor
|
|
* originally.
|
|
*/
|
|
|
|
desc = sc->recv_ring + start_of_packet;
|
|
|
|
head = desc->buff.mbuf;
|
|
head->m_flags |= M_PKTHDR;
|
|
|
|
m = head;
|
|
do {
|
|
m = desc->buff.mbuf;
|
|
m->m_len = min((MCLBYTES - sizeof(struct pkthdr)), pkt_len);
|
|
pkt_len -= m->m_len;
|
|
if (alloc_mbuf_cluster(sc, desc))
|
|
return((struct mbuf *)NULL);
|
|
INC_MD_PTR(start_of_packet, sc->nrdre)
|
|
desc = sc->recv_ring + start_of_packet;
|
|
m->m_next = desc->buff.mbuf;
|
|
} while (start_of_packet != sc->recv_next);
|
|
|
|
m->m_next = 0;
|
|
return(head);
|
|
}
|
|
|
|
static __inline struct mbuf *
|
|
mbuf_packet(struct lnc_softc *sc, int start_of_packet, int pkt_len)
|
|
{
|
|
|
|
struct host_ring_entry *start;
|
|
struct mbuf *head,*m,*m_prev;
|
|
char *data,*mbuf_data;
|
|
short blen;
|
|
int amount;
|
|
|
|
/* Get a pkthdr mbuf for the start of packet */
|
|
MGETHDR(head, M_DONTWAIT, MT_DATA);
|
|
if (!head) {
|
|
LNCSTATS(drop_packet)
|
|
return(0);
|
|
}
|
|
|
|
m = head;
|
|
m->m_len = 0;
|
|
start = sc->recv_ring + start_of_packet;
|
|
/*blen = -(start->md->md2);*/
|
|
blen = RECVBUFSIZE; /* XXX More PCnet-32 crap */
|
|
data = start->buff.data;
|
|
mbuf_data = m->m_data;
|
|
|
|
while (start_of_packet != sc->recv_next) {
|
|
/*
|
|
* If the data left fits in a single buffer then set
|
|
* blen to the size of the data left.
|
|
*/
|
|
if (pkt_len < blen)
|
|
blen = pkt_len;
|
|
|
|
/*
|
|
* amount is least of data in current ring buffer and
|
|
* amount of space left in current mbuf.
|
|
*/
|
|
amount = min(blen, M_TRAILINGSPACE(m));
|
|
if (amount == 0) {
|
|
/* mbuf must be empty */
|
|
m_prev = m;
|
|
MGET(m, M_DONTWAIT, MT_DATA);
|
|
if (!m) {
|
|
m_freem(head);
|
|
return(0);
|
|
}
|
|
if (pkt_len >= MINCLSIZE)
|
|
MCLGET(m, M_DONTWAIT);
|
|
m->m_len = 0;
|
|
m_prev->m_next = m;
|
|
amount = min(blen, M_TRAILINGSPACE(m));
|
|
mbuf_data = m->m_data;
|
|
}
|
|
bcopy(data, mbuf_data, amount);
|
|
blen -= amount;
|
|
pkt_len -= amount;
|
|
m->m_len += amount;
|
|
data += amount;
|
|
mbuf_data += amount;
|
|
|
|
if (blen == 0) {
|
|
start->md->md1 &= HADR;
|
|
start->md->md1 |= OWN;
|
|
start->md->md2 = -RECVBUFSIZE; /* XXX - shouldn't be necessary */
|
|
INC_MD_PTR(start_of_packet, sc->nrdre)
|
|
start = sc->recv_ring + start_of_packet;
|
|
data = start->buff.data;
|
|
/*blen = -(start->md->md2);*/
|
|
blen = RECVBUFSIZE; /* XXX More PCnet-32 crap */
|
|
}
|
|
}
|
|
return(head);
|
|
}
|
|
|
|
|
|
static __inline void
|
|
lnc_rint(struct lnc_softc *sc)
|
|
{
|
|
struct host_ring_entry *next, *start;
|
|
int start_of_packet;
|
|
struct mbuf *head;
|
|
struct ether_header *eh;
|
|
int lookahead;
|
|
int flags;
|
|
int pkt_len;
|
|
|
|
/*
|
|
* The LANCE will issue a RINT interrupt when the ownership of the
|
|
* last buffer of a receive packet has been relinquished by the LANCE.
|
|
* Therefore, it can be assumed that a complete packet can be found
|
|
* before hitting buffers that are still owned by the LANCE, if not
|
|
* then there is a bug in the driver that is causing the descriptors
|
|
* to get out of sync.
|
|
*/
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if ((sc->recv_ring + sc->recv_next)->md->md1 & OWN) {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: Receive interrupt with buffer still owned by controller -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
if (!((sc->recv_ring + sc->recv_next)->md->md1 & STP)) {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: Receive interrupt but not start of packet -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
lookahead = 0;
|
|
next = sc->recv_ring + sc->recv_next;
|
|
while ((flags = next->md->md1) & STP) {
|
|
|
|
/* Make a note of the start of the packet */
|
|
start_of_packet = sc->recv_next;
|
|
|
|
/*
|
|
* Find the end of the packet. Even if not data chaining,
|
|
* jabber packets can overrun into a second descriptor.
|
|
* If there is no error, then the ENP flag is set in the last
|
|
* descriptor of the packet. If there is an error then the ERR
|
|
* flag will be set in the descriptor where the error occured.
|
|
* Therefore, to find the last buffer of a packet we search for
|
|
* either ERR or ENP.
|
|
*/
|
|
|
|
if (!(flags & (ENP | MDERR))) {
|
|
do {
|
|
INC_MD_PTR(sc->recv_next, sc->nrdre)
|
|
next = sc->recv_ring + sc->recv_next;
|
|
flags = next->md->md1;
|
|
} while (!(flags & (STP | OWN | ENP | MDERR)));
|
|
|
|
if (flags & STP) {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: Start of packet found before end of previous in receive ring -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
if (flags & OWN) {
|
|
if (lookahead) {
|
|
/*
|
|
* Looked ahead into a packet still
|
|
* being received
|
|
*/
|
|
sc->recv_next = start_of_packet;
|
|
break;
|
|
} else {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: End of received packet not found-- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
pkt_len = (next->md->md3 & MCNT) - FCS_LEN;
|
|
|
|
/* Move pointer onto start of next packet */
|
|
INC_MD_PTR(sc->recv_next, sc->nrdre)
|
|
next = sc->recv_ring + sc->recv_next;
|
|
|
|
if (flags & MDERR) {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
if (flags & RBUFF) {
|
|
LNCSTATS(rbuff)
|
|
log(LOG_ERR, "lnc%d: Receive buffer error\n", unit);
|
|
}
|
|
if (flags & OFLO) {
|
|
/* OFLO only valid if ENP is not set */
|
|
if (!(flags & ENP)) {
|
|
LNCSTATS(oflo)
|
|
log(LOG_ERR, "lnc%d: Receive overflow error \n", unit);
|
|
}
|
|
} else if (flags & ENP) {
|
|
if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC)==0) {
|
|
/*
|
|
* FRAM and CRC are valid only if ENP
|
|
* is set and OFLO is not.
|
|
*/
|
|
if (flags & FRAM) {
|
|
LNCSTATS(fram)
|
|
log(LOG_ERR, "lnc%d: Framing error\n", unit);
|
|
/*
|
|
* FRAM is only set if there's a CRC
|
|
* error so avoid multiple messages
|
|
*/
|
|
} else if (flags & CRC) {
|
|
LNCSTATS(crc)
|
|
log(LOG_ERR, "lnc%d: Receive CRC error\n", unit);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Drop packet */
|
|
LNCSTATS(rerr)
|
|
sc->arpcom.ac_if.if_ierrors++;
|
|
while (start_of_packet != sc->recv_next) {
|
|
start = sc->recv_ring + start_of_packet;
|
|
start->md->md2 = -RECVBUFSIZE; /* XXX - shouldn't be necessary */
|
|
start->md->md1 &= HADR;
|
|
start->md->md1 |= OWN;
|
|
INC_MD_PTR(start_of_packet, sc->nrdre)
|
|
}
|
|
} else { /* Valid packet */
|
|
|
|
sc->arpcom.ac_if.if_ipackets++;
|
|
|
|
|
|
if (sc->nic.mem_mode == DMA_MBUF)
|
|
head = chain_mbufs(sc, start_of_packet, pkt_len);
|
|
else
|
|
head = mbuf_packet(sc, start_of_packet, pkt_len);
|
|
|
|
if (head) {
|
|
/*
|
|
* First mbuf in packet holds the
|
|
* ethernet and packet headers
|
|
*/
|
|
head->m_pkthdr.rcvif = &sc->arpcom.ac_if;
|
|
head->m_pkthdr.len = pkt_len ;
|
|
|
|
/*
|
|
* BPF expects the ether header to be in the first
|
|
* mbuf of the chain so point eh at the right place
|
|
* but don't increment the mbuf pointers before
|
|
* the bpf tap.
|
|
*/
|
|
|
|
eh = (struct ether_header *) head->m_data;
|
|
|
|
if (sc->arpcom.ac_if.if_bpf)
|
|
bpf_mtap(&sc->arpcom.ac_if, head);
|
|
#ifdef BRIDGE
|
|
if (do_bridge) {
|
|
struct ifnet *bdg_ifp ;
|
|
|
|
bdg_ifp = bridge_in(head);
|
|
if (bdg_ifp == BDG_DROP)
|
|
m_freem(head);
|
|
else {
|
|
if (bdg_ifp != BDG_LOCAL)
|
|
bdg_forward(&head, bdg_ifp);
|
|
if ( bdg_ifp == BDG_LOCAL ||
|
|
bdg_ifp == BDG_BCAST ||
|
|
bdg_ifp == BDG_MCAST )
|
|
goto getit;
|
|
else if (head)
|
|
m_freem(head);
|
|
}
|
|
} else
|
|
#endif
|
|
/* Check this packet is really for us */
|
|
|
|
if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
|
|
!(eh->ether_dhost[0] & 1) && /* Broadcast and multicast */
|
|
(bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
|
|
sizeof(eh->ether_dhost))))
|
|
m_freem(head);
|
|
else
|
|
{
|
|
#ifdef BRIDGE
|
|
getit:
|
|
#endif
|
|
/* Skip over the ether header */
|
|
head->m_data += sizeof *eh;
|
|
head->m_len -= sizeof *eh;
|
|
head->m_pkthdr.len -= sizeof *eh;
|
|
|
|
ether_input(&sc->arpcom.ac_if, eh, head);
|
|
}
|
|
|
|
} else {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR,"lnc%d: Packet dropped, no mbufs\n",unit);
|
|
LNCSTATS(drop_packet)
|
|
}
|
|
}
|
|
|
|
lookahead++;
|
|
}
|
|
|
|
/*
|
|
* At this point all completely received packets have been processed
|
|
* so clear RINT since any packets that have arrived while we were in
|
|
* here have been dealt with.
|
|
*/
|
|
|
|
outw(sc->rdp, RINT | INEA);
|
|
}
|
|
|
|
static __inline void
|
|
lnc_tint(struct lnc_softc *sc)
|
|
{
|
|
struct host_ring_entry *next, *start;
|
|
int start_of_packet;
|
|
int lookahead;
|
|
|
|
/*
|
|
* If the driver is reset in this routine then we return immediately to
|
|
* the interrupt driver routine. Any interrupts that have occured
|
|
* since the reset will be dealt with there. sc->trans_next
|
|
* should point to the start of the first packet that was awaiting
|
|
* transmission after the last transmit interrupt was dealt with. The
|
|
* LANCE should have relinquished ownership of that descriptor before
|
|
* the interrupt. Therefore, sc->trans_next should point to a
|
|
* descriptor with STP set and OWN cleared. If not then the driver's
|
|
* pointers are out of sync with the LANCE, which signifies a bug in
|
|
* the driver. Therefore, the following two checks are really
|
|
* diagnostic, since if the driver is working correctly they should
|
|
* never happen.
|
|
*/
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if ((sc->trans_ring + sc->trans_next)->md->md1 & OWN) {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: Transmit interrupt with buffer still owned by controller -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* The LANCE will write the status information for the packet it just
|
|
* tried to transmit in one of two places. If the packet was
|
|
* transmitted successfully then the status will be written into the
|
|
* last descriptor of the packet. If the transmit failed then the
|
|
* status will be written into the descriptor that was being accessed
|
|
* when the error occured and all subsequent descriptors in that
|
|
* packet will have been relinquished by the LANCE.
|
|
*
|
|
* At this point we know that sc->trans_next points to the start
|
|
* of a packet that the LANCE has just finished trying to transmit.
|
|
* We now search for a buffer with either ENP or ERR set.
|
|
*/
|
|
|
|
lookahead = 0;
|
|
|
|
do {
|
|
start_of_packet = sc->trans_next;
|
|
next = sc->trans_ring + sc->trans_next;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (!(next->md->md1 & STP)) {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: Transmit interrupt but not start of packet -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Find end of packet.
|
|
*/
|
|
|
|
if (!(next->md->md1 & (ENP | MDERR))) {
|
|
do {
|
|
INC_MD_PTR(sc->trans_next, sc->ntdre)
|
|
next = sc->trans_ring + sc->trans_next;
|
|
} while (!(next->md->md1 & (STP | OWN | ENP | MDERR)));
|
|
|
|
if (next->md->md1 & STP) {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: Start of packet found before end of previous in transmit ring -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
if (next->md->md1 & OWN) {
|
|
if (lookahead) {
|
|
/*
|
|
* Looked ahead into a packet still
|
|
* being transmitted
|
|
*/
|
|
sc->trans_next = start_of_packet;
|
|
break;
|
|
} else {
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
log(LOG_ERR, "lnc%d: End of transmitted packet not found -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Check for ERR first since other flags are irrelevant if an
|
|
* error occurred.
|
|
*/
|
|
if (next->md->md1 & MDERR) {
|
|
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
|
|
LNCSTATS(terr)
|
|
sc->arpcom.ac_if.if_oerrors++;
|
|
|
|
if (next->md->md3 & LCOL) {
|
|
LNCSTATS(lcol)
|
|
log(LOG_ERR, "lnc%d: Transmit late collision -- Net error?\n", unit);
|
|
sc->arpcom.ac_if.if_collisions++;
|
|
/*
|
|
* Clear TBUFF since it's not valid when LCOL
|
|
* set
|
|
*/
|
|
next->md->md3 &= ~TBUFF;
|
|
}
|
|
if (next->md->md3 & LCAR) {
|
|
LNCSTATS(lcar)
|
|
log(LOG_ERR, "lnc%d: Loss of carrier during transmit -- Net error?\n", unit);
|
|
}
|
|
if (next->md->md3 & RTRY) {
|
|
LNCSTATS(rtry)
|
|
log(LOG_ERR, "lnc%d: Transmit of packet failed after 16 attempts -- TDR = %d\n", unit, ((sc->trans_ring + sc->trans_next)->md->md3 & TDR));
|
|
sc->arpcom.ac_if.if_collisions += 16;
|
|
/*
|
|
* Clear TBUFF since it's not valid when RTRY
|
|
* set
|
|
*/
|
|
next->md->md3 &= ~TBUFF;
|
|
}
|
|
/*
|
|
* TBUFF is only valid if neither LCOL nor RTRY are set.
|
|
* We need to check UFLO after LCOL and RTRY so that we
|
|
* know whether or not TBUFF is valid. If either are
|
|
* set then TBUFF will have been cleared above. A
|
|
* UFLO error will turn off the transmitter so we
|
|
* have to reset.
|
|
*
|
|
*/
|
|
|
|
if (next->md->md3 & UFLO) {
|
|
LNCSTATS(uflo)
|
|
/*
|
|
* If an UFLO has occured it's possibly due
|
|
* to a TBUFF error
|
|
*/
|
|
if (next->md->md3 & TBUFF) {
|
|
LNCSTATS(tbuff)
|
|
log(LOG_ERR, "lnc%d: Transmit buffer error -- Resetting\n", unit);
|
|
} else
|
|
log(LOG_ERR, "lnc%d: Transmit underflow error -- Resetting\n", unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
do {
|
|
INC_MD_PTR(sc->trans_next, sc->ntdre)
|
|
next = sc->trans_ring + sc->trans_next;
|
|
} while (!(next->md->md1 & STP) && (sc->trans_next != sc->next_to_send));
|
|
|
|
} else {
|
|
/*
|
|
* Since we check for ERR first then if we get here
|
|
* the packet was transmitted correctly. There may
|
|
* still have been non-fatal errors though.
|
|
* Don't bother checking for DEF, waste of time.
|
|
*/
|
|
|
|
sc->arpcom.ac_if.if_opackets++;
|
|
|
|
if (next->md->md1 & MORE) {
|
|
LNCSTATS(more)
|
|
sc->arpcom.ac_if.if_collisions += 2;
|
|
}
|
|
|
|
/*
|
|
* ONE is invalid if LCOL is set. If LCOL was set then
|
|
* ERR would have also been set and we would have
|
|
* returned from lnc_tint above. Therefore we can
|
|
* assume if we arrive here that ONE is valid.
|
|
*
|
|
*/
|
|
|
|
if (next->md->md1 & ONE) {
|
|
LNCSTATS(one)
|
|
sc->arpcom.ac_if.if_collisions++;
|
|
}
|
|
INC_MD_PTR(sc->trans_next, sc->ntdre)
|
|
next = sc->trans_ring + sc->trans_next;
|
|
}
|
|
|
|
/*
|
|
* Clear descriptors and free any mbufs.
|
|
*/
|
|
|
|
do {
|
|
start = sc->trans_ring + start_of_packet;
|
|
start->md->md1 &= HADR;
|
|
if (sc->nic.mem_mode == DMA_MBUF) {
|
|
/* Cache clusters on a local queue */
|
|
if ((start->buff.mbuf->m_flags & M_EXT) && (sc->mbuf_count < MBUF_CACHE_LIMIT)) {
|
|
if (sc->mbuf_count) {
|
|
start->buff.mbuf->m_next = sc->mbufs;
|
|
sc->mbufs = start->buff.mbuf;
|
|
} else
|
|
sc->mbufs = start->buff.mbuf;
|
|
sc->mbuf_count++;
|
|
start->buff.mbuf = 0;
|
|
} else {
|
|
struct mbuf *junk;
|
|
MFREE(start->buff.mbuf, junk);
|
|
start->buff.mbuf = 0;
|
|
}
|
|
}
|
|
sc->pending_transmits--;
|
|
INC_MD_PTR(start_of_packet, sc->ntdre)
|
|
}while (start_of_packet != sc->trans_next);
|
|
|
|
/*
|
|
* There's now at least one free descriptor
|
|
* in the ring so indicate that we can accept
|
|
* more packets again.
|
|
*/
|
|
|
|
sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
|
|
|
|
lookahead++;
|
|
|
|
} while (sc->pending_transmits && !(next->md->md1 & OWN));
|
|
|
|
/*
|
|
* Clear TINT since we've dealt with all
|
|
* the completed transmissions.
|
|
*/
|
|
|
|
outw(sc->rdp, TINT | INEA);
|
|
|
|
/* XXX only while doing if_is comparisons */
|
|
if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE))
|
|
lnc_start(&sc->arpcom.ac_if);
|
|
|
|
}
|
|
|
|
static int
|
|
lnc_probe(struct isa_device * isa_dev)
|
|
{
|
|
int nports;
|
|
int unit = isa_dev->id_unit;
|
|
struct lnc_softc *sc = &lnc_softc[unit];
|
|
unsigned iobase = isa_dev->id_iobase;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
int vsw;
|
|
vsw = inw(isa_dev->id_iobase + PCNET_VSW);
|
|
printf("Vendor Specific Word = %x\n", vsw);
|
|
#endif
|
|
|
|
nports = bicc_probe(sc, iobase);
|
|
if (nports == 0)
|
|
nports = ne2100_probe(sc, iobase);
|
|
if (nports == 0)
|
|
nports = depca_probe(sc, iobase);
|
|
#ifdef PC98
|
|
if (nports == 0)
|
|
nports = cnet98s_probe(sc, iobase);
|
|
#endif
|
|
return (nports);
|
|
}
|
|
|
|
#ifdef PC98
|
|
/* ISA Bus Configuration Registers */
|
|
/* XXX - Should be in ic/Am7990.h */
|
|
#define MSRDA 0x0000 /* ISACSR0: Master Mode Read Activity */
|
|
#define MSWRA 0x0001 /* ISACSR1: Master Mode Write Activity */
|
|
#define MC 0x0002 /* ISACSR2: Miscellaneous Configuration */
|
|
|
|
#define LED1 0x0005 /* ISACSR5: LED1 Status */
|
|
#define LED2 0x0006 /* ISACSR6: LED2 Status */
|
|
#define LED3 0x0007 /* ISACSR7: LED3 Status */
|
|
|
|
#define LED_PSE 0x0080 /* Pulse Stretcher */
|
|
#define LED_XMTE 0x0010 /* Transmit Status */
|
|
#define LED_RVPOLE 0x0008 /* Receive Polarity */
|
|
#define LED_RCVE 0x0004 /* Receive Status */
|
|
#define LED_JABE 0x0002 /* Jabber */
|
|
#define LED_COLE 0x0001 /* Collision */
|
|
|
|
static int
|
|
cnet98s_probe(struct lnc_softc *sc, unsigned iobase)
|
|
{
|
|
int i;
|
|
ushort tmp;
|
|
|
|
sc->rap = iobase + CNET98S_RAP;
|
|
sc->rdp = iobase + CNET98S_RDP;
|
|
|
|
/* Reset */
|
|
tmp = inw(iobase + CNET98S_RESET);
|
|
outw(iobase + CNET98S_RESET, tmp);
|
|
DELAY(500);
|
|
|
|
sc->nic.ic = pcnet_probe(sc);
|
|
if ((sc->nic.ic == UNKNOWN) || (sc->nic.ic > PCnet_32)) {
|
|
return (0);
|
|
}
|
|
|
|
sc->nic.ident = CNET98S;
|
|
sc->nic.mem_mode = DMA_FIXED;
|
|
|
|
/* XXX - For now just use the defines */
|
|
sc->nrdre = NRDRE;
|
|
sc->ntdre = NTDRE;
|
|
|
|
/* Extract MAC address from PROM */
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
sc->arpcom.ac_enaddr[i] = inb(iobase + (i * 2));
|
|
}
|
|
|
|
/*
|
|
* ISA Configuration
|
|
*
|
|
* XXX - Following parameters are Contec C-NET(98)S only.
|
|
* So, check the Ethernet address here.
|
|
*
|
|
* Contec uses 00 80 4c ?? ?? ??
|
|
*/
|
|
if (sc->arpcom.ac_enaddr[0] == (u_char)0x00
|
|
&& sc->arpcom.ac_enaddr[1] == (u_char)0x80
|
|
&& sc->arpcom.ac_enaddr[2] == (u_char)0x4c) {
|
|
outw(sc->rap, MSRDA);
|
|
outw(iobase + CNET98S_IDP, 0x0006);
|
|
outw(sc->rap, MSWRA);
|
|
outw(iobase + CNET98S_IDP, 0x0006);
|
|
#ifdef DIAGNOSTIC
|
|
outw(sc->rap, MC);
|
|
printf("ISACSR2 = %x\n", inw(iobase + CNET98S_IDP));
|
|
#endif
|
|
outw(sc->rap, LED1);
|
|
outw(iobase + CNET98S_IDP, LED_PSE | LED_XMTE);
|
|
outw(sc->rap, LED2);
|
|
outw(iobase + CNET98S_IDP, LED_PSE | LED_RCVE);
|
|
outw(sc->rap, LED3);
|
|
outw(iobase + CNET98S_IDP, LED_PSE | LED_COLE);
|
|
}
|
|
|
|
return (CNET98S_IOSIZE);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
ne2100_probe(struct lnc_softc *sc, unsigned iobase)
|
|
{
|
|
int i;
|
|
|
|
sc->rap = iobase + PCNET_RAP;
|
|
sc->rdp = iobase + PCNET_RDP;
|
|
|
|
sc->nic.ic = pcnet_probe(sc);
|
|
if ((sc->nic.ic > 0) && (sc->nic.ic < PCnet_PCI)) {
|
|
sc->nic.ident = NE2100;
|
|
sc->nic.mem_mode = DMA_FIXED;
|
|
|
|
/* XXX - For now just use the defines */
|
|
sc->nrdre = NRDRE;
|
|
sc->ntdre = NTDRE;
|
|
|
|
/* Extract MAC address from PROM */
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
sc->arpcom.ac_enaddr[i] = inb(iobase + i);
|
|
return (NE2100_IOSIZE);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
static int
|
|
bicc_probe(struct lnc_softc *sc, unsigned iobase)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* There isn't any way to determine if a NIC is a BICC. Basically, if
|
|
* the lance probe succeeds using the i/o addresses of the BICC then
|
|
* we assume it's a BICC.
|
|
*
|
|
*/
|
|
|
|
sc->rap = iobase + BICC_RAP;
|
|
sc->rdp = iobase + BICC_RDP;
|
|
|
|
/* I think all these cards us the Am7990 */
|
|
|
|
if ((sc->nic.ic = lance_probe(sc))) {
|
|
sc->nic.ident = BICC;
|
|
sc->nic.mem_mode = DMA_FIXED;
|
|
|
|
/* XXX - For now just use the defines */
|
|
sc->nrdre = NRDRE;
|
|
sc->ntdre = NTDRE;
|
|
|
|
/* Extract MAC address from PROM */
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
sc->arpcom.ac_enaddr[i] = inb(iobase + (i * 2));
|
|
|
|
return (BICC_IOSIZE);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* I don't have data sheets for the dec cards but it looks like the mac
|
|
* address is contained in a 32 byte ring. Each time you read from the port
|
|
* you get the next byte in the ring. The mac address is stored after a
|
|
* signature so keep searching for the signature first.
|
|
*/
|
|
static int
|
|
dec_macaddr_extract(u_char ring[], struct lnc_softc * sc)
|
|
{
|
|
const unsigned char signature[] = {0xff, 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa};
|
|
|
|
int i, j, rindex;
|
|
|
|
for (i = 0; i < sizeof ring; i++) {
|
|
for (j = 0, rindex = i; j < sizeof signature; j++) {
|
|
if (ring[rindex] != signature[j])
|
|
break;
|
|
if (++rindex > sizeof ring)
|
|
rindex = 0;
|
|
}
|
|
if (j == sizeof signature) {
|
|
for (j = 0, rindex = i; j < ETHER_ADDR_LEN; j++) {
|
|
sc->arpcom.ac_enaddr[j] = ring[rindex];
|
|
if (++rindex > sizeof ring)
|
|
rindex = 0;
|
|
}
|
|
return (1);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
depca_probe(struct lnc_softc *sc, unsigned iobase)
|
|
{
|
|
int i;
|
|
unsigned char maddr_ring[DEPCA_ADDR_ROM_SIZE];
|
|
|
|
sc->rap = iobase + DEPCA_RAP;
|
|
sc->rdp = iobase + DEPCA_RDP;
|
|
|
|
if ((sc->nic.ic = lance_probe(sc))) {
|
|
sc->nic.ident = DEPCA;
|
|
sc->nic.mem_mode = SHMEM;
|
|
|
|
/* Extract MAC address from PROM */
|
|
for (i = 0; i < DEPCA_ADDR_ROM_SIZE; i++)
|
|
maddr_ring[i] = inb(iobase + DEPCA_ADP);
|
|
if (dec_macaddr_extract(maddr_ring, sc)) {
|
|
return (DEPCA_IOSIZE);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
lance_probe(struct lnc_softc *sc)
|
|
{
|
|
write_csr(sc, CSR0, STOP);
|
|
|
|
if ((inw(sc->rdp) & STOP) && !(read_csr(sc, CSR3))) {
|
|
/*
|
|
* Check to see if it's a C-LANCE. For the LANCE the INEA bit
|
|
* cannot be set while the STOP bit is. This restriction is
|
|
* removed for the C-LANCE.
|
|
*/
|
|
write_csr(sc, CSR0, INEA);
|
|
if (read_csr(sc, CSR0) & INEA)
|
|
return (C_LANCE);
|
|
else
|
|
return (LANCE);
|
|
} else
|
|
return (UNKNOWN);
|
|
}
|
|
|
|
static int
|
|
pcnet_probe(struct lnc_softc *sc)
|
|
{
|
|
u_long chip_id;
|
|
int type;
|
|
|
|
/*
|
|
* The PCnet family don't reset the RAP register on reset so we'll
|
|
* have to write during the probe :-) It does have an ID register
|
|
* though so the probe is just a matter of reading it.
|
|
*/
|
|
|
|
if ((type = lance_probe(sc))) {
|
|
chip_id = read_csr(sc, CSR89);
|
|
chip_id <<= 16;
|
|
chip_id |= read_csr(sc, CSR88);
|
|
if (chip_id & AMD_MASK) {
|
|
chip_id >>= 12;
|
|
switch (chip_id & PART_MASK) {
|
|
case Am79C960:
|
|
return (PCnet_ISA);
|
|
case Am79C961:
|
|
return (PCnet_ISAplus);
|
|
case Am79C961A:
|
|
return (PCnet_ISA_II);
|
|
case Am79C965:
|
|
return (PCnet_32);
|
|
case Am79C970:
|
|
return (PCnet_PCI);
|
|
case Am79C970A:
|
|
return (PCnet_PCI_II);
|
|
case Am79C971:
|
|
return (PCnet_FAST);
|
|
case Am79C972:
|
|
return (PCnet_FASTplus);
|
|
case Am79C978:
|
|
return (PCnet_Home);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return (type);
|
|
}
|
|
|
|
static int
|
|
lnc_attach_sc(struct lnc_softc *sc, int unit)
|
|
{
|
|
int lnc_mem_size;
|
|
|
|
/*
|
|
* Allocate memory for use by the controller.
|
|
*
|
|
* XXX -- the Am7990 and Am79C960 only have 24 address lines and so can
|
|
* only access the lower 16Mb of physical memory. For the moment we
|
|
* assume that malloc will allocate memory within the lower 16Mb
|
|
* range. This is not a very valid assumption but there's nothing
|
|
* that can be done about it yet. For shared memory NICs this isn't
|
|
* relevant.
|
|
*
|
|
*/
|
|
|
|
lnc_mem_size = ((NDESC(sc->nrdre) + NDESC(sc->ntdre)) *
|
|
sizeof(struct host_ring_entry));
|
|
|
|
if (sc->nic.mem_mode != SHMEM)
|
|
lnc_mem_size += sizeof(struct init_block) + (sizeof(struct mds) *
|
|
(NDESC(sc->nrdre) + NDESC(sc->ntdre))) +
|
|
MEM_SLEW;
|
|
|
|
/* If using DMA to fixed host buffers then allocate memory for them */
|
|
|
|
if (sc->nic.mem_mode == DMA_FIXED)
|
|
lnc_mem_size += (NDESC(sc->nrdre) * RECVBUFSIZE) + (NDESC(sc->ntdre) * TRANSBUFSIZE);
|
|
|
|
if (sc->nic.mem_mode != SHMEM) {
|
|
if (sc->nic.ic < PCnet_32) {
|
|
/* ISA based cards */
|
|
sc->recv_ring = contigmalloc(lnc_mem_size, M_DEVBUF, M_NOWAIT,
|
|
0ul, 0xfffffful, 4ul, 0x1000000);
|
|
} else {
|
|
/* Non-ISA based cards, 32 bit capable */
|
|
#ifdef notyet
|
|
/*
|
|
* For the 32 bit driver we're not fussed where we DMA to
|
|
* though it'll still need to be contiguous
|
|
*/
|
|
sc->recv_ring = malloc(lnc_mem_size, M_DEVBUF, M_NOWAIT);
|
|
#else
|
|
/*
|
|
* For now it still needs to be below 16MB because the
|
|
* descriptor's can only hold 16 bit addresses.
|
|
*/
|
|
sc->recv_ring = contigmalloc(lnc_mem_size, M_DEVBUF, M_NOWAIT,
|
|
0ul, 0xfffffful, 4ul, 0x1000000);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!sc->recv_ring) {
|
|
log(LOG_ERR, "lnc%d: Couldn't allocate memory for NIC\n", unit);
|
|
return (0); /* XXX -- attach failed -- not tested in
|
|
* calling routines */
|
|
}
|
|
|
|
/* Set default mode */
|
|
sc->nic.mode = NORMAL;
|
|
|
|
/* Fill in arpcom structure entries */
|
|
|
|
sc->arpcom.ac_if.if_softc = sc;
|
|
sc->arpcom.ac_if.if_name = lncdriver.name;
|
|
sc->arpcom.ac_if.if_unit = unit;
|
|
sc->arpcom.ac_if.if_mtu = ETHERMTU;
|
|
sc->arpcom.ac_if.if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
sc->arpcom.ac_if.if_timer = 0;
|
|
sc->arpcom.ac_if.if_output = ether_output;
|
|
sc->arpcom.ac_if.if_start = lnc_start;
|
|
sc->arpcom.ac_if.if_ioctl = lnc_ioctl;
|
|
sc->arpcom.ac_if.if_watchdog = lnc_watchdog;
|
|
sc->arpcom.ac_if.if_init = lnc_init;
|
|
sc->arpcom.ac_if.if_type = IFT_ETHER;
|
|
sc->arpcom.ac_if.if_addrlen = ETHER_ADDR_LEN;
|
|
sc->arpcom.ac_if.if_hdrlen = ETHER_HDR_LEN;
|
|
sc->arpcom.ac_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
|
|
|
|
/*
|
|
* XXX -- should check return status of if_attach
|
|
*/
|
|
|
|
if_attach(&sc->arpcom.ac_if);
|
|
ether_ifattach(&sc->arpcom.ac_if);
|
|
|
|
printf("lnc%d: ", unit);
|
|
if (sc->nic.ic == LANCE || sc->nic.ic == C_LANCE)
|
|
printf("%s (%s)",
|
|
nic_ident[sc->nic.ident], ic_ident[sc->nic.ic]);
|
|
else
|
|
printf("%s", ic_ident[sc->nic.ic]);
|
|
printf(" address %6D\n", sc->arpcom.ac_enaddr, ":");
|
|
|
|
bpfattach(&sc->arpcom.ac_if, DLT_EN10MB, sizeof(struct ether_header));
|
|
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
lnc_attach(struct isa_device * isa_dev)
|
|
{
|
|
int unit = isa_dev->id_unit;
|
|
struct lnc_softc *sc = &lnc_softc[unit];
|
|
int result;
|
|
|
|
isa_dev->id_ointr = lncintr;
|
|
result = lnc_attach_sc (sc, unit);
|
|
if (result == 0)
|
|
return (0);
|
|
|
|
#ifndef PC98
|
|
/*
|
|
* XXX - is it safe to call isa_dmacascade() after if_attach()
|
|
* and ether_ifattach() have been called in lnc_attach() ???
|
|
*/
|
|
if ((sc->nic.mem_mode != SHMEM) &&
|
|
(sc->nic.ic < PCnet_32))
|
|
isa_dmacascade(isa_dev->id_drq);
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
#if NPCI > 0
|
|
void *
|
|
lnc_attach_ne2100_pci(int unit, unsigned iobase)
|
|
{
|
|
int i;
|
|
struct lnc_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);
|
|
|
|
if (sc) {
|
|
bzero (sc, sizeof *sc);
|
|
|
|
sc->rap = iobase + PCNET_RAP;
|
|
sc->rdp = iobase + PCNET_RDP;
|
|
sc->bdp = iobase + PCNET_BDP;
|
|
|
|
sc->nic.ic = pcnet_probe(sc);
|
|
if (sc->nic.ic >= PCnet_32) {
|
|
sc->nic.ident = NE2100;
|
|
sc->nic.mem_mode = DMA_FIXED;
|
|
|
|
/* XXX - For now just use the defines */
|
|
sc->nrdre = NRDRE;
|
|
sc->ntdre = NTDRE;
|
|
|
|
/* Extract MAC address from PROM */
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
sc->arpcom.ac_enaddr[i] = inb(iobase + i);
|
|
|
|
if (lnc_attach_sc(sc, unit) == 0) {
|
|
free(sc, M_DEVBUF);
|
|
sc = NULL;
|
|
}
|
|
}
|
|
else {
|
|
free(sc, M_DEVBUF);
|
|
sc = NULL;
|
|
}
|
|
}
|
|
return sc;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
lnc_init(xsc)
|
|
void *xsc;
|
|
{
|
|
struct lnc_softc *sc = xsc;
|
|
int s, i;
|
|
char *lnc_mem;
|
|
|
|
/* Check that interface has valid address */
|
|
|
|
if (TAILQ_EMPTY(&sc->arpcom.ac_if.if_addrhead)) /* XXX unlikely */
|
|
return;
|
|
|
|
/* Shut down interface */
|
|
|
|
s = splimp();
|
|
lnc_stop(sc);
|
|
sc->arpcom.ac_if.if_flags |= IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /* XXX??? */
|
|
|
|
/*
|
|
* This sets up the memory area for the controller. Memory is set up for
|
|
* the initialisation block (12 words of contiguous memory starting
|
|
* on a word boundary),the transmit and receive ring structures (each
|
|
* entry is 4 words long and must start on a quadword boundary) and
|
|
* the data buffers.
|
|
*
|
|
* The alignment tests are particularly paranoid.
|
|
*/
|
|
|
|
|
|
|
|
sc->recv_next = 0;
|
|
sc->trans_ring = sc->recv_ring + NDESC(sc->nrdre);
|
|
sc->trans_next = 0;
|
|
|
|
if (sc->nic.mem_mode == SHMEM)
|
|
lnc_mem = (char *) sc->nic.iobase;
|
|
else
|
|
lnc_mem = (char *) (sc->trans_ring + NDESC(sc->ntdre));
|
|
|
|
lnc_mem = (char *)(((int)lnc_mem + 1) & ~1);
|
|
sc->init_block = (struct init_block *) ((int) lnc_mem & ~1);
|
|
lnc_mem = (char *) (sc->init_block + 1);
|
|
lnc_mem = (char *)(((int)lnc_mem + 7) & ~7);
|
|
|
|
/* Initialise pointers to descriptor entries */
|
|
for (i = 0; i < NDESC(sc->nrdre); i++) {
|
|
(sc->recv_ring + i)->md = (struct mds *) lnc_mem;
|
|
lnc_mem += sizeof(struct mds);
|
|
}
|
|
for (i = 0; i < NDESC(sc->ntdre); i++) {
|
|
(sc->trans_ring + i)->md = (struct mds *) lnc_mem;
|
|
lnc_mem += sizeof(struct mds);
|
|
}
|
|
|
|
/* Initialise the remaining ring entries */
|
|
|
|
if (sc->nic.mem_mode == DMA_MBUF) {
|
|
|
|
sc->mbufs = 0;
|
|
sc->mbuf_count = 0;
|
|
|
|
/* Free previously allocated mbufs */
|
|
if (sc->flags & LNC_INITIALISED)
|
|
lnc_free_mbufs(sc);
|
|
|
|
|
|
for (i = 0; i < NDESC(sc->nrdre); i++) {
|
|
if (alloc_mbuf_cluster(sc, sc->recv_ring+i)) {
|
|
log(LOG_ERR, "Initialisation failed -- no mbufs\n");
|
|
splx(s);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NDESC(sc->ntdre); i++) {
|
|
(sc->trans_ring + i)->buff.mbuf = 0;
|
|
(sc->trans_ring + i)->md->md0 = 0;
|
|
(sc->trans_ring + i)->md->md1 = 0;
|
|
(sc->trans_ring + i)->md->md2 = 0;
|
|
(sc->trans_ring + i)->md->md3 = 0;
|
|
}
|
|
} else {
|
|
for (i = 0; i < NDESC(sc->nrdre); i++) {
|
|
(sc->recv_ring + i)->md->md0 = kvtop(lnc_mem);
|
|
(sc->recv_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff) | OWN;
|
|
(sc->recv_ring + i)->md->md2 = -RECVBUFSIZE;
|
|
(sc->recv_ring + i)->md->md3 = 0;
|
|
(sc->recv_ring + i)->buff.data = lnc_mem;
|
|
lnc_mem += RECVBUFSIZE;
|
|
}
|
|
for (i = 0; i < NDESC(sc->ntdre); i++) {
|
|
(sc->trans_ring + i)->md->md0 = kvtop(lnc_mem);
|
|
(sc->trans_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff);
|
|
(sc->trans_ring + i)->md->md2 = 0;
|
|
(sc->trans_ring + i)->md->md3 = 0;
|
|
(sc->trans_ring + i)->buff.data = lnc_mem;
|
|
lnc_mem += TRANSBUFSIZE;
|
|
}
|
|
}
|
|
|
|
sc->next_to_send = 0;
|
|
|
|
/* Set up initialisation block */
|
|
|
|
sc->init_block->mode = sc->nic.mode;
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
sc->init_block->padr[i] = sc->arpcom.ac_enaddr[i];
|
|
|
|
lnc_setladrf(sc);
|
|
|
|
sc->init_block->rdra = kvtop(sc->recv_ring->md);
|
|
sc->init_block->rlen = ((kvtop(sc->recv_ring->md) >> 16) & 0xff) | (sc->nrdre << 13);
|
|
sc->init_block->tdra = kvtop(sc->trans_ring->md);
|
|
sc->init_block->tlen = ((kvtop(sc->trans_ring->md) >> 16) & 0xff) | (sc->ntdre << 13);
|
|
|
|
|
|
/* Set flags to show that the memory area is valid */
|
|
sc->flags |= LNC_INITIALISED;
|
|
|
|
sc->pending_transmits = 0;
|
|
|
|
/* Give the LANCE the physical address of the initialisation block */
|
|
|
|
if (sc->nic.ic == PCnet_Home) {
|
|
u_short media;
|
|
/* Set PHY_SEL to HomeRun */
|
|
media = read_bcr(sc, BCR49);
|
|
media &= ~3;
|
|
media |= 1;
|
|
write_bcr(sc, BCR49, media);
|
|
}
|
|
|
|
write_csr(sc, CSR1, kvtop(sc->init_block));
|
|
write_csr(sc, CSR2, (kvtop(sc->init_block) >> 16) & 0xff);
|
|
|
|
/*
|
|
* Depending on which controller this is, CSR3 has different meanings.
|
|
* For the Am7990 it controls DMA operations, for the Am79C960 it
|
|
* controls interrupt masks and transmitter algorithms. In either
|
|
* case, none of the flags are set.
|
|
*
|
|
*/
|
|
|
|
write_csr(sc, CSR3, 0);
|
|
|
|
/* Let's see if it starts */
|
|
|
|
write_csr(sc, CSR0, INIT);
|
|
for (i = 0; i < 1000; i++)
|
|
if (read_csr(sc, CSR0) & IDON)
|
|
break;
|
|
|
|
/*
|
|
* Now that the initialisation is complete there's no reason to
|
|
* access anything except CSR0, so we leave RAP pointing there
|
|
* so we can just access RDP from now on, saving an outw each
|
|
* time.
|
|
*/
|
|
|
|
if (read_csr(sc, CSR0) & IDON) {
|
|
/*
|
|
* Enable interrupts, start the LANCE, mark the interface as
|
|
* running and transmit any pending packets.
|
|
*/
|
|
write_csr(sc, CSR0, STRT | INEA);
|
|
sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
|
|
sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
|
|
lnc_start(&sc->arpcom.ac_if);
|
|
} else
|
|
log(LOG_ERR, "lnc%d: Initialisation failed\n",
|
|
sc->arpcom.ac_if.if_unit);
|
|
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* The interrupt flag (INTR) will be set and provided that the interrupt enable
|
|
* flag (INEA) is also set, the interrupt pin will be driven low when any of
|
|
* the following occur:
|
|
*
|
|
* 1) Completion of the initialisation routine (IDON). 2) The reception of a
|
|
* packet (RINT). 3) The transmission of a packet (TINT). 4) A transmitter
|
|
* timeout error (BABL). 5) A missed packet (MISS). 6) A memory error (MERR).
|
|
*
|
|
* The interrupt flag is cleared when all of the above conditions are cleared.
|
|
*
|
|
* If the driver is reset from this routine then it first checks to see if any
|
|
* interrupts have ocurred since the reset and handles them before returning.
|
|
* This is because the NIC may signify a pending interrupt in CSR0 using the
|
|
* INTR flag even if a hardware interrupt is currently inhibited (at least I
|
|
* think it does from reading the data sheets). We may as well deal with
|
|
* these pending interrupts now rather than get the overhead of another
|
|
* hardware interrupt immediately upon returning from the interrupt handler.
|
|
*
|
|
*/
|
|
|
|
void
|
|
lncintr_sc(struct lnc_softc *sc)
|
|
{
|
|
int unit = sc->arpcom.ac_if.if_unit;
|
|
u_short csr0;
|
|
|
|
/*
|
|
* INEA is the only bit that can be cleared by writing a 0 to it so
|
|
* we have to include it in any writes that clear other flags.
|
|
*/
|
|
|
|
while ((csr0 = inw(sc->rdp)) & INTR) {
|
|
|
|
/*
|
|
* Clear interrupt flags early to avoid race conditions. The
|
|
* controller can still set these flags even while we're in
|
|
* this interrupt routine. If the flag is still set from the
|
|
* event that caused this interrupt any new events will
|
|
* be missed.
|
|
*/
|
|
|
|
/* outw(sc->rdp, IDON | CERR | BABL | MISS | MERR | RINT | TINT | INEA); */
|
|
outw(sc->rdp, csr0);
|
|
|
|
/* We don't do anything with the IDON flag */
|
|
|
|
if (csr0 & ERR) {
|
|
if (csr0 & CERR) {
|
|
log(LOG_ERR, "lnc%d: Heartbeat error -- SQE test failed\n", unit);
|
|
LNCSTATS(cerr)
|
|
}
|
|
if (csr0 & BABL) {
|
|
log(LOG_ERR, "lnc%d: Babble error - more than 1519 bytes transmitted\n", unit);
|
|
LNCSTATS(babl)
|
|
sc->arpcom.ac_if.if_oerrors++;
|
|
}
|
|
if (csr0 & MISS) {
|
|
log(LOG_ERR, "lnc%d: Missed packet -- no receive buffer\n", unit);
|
|
LNCSTATS(miss)
|
|
sc->arpcom.ac_if.if_ierrors++;
|
|
}
|
|
if (csr0 & MERR) {
|
|
log(LOG_ERR, "lnc%d: Memory error -- Resetting\n", unit);
|
|
LNCSTATS(merr)
|
|
lnc_reset(sc);
|
|
continue;
|
|
}
|
|
}
|
|
if (csr0 & RINT) {
|
|
LNCSTATS(rint)
|
|
lnc_rint(sc);
|
|
}
|
|
if (csr0 & TINT) {
|
|
LNCSTATS(tint)
|
|
sc->arpcom.ac_if.if_timer = 0;
|
|
lnc_tint(sc);
|
|
}
|
|
|
|
/*
|
|
* If there's room in the transmit descriptor ring then queue
|
|
* some more transmit packets.
|
|
*/
|
|
|
|
if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE))
|
|
lnc_start(&sc->arpcom.ac_if);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lncintr(int unit)
|
|
{
|
|
struct lnc_softc *sc = &lnc_softc[unit];
|
|
lncintr_sc (sc);
|
|
}
|
|
|
|
static __inline int
|
|
mbuf_to_buffer(struct mbuf *m, char *buffer)
|
|
{
|
|
|
|
int len=0;
|
|
|
|
for( ; m; m = m->m_next) {
|
|
bcopy(mtod(m, caddr_t), buffer, m->m_len);
|
|
buffer += m->m_len;
|
|
len += m->m_len;
|
|
}
|
|
|
|
return(len);
|
|
}
|
|
|
|
static __inline struct mbuf *
|
|
chain_to_cluster(struct mbuf *m)
|
|
{
|
|
struct mbuf *new;
|
|
|
|
MGET(new, M_DONTWAIT, MT_DATA);
|
|
if (new) {
|
|
MCLGET(new, M_DONTWAIT);
|
|
if (new->m_ext.ext_buf) {
|
|
new->m_len = mbuf_to_buffer(m, new->m_data);
|
|
m_freem(m);
|
|
return(new);
|
|
} else
|
|
m_free(new);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* IFF_OACTIVE and IFF_RUNNING are checked in ether_output so it's redundant
|
|
* to check them again since we wouldn't have got here if they were not
|
|
* appropriately set. This is also called from lnc_init and lncintr but the
|
|
* flags should be ok at those points too.
|
|
*/
|
|
|
|
static void
|
|
lnc_start(struct ifnet *ifp)
|
|
{
|
|
|
|
struct lnc_softc *sc = ifp->if_softc;
|
|
struct host_ring_entry *desc;
|
|
int tmp;
|
|
int end_of_packet;
|
|
struct mbuf *head, *m;
|
|
int len, chunk;
|
|
int addr;
|
|
int no_entries_needed;
|
|
|
|
do {
|
|
|
|
IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, head);
|
|
if (!head)
|
|
return;
|
|
|
|
if (sc->nic.mem_mode == DMA_MBUF) {
|
|
|
|
no_entries_needed = 0;
|
|
for (m=head; m; m = m->m_next)
|
|
no_entries_needed++;
|
|
|
|
/*
|
|
* We try and avoid bcopy as much as possible
|
|
* but there are two cases when we use it.
|
|
*
|
|
* 1) If there are not enough free entries in the ring
|
|
* to hold each mbuf in the chain then compact the
|
|
* chain into a single cluster.
|
|
*
|
|
* 2) The Am7990 and Am79C90 must not have less than
|
|
* 100 bytes in the first descriptor of a chained
|
|
* packet so it's necessary to shuffle the mbuf
|
|
* contents to ensure this.
|
|
*/
|
|
|
|
|
|
if (no_entries_needed > (NDESC(sc->ntdre) - sc->pending_transmits)) {
|
|
if (!(head = chain_to_cluster(head))) {
|
|
log(LOG_ERR, "lnc%d: Couldn't get mbuf for transmit packet -- Resetting \n ",ifp->if_unit);
|
|
lnc_reset(sc);
|
|
return;
|
|
}
|
|
} else if ((sc->nic.ic == LANCE) || (sc->nic.ic == C_LANCE)) {
|
|
if ((head->m_len < 100) && (head->m_next)) {
|
|
len = 100 - head->m_len;
|
|
if (M_TRAILINGSPACE(head) < len) {
|
|
/*
|
|
* Move data to start of data
|
|
* area. We assume the first
|
|
* mbuf has a packet header
|
|
* and is not a cluster.
|
|
*/
|
|
bcopy((caddr_t)head->m_data, (caddr_t)head->m_pktdat, head->m_len);
|
|
head->m_data = head->m_pktdat;
|
|
}
|
|
m = head->m_next;
|
|
while (m && (len > 0)) {
|
|
chunk = min(len, m->m_len);
|
|
bcopy(mtod(m, caddr_t), mtod(head, caddr_t) + head->m_len, chunk);
|
|
len -= chunk;
|
|
head->m_len += chunk;
|
|
m->m_len -= chunk;
|
|
m->m_data += chunk;
|
|
if (m->m_len <= 0) {
|
|
MFREE(m, head->m_next);
|
|
m = head->m_next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tmp = sc->next_to_send;
|
|
|
|
/*
|
|
* On entering this loop we know that tmp points to a
|
|
* descriptor with a clear OWN bit.
|
|
*/
|
|
|
|
desc = sc->trans_ring + tmp;
|
|
len = ETHER_MIN_LEN;
|
|
for (m = head; m; m = m->m_next) {
|
|
desc->buff.mbuf = m;
|
|
addr = kvtop(m->m_data);
|
|
desc->md->md0 = addr;
|
|
desc->md->md1 = ((addr >> 16) & 0xff);
|
|
desc->md->md3 = 0;
|
|
desc->md->md2 = -m->m_len;
|
|
sc->pending_transmits++;
|
|
len -= m->m_len;
|
|
|
|
INC_MD_PTR(tmp, sc->ntdre)
|
|
desc = sc->trans_ring + tmp;
|
|
}
|
|
|
|
end_of_packet = tmp;
|
|
DEC_MD_PTR(tmp, sc->ntdre)
|
|
desc = sc->trans_ring + tmp;
|
|
desc->md->md1 |= ENP;
|
|
|
|
if (len > 0)
|
|
desc->md->md2 -= len;
|
|
|
|
/*
|
|
* Set OWN bits in reverse order, otherwise the Lance
|
|
* could start sending the packet before all the
|
|
* buffers have been relinquished by the host.
|
|
*/
|
|
|
|
while (tmp != sc->next_to_send) {
|
|
desc->md->md1 |= OWN;
|
|
DEC_MD_PTR(tmp, sc->ntdre)
|
|
desc = sc->trans_ring + tmp;
|
|
}
|
|
sc->next_to_send = end_of_packet;
|
|
desc->md->md1 |= STP | OWN;
|
|
} else {
|
|
sc->pending_transmits++;
|
|
desc = sc->trans_ring + sc->next_to_send;
|
|
len = mbuf_to_buffer(head, desc->buff.data);
|
|
desc->md->md3 = 0;
|
|
desc->md->md2 = -max(len, ETHER_MIN_LEN - ETHER_CRC_LEN);
|
|
desc->md->md1 |= OWN | STP | ENP;
|
|
INC_MD_PTR(sc->next_to_send, sc->ntdre)
|
|
}
|
|
|
|
/* Force an immediate poll of the transmit ring */
|
|
outw(sc->rdp, TDMD | INEA);
|
|
|
|
/*
|
|
* Set a timer so if the buggy Am7990.h shuts
|
|
* down we can wake it up.
|
|
*/
|
|
|
|
ifp->if_timer = 2;
|
|
|
|
if (sc->arpcom.ac_if.if_bpf)
|
|
bpf_mtap(&sc->arpcom.ac_if, head);
|
|
|
|
if (sc->nic.mem_mode != DMA_MBUF)
|
|
m_freem(head);
|
|
|
|
} while (sc->pending_transmits < NDESC(sc->ntdre));
|
|
|
|
/*
|
|
* Transmit ring is full so set IFF_OACTIVE
|
|
* since we can't buffer any more packets.
|
|
*/
|
|
|
|
sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
|
|
LNCSTATS(trans_ring_full)
|
|
}
|
|
|
|
static int
|
|
lnc_ioctl(struct ifnet * ifp, u_long command, caddr_t data)
|
|
{
|
|
|
|
struct lnc_softc *sc = ifp->if_softc;
|
|
int s, error = 0;
|
|
|
|
s = splimp();
|
|
|
|
switch (command) {
|
|
case SIOCSIFADDR:
|
|
case SIOCGIFADDR:
|
|
case SIOCSIFMTU:
|
|
error = ether_ioctl(ifp, command, data);
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
#ifdef DEBUG
|
|
if (ifp->if_flags & IFF_DEBUG)
|
|
sc->lnc_debug = 1;
|
|
else
|
|
sc->lnc_debug = 0;
|
|
#endif
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
if (!(sc->nic.mode & PROM)) {
|
|
sc->nic.mode |= PROM;
|
|
lnc_init(sc);
|
|
}
|
|
} else if (sc->nic.mode & PROM) {
|
|
sc->nic.mode &= ~PROM;
|
|
lnc_init(sc);
|
|
}
|
|
|
|
if ((ifp->if_flags & IFF_ALLMULTI) &&
|
|
!(sc->flags & LNC_ALLMULTI)) {
|
|
sc->flags |= LNC_ALLMULTI;
|
|
lnc_init(sc);
|
|
} else if (!(ifp->if_flags & IFF_ALLMULTI) &&
|
|
(sc->flags & LNC_ALLMULTI)) {
|
|
sc->flags &= ~LNC_ALLMULTI;
|
|
lnc_init(sc);
|
|
}
|
|
|
|
if ((ifp->if_flags & IFF_UP) == 0 &&
|
|
(ifp->if_flags & IFF_RUNNING) != 0) {
|
|
/*
|
|
* If interface is marked down and it is running,
|
|
* then stop it.
|
|
*/
|
|
lnc_stop(sc);
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
} else if ((ifp->if_flags & IFF_UP) != 0 &&
|
|
(ifp->if_flags & IFF_RUNNING) == 0) {
|
|
/*
|
|
* If interface is marked up and it is stopped, then
|
|
* start it.
|
|
*/
|
|
lnc_init(sc);
|
|
}
|
|
break;
|
|
case SIOCADDMULTI:
|
|
case SIOCDELMULTI:
|
|
lnc_init(sc);
|
|
error = 0;
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
}
|
|
(void) splx(s);
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
lnc_watchdog(struct ifnet *ifp)
|
|
{
|
|
log(LOG_ERR, "lnc%d: Device timeout -- Resetting\n", ifp->if_unit);
|
|
ifp->if_oerrors++;
|
|
lnc_reset(ifp->if_softc);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
lnc_dump_state(struct lnc_softc *sc)
|
|
{
|
|
int i;
|
|
|
|
printf("\nDriver/NIC [%d] state dump\n", sc->arpcom.ac_if.if_unit);
|
|
printf("Memory access mode: %b\n", sc->nic.mem_mode, MEM_MODES);
|
|
printf("Host memory\n");
|
|
printf("-----------\n");
|
|
|
|
printf("Receive ring: base = %p, next = %p\n",
|
|
(void *)sc->recv_ring, (void *)(sc->recv_ring + sc->recv_next));
|
|
for (i = 0; i < NDESC(sc->nrdre); i++)
|
|
printf("\t%d:%p md = %p buff = %p\n",
|
|
i, (void *)(sc->recv_ring + i),
|
|
(void *)(sc->recv_ring + i)->md,
|
|
(void *)(sc->recv_ring + i)->buff.data);
|
|
|
|
printf("Transmit ring: base = %p, next = %p\n",
|
|
(void *)sc->trans_ring, (void *)(sc->trans_ring + sc->trans_next));
|
|
for (i = 0; i < NDESC(sc->ntdre); i++)
|
|
printf("\t%d:%p md = %p buff = %p\n",
|
|
i, (void *)(sc->trans_ring + i),
|
|
(void *)(sc->trans_ring + i)->md,
|
|
(void *)(sc->trans_ring + i)->buff.data);
|
|
printf("Lance memory (may be on host(DMA) or card(SHMEM))\n");
|
|
printf("Init block = %p\n", (void *)sc->init_block);
|
|
printf("\tmode = %b rlen:rdra = %x:%x tlen:tdra = %x:%x\n",
|
|
sc->init_block->mode, INIT_MODE, sc->init_block->rlen,
|
|
sc->init_block->rdra, sc->init_block->tlen, sc->init_block->tdra);
|
|
printf("Receive descriptor ring\n");
|
|
for (i = 0; i < NDESC(sc->nrdre); i++)
|
|
printf("\t%d buffer = 0x%x%x, BCNT = %d,\tMCNT = %u,\tflags = %b\n",
|
|
i, ((sc->recv_ring + i)->md->md1 & HADR),
|
|
(sc->recv_ring + i)->md->md0,
|
|
-(short) (sc->recv_ring + i)->md->md2,
|
|
(sc->recv_ring + i)->md->md3,
|
|
(((sc->recv_ring + i)->md->md1 & ~HADR) >> 8), RECV_MD1);
|
|
printf("Transmit descriptor ring\n");
|
|
for (i = 0; i < NDESC(sc->ntdre); i++)
|
|
printf("\t%d buffer = 0x%x%x, BCNT = %d,\tflags = %b %b\n",
|
|
i, ((sc->trans_ring + i)->md->md1 & HADR),
|
|
(sc->trans_ring + i)->md->md0,
|
|
-(short) (sc->trans_ring + i)->md->md2,
|
|
((sc->trans_ring + i)->md->md1 >> 8), TRANS_MD1,
|
|
((sc->trans_ring + i)->md->md3 >> 10), TRANS_MD3);
|
|
printf("\nnext_to_send = %x\n", sc->next_to_send);
|
|
printf("\n CSR0 = %b CSR1 = %x CSR2 = %x CSR3 = %x\n\n",
|
|
read_csr(sc, CSR0), CSR0_FLAGS, read_csr(sc, CSR1),
|
|
read_csr(sc, CSR2), read_csr(sc, CSR3));
|
|
|
|
/* Set RAP back to CSR0 */
|
|
outw(sc->rap, CSR0);
|
|
}
|
|
|
|
void
|
|
mbuf_dump_chain(struct mbuf * m)
|
|
{
|
|
|
|
#define MBUF_FLAGS \
|
|
"\20\1M_EXT\2M_PKTHDR\3M_EOR\4UNKNOWN\5M_BCAST\6M_MCAST"
|
|
|
|
if (!m)
|
|
log(LOG_DEBUG, "m == NULL\n");
|
|
do {
|
|
log(LOG_DEBUG, "m = %p\n", (void *)m);
|
|
log(LOG_DEBUG, "m_hdr.mh_next = %p\n",
|
|
(void *)m->m_hdr.mh_next);
|
|
log(LOG_DEBUG, "m_hdr.mh_nextpkt = %p\n",
|
|
(void *)m->m_hdr.mh_nextpkt);
|
|
log(LOG_DEBUG, "m_hdr.mh_len = %d\n", m->m_hdr.mh_len);
|
|
log(LOG_DEBUG, "m_hdr.mh_data = %p\n",
|
|
(void *)m->m_hdr.mh_data);
|
|
log(LOG_DEBUG, "m_hdr.mh_type = %d\n", m->m_hdr.mh_type);
|
|
log(LOG_DEBUG, "m_hdr.mh_flags = %b\n", m->m_hdr.mh_flags,
|
|
MBUF_FLAGS);
|
|
if (!(m->m_hdr.mh_flags & (M_PKTHDR | M_EXT)))
|
|
log(LOG_DEBUG, "M_dat.M_databuf = %p\n",
|
|
(void *)m->M_dat.M_databuf);
|
|
else {
|
|
if (m->m_hdr.mh_flags & M_PKTHDR) {
|
|
log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.len = %d\n",
|
|
m->M_dat.MH.MH_pkthdr.len);
|
|
log(LOG_DEBUG,
|
|
"M_dat.MH.MH_pkthdr.rcvif = %p\n",
|
|
(void *)m->M_dat.MH.MH_pkthdr.rcvif);
|
|
if (!(m->m_hdr.mh_flags & M_EXT))
|
|
log(LOG_DEBUG,
|
|
"M_dat.MH.MH_dat.MH_databuf = %p\n",
|
|
(void *)m->M_dat.MH.MH_dat.MH_databuf);
|
|
}
|
|
if (m->m_hdr.mh_flags & M_EXT) {
|
|
log(LOG_DEBUG,
|
|
"M_dat.MH.MH_dat.MH_ext.ext_buff %p\n",
|
|
(void *)m->M_dat.MH.MH_dat.MH_ext.ext_buf);
|
|
log(LOG_DEBUG,
|
|
"M_dat.MH.MH_dat.MH_ext.ext_free %p\n",
|
|
(void *)m->M_dat.MH.MH_dat.MH_ext.ext_free);
|
|
log(LOG_DEBUG,
|
|
"M_dat.MH.MH_dat.MH_ext.ext_size %d\n",
|
|
m->M_dat.MH.MH_dat.MH_ext.ext_size);
|
|
}
|
|
}
|
|
} while ((m = m->m_next) != NULL);
|
|
}
|
|
#endif
|
|
|
|
#endif
|