📄 ne2000.c
字号:
next = data; inport_byte (dport, data); len = data; inport_byte (dport, data); len |= data << 8; } else { /* Word transfers */ inport_word (dport, statnext); inport_word (dport, len); next = statnext >> 8; } outport_byte (port + ISR, MSK_RDC); if (next >= NE_STOP_PAGE) next = NE_FIRST_RX_PAGE; /* The first four bytes of the length are the buffer header. */ len -= 4; startaddr = startpage * NE_PAGE_SIZE + 4; MGETHDR (m, M_WAIT, MT_DATA); MCLGET (m, M_WAIT); m->m_pkthdr.rcvif = ifp; p = mtod (m, unsigned char *); m->m_len = m->m_pkthdr.len = len - sizeof (struct ether_header); toend = NE_STOP_PAGE * NE_PAGE_SIZE - startaddr; if (toend < len) { ne_read_data (sc, startaddr, toend, p); p += toend; len -= toend; startaddr = NE_FIRST_RX_PAGE * NE_PAGE_SIZE; } if (len > 0) ne_read_data (sc, startaddr, len, p); eh = mtod (m, struct ether_header *); m->m_data += sizeof (struct ether_header); ether_input (ifp, eh, m); ++sc->stats.rx_packets; outport_byte (port + BNRY, next - 1); } if (sc->overrun) { outport_byte (port + ISR, MSK_OVW); outport_byte (port + TCR, 0); if (sc->resend) outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA); sc->resend = 0; sc->overrun = 0; } /* Reenable device interrupts. */ outport_byte (port + IMR, NE_INTERRUPTS); }}/* Load an NE2000 packet onto the device. */static voidne_loadpacket (struct ne_softc *sc, struct mbuf *m){ unsigned int port = sc->port; unsigned int dport = port + DATAPORT; struct mbuf *mhold = m; int leftover; unsigned char leftover_data; int timeout;#ifdef DEBUG_NE2000 printk ("Uploading NE2000 packet\n");#endif /* Reset remote DMA complete flag. */ outport_byte (port + ISR, MSK_RDC); /* Write out the count. */ outport_byte (port + RBCR0, m->m_pkthdr.len); outport_byte (port + RBCR1, m->m_pkthdr.len >> 8); sc->sendlen[sc->nextavail] = m->m_pkthdr.len; /* Tell the device which address we want to write to. */ outport_byte (port + RSAR0, 0); outport_byte (port + RSAR1, NE_FIRST_TX_PAGE + (sc->nextavail * NE_TX_PAGES)); /* Set up the write. */ outport_byte (port + CMDR, MSK_PG0 | MSK_RWR | MSK_STA); /* Transfer the mbuf chain to device memory. NE2000 devices require that the data be transferred as words, so we need to handle odd length mbufs. Never occurs if we force byte transfers. */ leftover = 0; leftover_data = '\0'; for (; m != NULL; m = m->m_next) { int len; unsigned char *data; len = m->m_len; if (len == 0) continue; data = mtod (m, unsigned char *); if (leftover) { unsigned char next; /* Data left over from previous mbuf in chain. */ next = *data++; --len; outport_word (dport, leftover_data | (next << 8)); leftover = 0; } /* If using byte transfers, len always ends up as zero so there are no leftovers. */ if (sc->byte_transfers) while (len > 0) { outport_byte (dport, *data++); len--; } else while (len > 1) { outport_word (dport, data[0] | (data[1] << 8)); data += 2; len -= 2; } if (len > 0) { leftover = 1; leftover_data = *data++; } } if (leftover) outport_word (dport, leftover_data); m_freem (mhold); /* Wait for the device to complete accepting the data, with a limiting counter so that we don't wait too long. */ for (timeout = 0; timeout < 100; ++timeout) { unsigned char status; inport_byte (port + ISR, status);#ifdef DEBUG_NE2000 if ((status &~ MSK_RDC) != 0) printk ("Status 0x%x while waiting for acknowledgement of uploaded packet\n", status);#endif if ((status & MSK_RDC) != 0) { outport_byte (port + ISR, MSK_RDC); break; } } if (timeout >= 100) printk ("Timed out waiting for acknowledgement of uploaded NE2000 packet\n"); ++sc->nextavail; if (sc->nextavail == NE_TX_BUFS) sc->nextavail = 0;}/* Tell the NE2000 to transmit a buffer whose contents we have already loaded onto the device. */static voidne_transmit (struct ne_softc *sc){ unsigned int port = sc->port; int len;#ifdef DEBUG_NE2000 printk ("Transmitting NE2000 packet\n");#endif len = sc->sendlen[sc->nextsend]; if (len < ET_MINLEN) len = ET_MINLEN; outport_byte (port + TBCR0, len); outport_byte (port + TBCR1, len >> 8); outport_byte (port + TPSR, NE_FIRST_TX_PAGE + (sc->nextsend * NE_TX_PAGES)); outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA); ++sc->nextsend; if (sc->nextsend == NE_TX_BUFS) sc->nextsend = 0; ++sc->stats.tx_packets;}/* The NE2000 packet transmit daemon. This task is started when the NE2000 driver is initialized. */static voidne_tx_daemon (void *arg){ struct ne_softc *sc = (struct ne_softc *) arg; unsigned int port = sc->port; struct ifnet *ifp = &sc->arpcom.ac_if; while (1) { rtems_event_set events; /* Wait for a packet to be ready for sending, or for there to be room for another packet in the device memory. */ rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);#ifdef DEBUG_NE2000 printk ("ne_tx_daemon\n");#endif /* This daemon handles both uploading data onto the device and telling the device to transmit data which has been uploaded. These are separate tasks, because while the device is transmitting one buffer we will upload another. */ /* Don't let the device interrupt us now. */ outport_byte (port + IMR, 0); while (1) { struct mbuf *m; /* If the device is not transmitting a packet, and we have uploaded a packet, tell the device to transmit it. */ if (! sc->transmitting && sc->inuse > 0) { sc->transmitting = 1; ne_transmit (sc); } /* If we don't have any more buffers to send, quit now. */ if (ifp->if_snd.ifq_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; break; } /* Allocate a buffer to load data into. If there are none available, quit until a buffer has been transmitted. */ if (sc->inuse >= NE_TX_BUFS) break; ++sc->inuse; IF_DEQUEUE (&ifp->if_snd, m); if (m == NULL) panic ("ne_tx_daemon"); ne_loadpacket (sc, m); /* Check the device status. It may have finished transmitting the last packet. */ ne_check_status (sc); } /* Reenable device interrupts. */ outport_byte (port + IMR, NE_INTERRUPTS); }}/* Start sending an NE2000 packet. */static voidne_start (struct ifnet *ifp){ struct ne_softc *sc = ifp->if_softc; /* Tell the transmit daemon to wake up and send a packet. */ rtems_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT); ifp->if_flags |= IFF_OACTIVE;}/* Initialize and start and NE2000. */static voidne_init (void *arg){ struct ne_softc *sc = (struct ne_softc *) arg; struct ifnet *ifp = &sc->arpcom.ac_if; if (sc->tx_daemon_tid == 0) { sc->inuse = 0; sc->nextavail = 0; sc->nextsend = 0; sc->transmitting = 0; ne_init_hardware (sc); sc->tx_daemon_tid = rtems_bsdnet_newproc ("SCtx", 4096, ne_tx_daemon, sc); sc->rx_daemon_tid = rtems_bsdnet_newproc ("SCrx", 4096, ne_rx_daemon, sc); } ifp->if_flags |= IFF_RUNNING;}/* Stop an NE2000. */static voidne_stop (struct ne_softc *sc){ unsigned int port = sc->port; int i; sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING; /* Stop everything. */ outport_byte (port + CMDR, MSK_STP | MSK_RD2); /* Wait for the interface to stop, using I as a time limit. */ for (i = 0; i < 5000; ++i) { unsigned char status; inport_byte (port + ISR, status); if ((status & MSK_RST) != 0) break; } sc->inuse = 0; sc->nextavail = 0; sc->nextsend = 0; sc->transmitting = 0;}/* Show NE2000 interface statistics. */static voidne_stats (struct ne_softc *sc){ printf (" Received packets: %-8lu", sc->stats.rx_packets); printf (" Transmitted packets: %-8lu\n", sc->stats.tx_packets); printf (" Receive acks: %-8lu", sc->stats.rx_acks); printf (" Transmit acks: %-8lu\n", sc->stats.tx_acks); printf (" Packet overruns: %-8lu", sc->stats.overruns); printf (" Frame errors: %-8lu\n", sc->stats.rx_frame_errors); printf (" CRC errors: %-8lu", sc->stats.rx_crc_errors); printf (" Missed packets: %-8lu\n", sc->stats.rx_missed_errors); printf (" Interrupts: %-8lu\n", sc->stats.interrupts);}/* NE2000 driver ioctl handler. */static intne_ioctl (struct ifnet *ifp, int command, caddr_t data){ struct ne_softc *sc = ifp->if_softc; int error = 0; switch (command) { case SIOCGIFADDR: case SIOCSIFADDR: error = ether_ioctl (ifp, command, data); break; case SIOCSIFFLAGS: switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { case IFF_RUNNING: ne_stop (sc); break; case IFF_UP: ne_init (sc); break; case IFF_UP | IFF_RUNNING: ne_stop (sc); ne_init (sc); break; default: break; } break; case SIO_RTEMS_SHOW_STATS: ne_stats (sc); break; /* FIXME: Multicast commands must be added here. */ default: error = EINVAL; break; } return error;}/* Attach an NE2000 driver to the system. */intrtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config){ int i; struct ne_softc *sc; struct ifnet *ifp; int mtu; /* Find a free driver. */ sc = NULL; for (i = 0; i < NNEDRIVER; ++i) { sc = &ne_softc[i]; ifp = &sc->arpcom.ac_if; if (ifp->if_softc == NULL) break; } if (sc == NULL) { printf ("Too many NE2000 drivers.\n"); return 0; } memset (sc, 0, sizeof *sc); /* Check whether we do byte-wide or word-wide transfers. */ #ifdef NE2000_BYTE_TRANSFERS sc->byte_transfers = TRUE;#else sc->byte_transfers = FALSE;#endif /* Handle the options passed in by the caller. */ if (config->mtu != 0) mtu = config->mtu; else mtu = ETHERMTU; if (config->irno != 0) sc->irno = config->irno; else { /* We use 5 as the default IRQ. */ sc->irno = 5; } if (config->port != 0) sc->port = config->port; else { /* We use 0x300 as the default IO port number. */ sc->port = 0x300; } sc->accept_broadcasts = ! config->ignore_broadcast; if (config->hardware_address != NULL) memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); else { unsigned char prom[16]; int ia; /* Read the PROM to get the Ethernet hardware address. */ outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP); if (sc->byte_transfers) { outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS); } else { outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS); } outport_byte (sc->port + RBCR0, 0); outport_byte (sc->port + RBCR1, 0); outport_byte (sc->port + RCR, MSK_MON); outport_byte (sc->port + TCR, MSK_LOOP); outport_byte (sc->port + IMR, 0); outport_byte (sc->port + ISR, 0xff); ne_read_data (sc, 0, sizeof prom, prom); outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP); for (ia = 0; ia < ETHER_ADDR_LEN; ++ia) sc->arpcom.ac_enaddr[ia] = prom[ia * 2]; } /* Set up the network interface. */ ifp->if_softc = sc; ifp->if_unit = i + 1; ifp->if_name = "ne"; ifp->if_mtu = mtu; ifp->if_init = ne_init; ifp->if_ioctl = ne_ioctl; ifp->if_start = ne_start; ifp->if_output = ether_output; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; if (ifp->if_snd.ifq_maxlen == 0) ifp->if_snd.ifq_maxlen = ifqmaxlen; /* Attach the interface. */ if_attach (ifp); ether_ifattach (ifp); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -