📄 nicrtl.c
字号:
* Set remote dma byte count * and start address. */ NICOUTB(NIC_PG0_RBCR0, sz); NICOUTB(NIC_PG0_RBCR1, sz >> 8); NICOUTB(NIC_PG0_RSAR0, 0); NICOUTB(NIC_PG0_RSAR1, NIC_FIRST_TX_PAGE); /* * Peform the write. */ NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD1); /* * Transfer the Ethernet frame. */ NicWrite(nb->nb_dl.vp, nb->nb_dl.sz); NicWrite(nb->nb_nw.vp, nb->nb_nw.sz); NicWrite(nb->nb_tp.vp, nb->nb_tp.sz); NicWrite(nb->nb_ap.vp, nb->nb_ap.sz); /* * Add pad bytes. */ for (i = 0; i < padding; i++) NICOUTB(NIC_IOPORT, 0); /* * Complete remote dma. */ NicCompleteDma(); /* * Number of bytes to be transmitted. */ NICOUTB(NIC_PG0_TBCR0, (sz & 0xff)); NICOUTB(NIC_PG0_TBCR1, ((sz >> 8) & 0xff)); /* * First page of packet to be transmitted. */ NICOUTB(NIC_PG0_TPSR, NIC_FIRST_TX_PAGE); /* * Start transmission. */ NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_TXP | NIC_CR_RD2); sbi(EIMSK, RTL_SIGNAL_IRQ); return 0;}/*! * \brief Fetch the next packet out of the receive ring buffer. * * Nic interrupts must be disabled when calling this funtion. * * \return Pointer to an allocated ::NETBUF. If there is no * no data available, then the function returns a * null pointer. If the NIC's buffer seems to be * corrupted, a pointer to 0xFFFF is returned. */static NETBUF *NicGetPacket(void){ NETBUF *nb = 0; struct nic_pkt_header hdr; u_short count; u_char *buf; u_char nextpg; u_char bnry; u_char curr; u_short i; u_char drop = 0; /* we don't want to be interrupted by NIC owerflow */ cbi(EIMSK, RTL_SIGNAL_IRQ); /* * Get the current page pointer. It points to the page where the NIC * will start saving the next incoming packet. */ NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2 | NIC_CR_PS0); Delay16Cycles(); curr = NICINB(NIC_PG1_CURR); NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2); /* * Get the pointer to the last page we read from. The following page * is the one where we start reading. If it's equal to the current * page pointer, then there's nothing to read. In this case we return * a null pointer. */ if ((bnry = NICINB(NIC_PG0_BNRY) + 1) >= NIC_STOP_PAGE) bnry = NIC_FIRST_RX_PAGE; if (bnry == curr) { sbi(EIMSK, RTL_SIGNAL_IRQ); return 0; } /* * Read the NIC specific packet header. */ NICOUTB(NIC_PG0_RBCR0, sizeof(struct nic_pkt_header)); NICOUTB(NIC_PG0_RBCR1, 0); NICOUTB(NIC_PG0_RSAR0, 0); NICOUTB(NIC_PG0_RSAR1, bnry); buf = (u_char *) & hdr; NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD0); Delay16Cycles(); for (i = 0; i < sizeof(struct nic_pkt_header); i++) *buf++ = NICINB(NIC_IOPORT); NicCompleteDma(); /* * Check packet length. Silently discard packets of illegal size. */ if (hdr.ph_size < 60 + sizeof(struct nic_pkt_header) || /* */ hdr.ph_size > 1514 + sizeof(struct nic_pkt_header)) { drop = 1; } /* * Calculate the page of the next packet. If it differs from the * pointer in the packet header, we return with errorcode. */ nextpg = bnry + (hdr.ph_size >> 8) + ((hdr.ph_size & 0xFF) != 0); if (nextpg >= NIC_STOP_PAGE) { nextpg -= NIC_STOP_PAGE; nextpg += NIC_FIRST_RX_PAGE; } if (nextpg != hdr.ph_nextpg) { u_char nextpg1 = nextpg + 1; if (nextpg1 >= NIC_STOP_PAGE) { nextpg1 -= NIC_STOP_PAGE; nextpg1 += NIC_FIRST_RX_PAGE; } if (nextpg1 != hdr.ph_nextpg) { sbi(EIMSK, RTL_SIGNAL_IRQ); return (NETBUF *) 0xFFFF; } nextpg = nextpg1; } /* * Check packet status. It should have set bit 0, but * even without this bit packets seem to be OK. */ if (!drop && ((hdr.ph_status & 0x0E) == 0)) { /* * Allocate a NETBUF. * Omit the fcs. */ count = hdr.ph_size - 4; if ((nb = NutNetBufAlloc(0, NBAF_DATALINK, count))) { /* * Set remote dma byte count and * start address. Don't read the * header again. */ NICOUTB(NIC_PG0_RBCR0, count); NICOUTB(NIC_PG0_RBCR1, count >> 8); NICOUTB(NIC_PG0_RSAR0, sizeof(struct nic_pkt_header)); NICOUTB(NIC_PG0_RSAR1, bnry); /* * Perform the read. */ NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD0); Delay16Cycles(); NicRead(nb->nb_dl.vp, count); NicCompleteDma(); } } /* * Set boundary register to the last page we read. * This also drops packets with errors */ if (--nextpg < NIC_FIRST_RX_PAGE) nextpg = NIC_STOP_PAGE - 1; NICOUTB(NIC_PG0_BNRY, nextpg); sbi(EIMSK, RTL_SIGNAL_IRQ); return nb;}/* * \brief Handle NIC overflows. * * When a receiver buffer overflow occurs, the NIC will defer any subsequent * action until properly restarted. * * This routine is called within interrupt context, which introduces a big * problem. It waits for the last transmission to finish, which may take * several milliseconds. Since Nut/OS 3.5, we do not support nested interrupts * on AVR systems anymore. So this routine may now increase interrupt * latency in an unacceptable way. The solution might be to handle overflows * in the receiver thread. * * In any case, this routines needs a major redesign. But it has been * tested in its current form to gracefully withstand ping floods. Thanks * to Bengt Florin for contributing his code, which provides much more * stability than its predecessor. */static u_char NicOverflow(void){ u_char cr; u_char resend = 0; u_char curr; /* * Wait for any transmission in progress. Save the command register, * so we can later determine, if NIC transmitter has been interrupted. * or reception in progress. */ while (NICINB(NIC_CR) & NIC_CR_TXP); cr = NICINB(NIC_CR); /* * Get the current page pointer. It points to the page where the NIC * will start saving the next incoming packet. */ NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0); curr = NICINB(NIC_PG1_CURR); NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2); /* Clear remote byte count register. */ NICOUTB(NIC_PG0_RBCR0, 0); NICOUTB(NIC_PG0_RBCR1, 0); /* Check for any incomplete transmission. */ if ((cr & NIC_CR_TXP) && ((NICINB(NIC_PG0_ISR) & (NIC_ISR_PTX | NIC_ISR_TXE)) == 0)) { resend = 1; } /* Enter loopback mode and restart the NIC. */ NICOUTB(NIC_PG0_TCR, NIC_TCR_LB0); NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2); /* * Discard all packets from the receiver buffer. Set boundary * register to the last page we read. */ if (--curr < NIC_FIRST_RX_PAGE) { curr = NIC_STOP_PAGE - 1; } NICOUTB(NIC_PG0_BNRY, curr); /* Switch from loopback to normal mode mode. */ NICOUTB(NIC_PG0_TCR, 0); /* Re-invoke any interrupted transmission. */ if (resend) { NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_TXP | NIC_CR_RD2); } /* Finally clear the overflow flag */ NICOUTB(NIC_PG0_ISR, NIC_ISR_OVW); return resend;}/* * \brief NIC interrupt entry. */static void NicInterrupt(void *arg){ u_char isr; NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb; ni->ni_interrupts++; isr = NICINB(NIC_PG0_ISR); NICOUTB(NIC_PG0_ISR, isr); /* * Recover from receive buffer overflow. This may take some * time, so we enable global interrupts but keep NIC * interrupts disabled. */ if (isr & NIC_ISR_OVW) { /* The AVR platform uses a dedicated interrupt stack, which * forbids interrupt nesting. */#if !defined(__AVR__) cbi(EIMSK, RTL_SIGNAL_IRQ); sei();#endif NicOverflow();#if !defined(__AVR__) cli(); sbi(EIMSK, RTL_SIGNAL_IRQ);#endif ni->ni_rx_overruns++; } else { /* * If this is a transmit interrupt, then a packet has been sent. * So we can clear the transmitter busy flag and wake up the * transmitter thread. */ if (isr & NIC_ISR_TXE) ni->ni_tx_errors++; /* * If this is a receive interrupt, then wake up the receiver * thread. */ if (isr & NIC_ISR_PRX) NutEventPostFromIrq(&ni->ni_rx_rdy); if (isr & NIC_ISR_RXE) { ni->ni_rx_frame_errors += NICINB(NIC_PG0_CNTR0); ni->ni_rx_crc_errors += NICINB(NIC_PG0_CNTR1); ni->ni_rx_missed_errors += NICINB(NIC_PG0_CNTR2); } }}/*! \fn NicRx(void *arg) * \brief NIC receiver thread. * * * It runs with high priority. */THREAD(NicRx, arg){ NUTDEVICE *dev; IFNET *ifn; NICINFO *ni; NETBUF *nb; dev = arg; ifn = (IFNET *) dev->dev_icb; ni = (NICINFO *) dev->dev_dcb; NutThreadSetPriority(9); /* * This is a temporary hack. Due to a change in initialization, * we may not have got a MAC address yet. Wait until one has been * set. */ if ((ifn->if_mac[0] & ifn->if_mac[1] & ifn->if_mac[2]) == 0xFF) { while ((ifn->if_mac[0] & ifn->if_mac[1] & ifn->if_mac[2]) == 0xFF) NutSleep(125); cbi(EIMSK, RTL_SIGNAL_IRQ); NicStart(ifn->if_mac); sbi(EIMSK, RTL_SIGNAL_IRQ); } while (1) { NutEventWait(&ni->ni_rx_rdy, 0); /* * Fetch all packets from the NIC's internal * buffer and pass them to the registered handler. */ do { nb = NicGetPacket(); /* The sanity check may fail because the controller is too busy. restart the NIC. */ if ((u_short) nb == 0xFFFF) { NicStart(ifn->if_mac); ni->ni_rx_size_errors++; } else if (nb) { ni->ni_rx_packets++; (*ifn->if_recv) (dev, nb); } } while (nb); }}/*! * \brief Send Ethernet packet. * * \param dev Identifies the device to use. * \param nb Network buffer structure containing the packet to be sent. * The structure must have been allocated by a previous * call NutNetBufAlloc(). * * \return 0 on success, -1 in case of any errors. */int NicOutput(NUTDEVICE * dev, NETBUF * nb){ int rc = -1; NICINFO *ni = (NICINFO *) dev->dev_dcb; if (NicPutPacket(nb) == 0) { ni->ni_tx_packets++; rc = 0; } return rc;}/*! * \brief Initialize Ethernet hardware. * * Resets RTL8019AS Ethernet controller, initializes all required * hardware registers and starts a background thread for incoming * Ethernet traffic. * * Applications should do not directly call this function. It is * automatically executed during during device registration by * NutRegisterDevice(). Note, that base address and interrupt number * passed to NutRegisterDevice() are silently ignored by this driver * for performance reasons. These values can be changed only by * using the Nut/OS Configurator to rebuild the system. * * If the network configuration hasn't been set by the application * before registering the specified device, this function will * call NutNetLoadConfig() to get the MAC address. * * \param dev Identifies the device to initialize. */int NicInit(NUTDEVICE * dev){ IFNET *ifn; NICINFO *ni; /* * We need to know our MAC address. If no configuration is * available, load it now. */ if (confnet.cd_size == 0) NutNetLoadConfig(dev->dev_name); ifn = dev->dev_icb; memcpy(ifn->if_mac, confnet.cdn_mac, 6); ni = (NICINFO *) dev->dev_dcb; memset(ni, 0, sizeof(NICINFO)); /* * Start the receiver thread. */ NutThreadCreate("rxi5", NicRx, dev, 640); NutSleep(WAIT500); /* * Register interrupt handler and enable interrupts. */ if (NutRegisterIrqHandler(&RTL_SIGNAL, NicInterrupt, dev)) return -1; cbi(EIMSK, RTL_SIGNAL_IRQ);#ifdef RTL_IRQ_RISING_EDGE /* Support of rising edge interrupts for HW w/o inverter gate */ RTL_RISING_EDGE_MODE();#endif if (ifn->if_mac[0] | ifn->if_mac[1] | ifn->if_mac[2]) if (NicStart(ifn->if_mac)) return -1; sbi(EIMSK, RTL_SIGNAL_IRQ); return 0;}/*@}*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -