📄 3c509.c
字号:
* DESCRIPTION: Completes the initialization/attachement of the driver. * * RETURNS: 0 - ok. * **********************************************************************************/static int ep_attach( struct ep_softc *sc ){ u_short *p; int i; /* * Setup the station address */ p = (u_short *) &sc->arpcom.ac_enaddr; GO_WINDOW(2); printf("ADDRESS=" ); for (i = 0; i < 3; i++) { p[i] = htons( sc->epb->eth_addr[i] ); outw( BASE + EP_W2_ADDR_0 + (i * 2), ntohs( p[i] ) ); printf("%04x ", (u_short)ntohs( p[i] ) ); } printf("\n" ); sc->rx_no_first = sc->rx_no_mbuf = sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0; ep_fset( F_RX_FIRST ); sc->top = sc->mcur = 0; return 0;}/********************************************************************************** * * DESCRIPTION: * Initializes the card. * The order in here seems important. Otherwise we may not receive interrupts. ?! * * RETURNS: nothing. * **********************************************************************************/static void epinit( struct ep_softc *sc ){ register struct ifnet *ifp = &sc->arpcom.ac_if; int i, j; while( inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS ) ; GO_WINDOW(0); outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); GO_WINDOW(4); outw(BASE + EP_W4_MEDIA_TYPE, DISABLE_UTP); GO_WINDOW(0); /* Disable the card */ outw(BASE + EP_W0_CONFIG_CTRL, 0); /* Enable the card */ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); GO_WINDOW(2); /* Reload the ether_addr. */ for (i = 0; i < 6; i++) outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); outw(BASE + EP_COMMAND, RX_RESET); outw(BASE + EP_COMMAND, TX_RESET); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); /* Window 1 is operating window */ GO_WINDOW(1); for (i = 0; i < 31; i++) inb(BASE + EP_W1_TX_STATUS); /* get rid of stray intr's */ outw(BASE + EP_COMMAND, ACK_INTR | 0xff); outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); if (ifp->if_flags & IFF_PROMISC) outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_GROUP | FIL_BRDCST | FIL_ALL); else outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_GROUP | FIL_BRDCST); /* * S.B. * * Now behavior was slightly changed: * * if any of flags link[0-2] is used and its connector is * physically present the following connectors are used: * * link0 - AUI * highest precedence * link1 - BNC * link2 - UTP * lowest precedence * * If none of them is specified then * connector specified in the EEPROM is used * (if present on card or AUI if not). * */ /* Set the xcvr. */ if (ifp->if_flags & IFF_LINK0 && sc->ep_connectors & AUI) { i = ACF_CONNECTOR_AUI; } else if (ifp->if_flags & IFF_LINK1 && sc->ep_connectors & BNC) { i = ACF_CONNECTOR_BNC; } else if (ifp->if_flags & IFF_LINK2 && sc->ep_connectors & UTP) { i = ACF_CONNECTOR_UTP; } else { i = sc->ep_connector; } GO_WINDOW(0); j = inw(BASE + EP_W0_ADDRESS_CFG) & 0x3fff; outw(BASE + EP_W0_ADDRESS_CFG, j | (i << ACF_CONNECTOR_BITS)); switch(i) { case ACF_CONNECTOR_UTP: if (sc->ep_connectors & UTP) { GO_WINDOW(4); outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); } break; case ACF_CONNECTOR_BNC: if (sc->ep_connectors & BNC) { outw(BASE + EP_COMMAND, START_TRANSCEIVER); Wait_X_ms( 1 ); } break; case ACF_CONNECTOR_AUI: /* nothing to do */ break; default: printf("ep%d: strange connector type in EEPROM: assuming AUI\n", sc->unit); break; } outw(BASE + EP_COMMAND, RX_ENABLE); outw(BASE + EP_COMMAND, TX_ENABLE); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ sc->rx_no_first = sc->rx_no_mbuf = sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0; ep_fset(F_RX_FIRST); if( sc->top ) { m_freem( sc->top ); sc->top = sc->mcur = 0; } outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH); outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16); /* * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up * any that we had in case we're being called from intr or somewhere * else. */ GO_WINDOW(1);}static const char padmap[] = {0, 3, 2, 1};/********************************************************************************** * * DESCRIPTION: Routine to transmit frames to the card. * * RETURNS: nothing. * **********************************************************************************/static void epstart( struct ifnet *ifp ){ register struct ep_softc *sc = ifp->if_softc; register u_int len; register struct mbuf *m; struct mbuf *top; int pad; while( inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS ) ;startagain: /* printk( "S-" ); */ /* Sneak a peek at the next packet */ m = ifp->if_snd.ifq_head; if (m == 0) { ifp->if_flags &= ~IFF_OACTIVE; return; } for( len = 0, top = m; m; m = m->m_next ) len += m->m_len; pad = padmap[ len & 3 ]; /* * The 3c509 automatically pads short packets to minimum ethernet length, * but we drop packets that are too large. Perhaps we should truncate * them instead? */ if( len + pad > ETHER_MAX_LEN ) { /* packet is obviously too large: toss it */ ++ifp->if_oerrors; IF_DEQUEUE( &ifp->if_snd, m ); m_freem( m ); goto readcheck; } if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { /* no room in FIFO */ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); /* make sure */ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { ifp->if_flags |= IFF_OACTIVE; return; } } IF_DEQUEUE( &ifp->if_snd, m ); outw(BASE + EP_W1_TX_PIO_WR_1, len); outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ for (top = m; m != 0; m = m->m_next) { if( ep_ftst(F_ACCESS_32_BITS ) ) { outsl( BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4 ); if( m->m_len & 3 ) outsb(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t) + (m->m_len & (~3)), m->m_len & 3 ); } else { outsw( BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2 ); if( m->m_len & 1 ) outb( BASE + EP_W1_TX_PIO_WR_1, *(mtod(m, caddr_t) + m->m_len - 1) ); } } while( pad-- ) { outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ } ifp->if_timer = 2; ifp->if_opackets++; m_freem(top);/* goto startagain; */ /* * Is another packet coming in? We don't want to overflow the tiny RX * fifo. */readcheck: if( inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK ) { /* * we check if we have packets left, in that case we prepare to come * back later */ if( ifp->if_snd.ifq_head ) { outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); } return; } goto startagain;}/********************************************************************************** * * DESCRIPTION: Routine to read frames from the card. * * RETURNS: nothing. * **********************************************************************************/static void epread( register struct ep_softc *sc ){ struct ether_header *eh; struct mbuf *top, *mcur, *m; struct ifnet *ifp; int lenthisone; short rx_fifo2, status; register short rx_fifo; ifp = &sc->arpcom.ac_if; status = inw( BASE + EP_W1_RX_STATUS );read_again: if (status & ERR_RX) { ++ifp->if_ierrors; if( status & ERR_RX_OVERRUN ) { /* * we can think the rx latency is actually greather than we * expect */ if( ep_ftst(F_RX_FIRST) ) sc->rx_overrunf++; else sc->rx_overrunl++; } goto out; } rx_fifo = rx_fifo2 = status & RX_BYTES_MASK; if( ep_ftst( F_RX_FIRST ) ) { MGETHDR( m, M_DONTWAIT, MT_DATA ); if( !m ) goto out; if( rx_fifo >= MINCLSIZE ) MCLGET( m, M_DONTWAIT ); sc->top = sc->mcur = top = m;#define EROUND ((sizeof(struct ether_header) + 3) & ~3)#define EOFF (EROUND - sizeof(struct ether_header)) top->m_data += EOFF; /* Read what should be the header. */ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), sizeof(struct ether_header) / 2); top->m_len = sizeof(struct ether_header); rx_fifo -= sizeof(struct ether_header); sc->cur_len = rx_fifo2; } else { /* come here if we didn't have a complete packet last time */ top = sc->top; m = sc->mcur; sc->cur_len += rx_fifo2; } /* Reads what is left in the RX FIFO */ while (rx_fifo > 0) { lenthisone = min( rx_fifo, M_TRAILINGSPACE(m) ); if( lenthisone == 0 ) { /* no room in this one */ mcur = m; MGET(m, M_WAIT, MT_DATA); if (!m) goto out; if (rx_fifo >= MINCLSIZE) MCLGET(m, M_WAIT); m->m_len = 0; mcur->m_next = m; lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); } if( ep_ftst( F_ACCESS_32_BITS ) ) { /* default for EISA configured cards*/ insl( BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 4); m->m_len += (lenthisone & ~3); if (lenthisone & 3) insb(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone & 3); m->m_len += (lenthisone & 3); } else { insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 2); m->m_len += lenthisone; if( lenthisone & 1 ) *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); } rx_fifo -= lenthisone; } if( status & ERR_RX_INCOMPLETE) { /* we haven't received the complete packet */ sc->mcur = m; sc->rx_no_first++; /* to know how often we come here */ ep_frst( F_RX_FIRST ); if( !((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE) ) { /* we see if by now, the packet has completly arrived */ goto read_again; } outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_NEXT_EARLY_THRESH); return; } outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); ++ifp->if_ipackets; ep_fset(F_RX_FIRST); top->m_pkthdr.rcvif = &sc->arpcom.ac_if; top->m_pkthdr.len = sc->cur_len; eh = mtod(top, struct ether_header *); m_adj(top, sizeof(struct ether_header)); ether_input(ifp, eh, top); sc->top = 0; while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH); return;out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); if (sc->top) { m_freem(sc->top); sc->top = 0; sc->rx_no_mbuf++; } ep_fset(F_RX_FIRST); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);}/********************************************************************************** * * DESCRIPTION: * This routine handles interrupts. It is called from the "RX" task whenever * the ISR post an event to the task. * This is basically the "isr" from the FreeBSD driver. * * RETURNS: nothing. * **********************************************************************************/static void ep_intr( struct ep_softc *sc ){ register int status; struct ifnet *ifp; ifp = &sc->arpcom.ac_if;rescan: /* printk( "I-" ); */ while( ( status = inw(BASE + EP_STATUS)) & S_5_INTS ) { /* first acknowledge all interrupt sources */ outw( BASE + EP_COMMAND, ACK_INTR | ( status & S_MASK ) ); if( status & ( S_RX_COMPLETE | S_RX_EARLY ) ) { epread( sc ); continue; } if (status & S_TX_AVAIL) { /* we need ACK */ ifp->if_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; GO_WINDOW(1); inw(BASE + EP_W1_FREE_TX); epstart(ifp); } if (status & S_CARD_FAILURE) { ifp->if_timer = 0; printf("\nep%d:\n\tStatus: %x\n", sc->unit, status); GO_WINDOW(4); printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG)); printf("\tStat: %x\n", sc->stat); printf("\tIpackets=%ld, Opackets=%ld\n", ifp->if_ipackets, ifp->if_opackets); printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n", sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf, sc->rx_overrunl, sc->tx_underrun); printf("ep%d: Status: %x (input buffer overflow)\n", sc->unit, status); ++ifp->if_ierrors; epinit(sc); return; } if (status & S_TX_COMPLETE) { ifp->if_timer = 0; /* we need ACK. we do it at the end */ /* * We need to read TX_STATUS until we get a 0 status in order to * turn off the interrupt flag. */ while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { if (status & TXS_SUCCES_INTR_REQ) ; else if( status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION ) ) { outw(BASE + EP_COMMAND, TX_RESET); if (status & TXS_UNDERRUN) { sc->tx_underrun++; } else { if( status & TXS_JABBER ) ; else /* TXS_MAX_COLLISION - we shouldn't get here */ ++ifp->if_collisions; } ++ifp->if_oerrors; outw(BASE + EP_COMMAND, TX_ENABLE); /* * To have a tx_avail_int but giving the chance to the * Reception */ if( ifp->if_snd.ifq_head ) { outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); } } outb( BASE + EP_W1_TX_STATUS, 0x0 ); /* pops up the next status */ } /* while */ ifp->if_flags &= ~IFF_OACTIVE; GO_WINDOW(1); inw(BASE + EP_W1_FREE_TX); epstart( ifp ); } /* end TX_COMPLETE */ } outw(BASE + EP_COMMAND, C_INTR_LATCH); /* ACK int Latch */ if( (status = inw(BASE + EP_STATUS) ) & S_5_INTS ) goto rescan; /* re-enable Ints */ outw( BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS ); /* printk( "I+" ); */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -