/* * Copyright (c) 1994, Paul Richards. * * All rights reserved. * * This software may be used, modified, copied, distributed, and * sold, in both source and binary form provided that the above * copyright and these terms are retained, verbatim, as the first * lines of this file. Under no circumstances is the author * responsible for the proper functioning of this software, nor does * the author assume any responsibility for damages incurred with * its use. */ /* #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 "lnc.h" #if NLNC > 0 #include "bpfilter.h" /* Some defines that should really be in generic locations */ #define FCS_LEN 4 #define ETHER_ADDR_LEN 6 #define ETHER_HDR_LEN 14 #define MULTICAST_ADDR_LEN 8 #define ETHER_MIN_LEN 64 #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #if NBPFILTER > 0 #include #include #endif #include #include struct lnc_softc { struct arpcom arpcom; /* see ../../netinet/if_ether.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 initialised; int rap; int rdp; #ifdef DEBUG int lnc_debug; #endif #if NBPFILTER > 0 caddr_t bpf; /* XXX bpf magic cookie - move to arpcom */ #endif LNCSTATS_STRUCT } lnc_softc[NLNC]; /* Function prototypes */ void lnc_init(int); void lnc_start(struct ifnet *); int lnc_ioctl(struct ifnet *, int, caddr_t); void lnc_watchdog(int); int lnc_probe(struct isa_device *); int lnc_attach(struct isa_device *); void lnc_dump_state(int); struct isa_driver lncdriver = {lnc_probe, lnc_attach, "lnc"}; inline void write_csr(int unit, u_short port, u_short val) { outw(lnc_softc[unit].rap, port); outw(lnc_softc[unit].rdp, val); } inline u_short read_csr(int unit, u_short port) { outw(lnc_softc[unit].rap, port); return (inw(lnc_softc[unit].rdp)); } void lnc_setladrf(struct ifnet *ifp, struct lnc_softc *sc) { } void lnc_stop(int unit) { write_csr(unit, CSR0, STOP); } void lnc_reset(int unit) { int s; lnc_init(unit); } 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); } 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); } 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(-(desc->md->md2), 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); } 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); 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; INC_MD_PTR(start_of_packet, sc->nrdre) start = sc->recv_ring + start_of_packet; data = start->buff.data; blen = -(start->md->md2); } } return(head); } inline void lnc_rint(int unit) { register struct lnc_softc *sc = &lnc_softc[unit]; 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) { log(LOG_ERR, "lnc%d: Receive interrupt with buffer still owned by controller -- Resetting\n", unit); lnc_reset(unit); return; } if (!((sc->recv_ring + sc->recv_next)->md->md1 & STP)) { log(LOG_ERR, "lnc%d: Receive interrupt but not start of packet -- Resetting\n", unit); lnc_reset(unit); 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) { log(LOG_ERR, "lnc%d: Start of packet found before end of previous in receive ring -- Resetting\n", unit); lnc_reset(unit); return; } if (flags & OWN) { if (lookahead) { /* * Looked ahead into a packet still * being received */ sc->recv_next = start_of_packet; break; } else { log(LOG_ERR, "lnc%d: End of received packet not found-- Resetting\n", unit); lnc_reset(unit); 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) { 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 { /* * FRAM and CRC are valid only if OFLO is not * set and ENP is */ if (flags & (FRAM | ENP)) { LNCSTATS(fram) log(LOG_ERR, "lnc%d: Framming error\n", unit); /* * FRAM is only set if there's a CRC * error so avoid multiple messages */ } else if (flags & (CRC | ENP)) { 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->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); eh = (struct ether_header *) head->m_data; head->m_data += sizeof *eh; head->m_len -= sizeof *eh; #if NBPFILTER > 0 if (sc->bpf) bpf_mtap(sc->bpf, head); /* Check this packet is really for us */ /* XXX -- this doesn't look right */ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0) && (bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0)) m_freem(head); else #endif ether_input(&sc->arpcom.ac_if, eh, head); } else { 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); } inline void lnc_tint(int unit) { register struct lnc_softc *sc = &lnc_softc[unit]; 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) { log(LOG_ERR, "lnc%d: Transmit interrupt with buffer still owned by controller -- Resetting\n", unit); lnc_reset(unit); 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)) { log(LOG_ERR, "lnc%d: Transmit interrupt but not start of packet -- Resetting\n", unit); lnc_reset(unit); 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) { log(LOG_ERR, "lnc%d: Start of packet found before end of previous in transmit ring -- Resetting\n", unit); lnc_reset(unit); return; } if (next->md->md1 & OWN) { if (lookahead) { /* * Looked ahead into a packet still * being transmitted */ sc->trans_next = start_of_packet; break; } else { log(LOG_ERR, "lnc%d: End of transmitted packet not found -- Resetting\n", unit); lnc_reset(unit); return; } } } /* * Check for ERR first since other flags are irrelevant if an * error occurred. */ if (next->md->md1 & MDERR) { 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(unit); 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 struct kern_devconf kdc_lnc[NLNC] = { { 0, 0, 0, /* filled in by dev_attach */ "lnc", 0, { MDDT_ISA, 0, "net" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_BUSY, "Generic LANCE-based Ethernet adapter" } }; static inline void lnc_registerdev(struct isa_device *id) { if(id->id_unit) kdc_lnc[id->id_unit] = kdc_lnc[0]; kdc_lnc[id->id_unit].kdc_unit = id->id_unit; kdc_lnc[id->id_unit].kdc_isa = id; dev_attach(&kdc_lnc[id->id_unit]); } int lnc_probe(struct isa_device * isa_dev) { int nports; int vsw; #ifdef DEBUG { int i; log(LOG_DEBUG, "Dumping io space for lnc%d starting at %x\n", isa_dev->id_unit, isa_dev->id_iobase); for (i = 0; i < 32; i++) log(LOG_DEBUG, " %x ", inb(isa_dev->id_iobase + i)); } #endif #ifdef DIAGNOSTIC /* First check the Vendor Specific Word */ vsw = inw(isa_dev->id_iobase + PCNET_VSW); printf("Vendor Specific Word = %x\n", vsw); #endif if (nports = bicc_probe(isa_dev)) return (nports); if (nports = ne2100_probe(isa_dev)) return (nports); if (nports = depca_probe(isa_dev)) return (nports); return (0); } int ne2100_probe(struct isa_device * isa_dev) { struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit]; int i; sc->rap = isa_dev->id_iobase + PCNET_RAP; sc->rdp = isa_dev->id_iobase + PCNET_RDP; if (sc->nic.ic = lance_probe(isa_dev->id_unit)) { sc->nic.ident = NE2100; sc->nic.mem_mode = DMA_FIXED; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = inb(isa_dev->id_iobase + i); return (NE2100_IOSIZE); } else { return (0); } } int bicc_probe(struct isa_device * isa_dev) { struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit]; 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 = isa_dev->id_iobase + BICC_RAP; sc->rdp = isa_dev->id_iobase + BICC_RDP; /* I think all these cards us the Am7990 */ if (sc->nic.ic = lance_probe(isa_dev->id_unit)) { 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(isa_dev->id_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. */ 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); } int depca_probe(struct isa_device * isa_dev) { int i; struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit]; unsigned char maddr_ring[DEPCA_ADDR_ROM_SIZE]; sc->rap = isa_dev->id_iobase + DEPCA_RAP; sc->rdp = isa_dev->id_iobase + DEPCA_RDP; if (sc->nic.ic = lance_probe(isa_dev->id_unit)) { 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(isa_dev->id_iobase + DEPCA_ADP); if (dec_macaddr_extract(maddr_ring, sc)) return (DEPCA_IOSIZE); } return (0); } int lance_probe(int unit) { write_csr(unit, CSR0, STOP); if ((inw(lnc_softc[unit].rdp) & STOP) && !(read_csr(unit, 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(unit, CSR0, INEA); if (read_csr(unit, CSR0) & INEA) return (C_LANCE); else return (LANCE); } else return (UNKNOWN); } int pcnet_probe(int unit) { 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(unit)) { chip_id = read_csr(unit, CSR88); chip_id <<= 16; chip_id |= read_csr(unit, CSR89); if (chip_id & AMD_MASK) { chip_id >>= 12; switch (chip_id & PART_MASK) { case Am79C960: return (PCnet_ISA); case Am79C961: return (PCnet_ISAplus); case Am79C965: return (PCnet_32); case Am79C970: return (PCnet_PCI); default: break; } } } return (type); } int lnc_attach(struct isa_device * isa_dev) { struct lnc_softc *sc = &lnc_softc[isa_dev->id_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", isa_dev->id_unit); return (0); /* XXX -- attach failed -- not tested in * calling routines */ } if ((sc->nic.mem_mode != SHMEM) && (kvtop(sc->recv_ring) > 0x1000000)) { log(LOG_ERR, "lnc%d: Memory allocated above 16Mb limit\n", isa_dev->id_unit); return (0); } if (sc->nic.mem_mode != SHMEM) isa_dmacascade(isa_dev->id_drq); /* Set default mode */ sc->nic.mode = NORMAL; /* Fill in arpcom structure entries */ sc->arpcom.ac_if.if_name = lncdriver.name; sc->arpcom.ac_if.if_unit = isa_dev->id_unit; sc->arpcom.ac_if.if_mtu = ETHERMTU; sc->arpcom.ac_if.if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX; sc->arpcom.ac_if.if_timer = 0; sc->arpcom.ac_if.if_init = lnc_init; 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_reset = lnc_reset; 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); printf("lnc%d: %s, %s address %s\n", isa_dev->id_unit, nic_ident[sc->nic.ident], ic_ident[sc->nic.ic], ether_sprintf(sc->arpcom.ac_enaddr)); #if NBPFILTER > 0 bpfattach(&sc->bpf, &sc->arpcom.ac_if, DLT_EN10MB, sizeof(struct ether_header)); #endif return (1); } void lnc_init(int unit) { struct lnc_softc *sc = &lnc_softc[unit]; int s, i; char *lnc_mem; struct mbuf *m = 0; /* Check that interface has valid address */ if (!sc->arpcom.ac_if.if_addrlist) return; /* Shut down interface */ s = splimp(); lnc_stop(unit); sc->arpcom.ac_if.if_flags |= IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX; /* * 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->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]; for (i = 0; i < MULTICAST_ADDR_LEN; i++) sc->init_block->ladrf[i] = MULTI_INIT_ADDR; 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 initialised to show that the memory area is valid */ sc->initialised = 1; sc->pending_transmits = 0; /* Give the LANCE the physical address of the initialisation block */ write_csr(unit, CSR1, kvtop(sc->init_block)); write_csr(unit, 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(unit, CSR3, 0); /* Let's see if it starts */ write_csr(unit, CSR0, INIT); for (i = 0; i < 1000; i++) if (read_csr(unit, 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(unit, CSR0) & IDON) { /* * Enable interrupts, start the LANCE, mark the interface as * running and transmit any pending packets. */ write_csr(unit, 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", 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(int unit) { struct lnc_softc *sc = &lnc_softc[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); /* 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(unit); continue; } } if (csr0 & RINT) { LNCSTATS(rint) lnc_rint(unit); } if (csr0 & TINT) { LNCSTATS(tint) sc->arpcom.ac_if.if_timer = 0; lnc_tint(unit); } /* * 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); } } 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); } 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. */ void lnc_start(struct ifnet *ifp) { struct lnc_softc *sc = &lnc_softc[ifp->if_unit]; struct host_ring_entry *desc; int tmp; int end_of_packet; struct mbuf *head, *m; int len, chunk; char *buffer; 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(ifp->if_unit); 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); 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->bpf) bpf_mtap(sc->bpf, 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) } int lnc_ioctl(struct ifnet * ifp, int command, caddr_t data) { struct lnc_softc *sc = &lnc_softc[ifp->if_unit]; 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(ifp->if_unit); ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); break; #endif default: lnc_init(ifp->if_unit); 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(ifp->if_unit); } } else if (sc->nic.mode & PROM) { sc->nic.mode &= ~PROM; lnc_init(ifp->if_unit); } 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(ifp->if_unit); 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(ifp->if_unit); } break; #ifdef notyet case SIOCADDMULTI: case SIOCDELMULTI: error = (command == SIOCADDMULTI) ? ether_addmulti(ifr, &sc->arpcom) : ether_delmulti(ifr, &sc->arpcom); if (error == ENETRESET) { lnc_setladrf(ifp,sc); error = 0; } break; #endif 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; } void lnc_watchdog(int unit) { log(LOG_ERR, "lnc%d: Device timeout -- Resetting\n", unit); ++lnc_softc[unit].arpcom.ac_if.if_oerrors; lnc_reset(unit); } #ifdef DEBUG void lnc_dump_state(int unit) { struct lnc_softc *sc = &lnc_softc[unit]; int i; log(LOG_DEBUG, "\nDriver/NIC [%d] state dump\n", unit); log(LOG_DEBUG, "Memory access mode: %b\n", sc->nic.mem_mode, MEM_MODES); log(LOG_DEBUG, "Host memory\n"); log(LOG_DEBUG, "-----------\n"); log(LOG_DEBUG, "Receive ring: base = %x, next = %x\n", sc->recv_ring, (sc->recv_ring + sc->recv_next)); for (i = 0; i < NDESC(sc->nrdre); i++) log(LOG_DEBUG, "\t%d:%x md = %x buff = %x\n", i, sc->recv_ring + i, (sc->recv_ring + i)->md, (sc->recv_ring + i)->buff); log(LOG_DEBUG, "Transmit ring: base = %x, next = %x\n", sc->trans_ring, (sc->trans_ring + sc->trans_next)); for (i = 0; i < NDESC(sc->ntdre); i++) log(LOG_DEBUG, "\t%d:%x md = %x buff = %x\n", i, sc->trans_ring + i, (sc->trans_ring + i)->md, (sc->trans_ring + i)->buff); log(LOG_DEBUG, "Lance memory (may be on host(DMA) or card(SHMEM))\n"); log(LOG_DEBUG, "Init block = %x\n", sc->init_block); log(LOG_DEBUG, "\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); log(LOG_DEBUG, "Receive descriptor ring\n"); for (i = 0; i < NDESC(sc->nrdre); i++) log(LOG_DEBUG, "\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); log(LOG_DEBUG, "Transmit descriptor ring\n"); for (i = 0; i < NDESC(sc->ntdre); i++) log(LOG_DEBUG, "\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); log(LOG_DEBUG, "\nnext_to_send = %x\n", sc->next_to_send); log(LOG_DEBUG, "\n CSR0 = %b CSR1 = %x CSR2 = %x CSR3 = %x\n\n", read_csr(unit, CSR0), CSR0_FLAGS, read_csr(unit, CSR1), read_csr(unit, CSR2), read_csr(unit, 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 = %x\n", m); log(LOG_DEBUG, "m_hdr.mh_next = %x\n", m->m_hdr.mh_next); log(LOG_DEBUG, "m_hdr.mh_nextpkt = %x\n", 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 = %x\n", 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 = %x\n", 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 = %x\n", 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 = %x\n", 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 %x\n", m->M_dat.MH.MH_dat.MH_ext.ext_buf); log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_free %x\n", 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