⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nicrtl.c

📁 avr上的RTOS
💻 C
📖 第 1 页 / 共 3 页
字号:
     * 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 + -