/*- * 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. * * $Id: if_lnc.c,v 1.52 1999/01/31 00:39:20 paul Exp $ */ /* #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 "bpfilter.h" #include "opt_inet.h" /* Some defines that should really be in generic locations */ #define FCS_LEN 4 #define MULTICAST_FILTER_LEN 8 #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #if NBPFILTER > 0 #include #endif #ifdef PC98 #include #endif #include #include #include 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+", }; 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((struct lnc_softc *sc)); 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 - sizeof *eh; /* * 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 NBPFILTER > 0 if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, head); /* 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 #endif { /* Skip over the ether header */ head->m_data += sizeof *eh; head->m_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); 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); sc->recv_ring = malloc(lnc_mem_size, M_DEVBUF, M_NOWAIT); 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 */ } /* * XXX - Shouldn't this be skipped for the EISA and PCI versions ??? * Print the message but do not return for the PCnet_PCI ! */ if ((sc->nic.mem_mode != SHMEM) && (kvtop(sc->recv_ring) > 0x1000000)) { log(LOG_ERR, "lnc%d: Memory allocated above 16Mb limit\n", unit); if ((sc->nic.ic != PCnet_PCI) && (sc->nic.ic != PCnet_PCI_II) && (sc->nic.ic != PCnet_FAST)) return (0); } /* 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_type = IFT_ETHER; sc->arpcom.ac_if.if_addrlen = ETHER_ADDR_LEN; sc->arpcom.ac_if.if_hdrlen = ETHER_HDR_LEN; /* * 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, ":"); #if NBPFILTER > 0 bpfattach(&sc->arpcom.ac_if, DLT_EN10MB, sizeof(struct ether_header)); #endif 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(struct lnc_softc *sc) { 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 */ 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 NBPFILTER > 0 if (sc->arpcom.ac_if.if_bpf) bpf_mtap(&sc->arpcom.ac_if, head); #endif 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; struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int s, error = 0; s = splimp(); switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: lnc_init(sc); arp_ifinit((struct arpcom *)ifp, ifa); break; #endif default: lnc_init(sc); break; } 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; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else ifp->if_mtu = ifr->ifr_mtu; 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); } #endif #endif