📄 nicrtl.c
字号:
* a null pointer. */ if ((bnry = nic_read(NIC_PG0_BNRY) + 1) >= NIC_STOP_PAGE) bnry = NIC_FIRST_RX_PAGE; if (bnry == curr) return 0; /* * Read the NIC specific packet header. */ nic_write(NIC_PG0_RBCR0, sizeof(struct nic_pkt_header)); nic_write(NIC_PG0_RBCR1, 0); nic_write(NIC_PG0_RSAR0, 0); nic_write(NIC_PG0_RSAR1, bnry); nic_write(NIC_CR, NIC_CR_STA | NIC_CR_RD0); buf = (u_short *) & hdr; NicMcu16bitBus(); /* Switch MCU data bus to 16-bit mode */ *buf++ = nic_read_dma(); /* Read status byte and next page pointer */ *buf = nic_read_dma(); /* Read frame length */ NicMcu8bitBus(); /* Switch MCU data bus to 8-bit mode */ NicCompleteDma(base);#ifdef __BIG_ENDIAN__ hdr.ph_size = __byte_swap2(hdr.ph_size);#endif /* * Check packet length. */ if (hdr.ph_size < 60 + sizeof(struct nic_pkt_header) || hdr.ph_size > 1518 + sizeof(struct nic_pkt_header)) { return dflg ? 0 : (NETBUF *) - 1; } /* * Calculate the page of the next packet. If it differs from the * pointer in the packet header, we discard the whole buffer * and return a null pointer. */ 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) { return dflg ? 0 : (NETBUF *) - 1; } nextpg = nextpg1; } /* * Check packet status. It should have set bit 0, but * even without this bit packets seem to be OK. */ if ((hdr.ph_status & 0x0E) == 0) { /* * Allocate a NETBUF. */ count = hdr.ph_size - sizeof(struct nic_pkt_header); if (dflg == 0) { nb = NutNetBufAlloc(0, NBAF_DATALINK, count); } /* * Set remote dma byte count and * start address. Don't read the * header again. */ nic_write(NIC_PG0_RBCR0, count); nic_write(NIC_PG0_RBCR1, count >> 8); nic_write(NIC_PG0_RSAR0, sizeof(struct nic_pkt_header)); nic_write(NIC_PG0_RSAR1, bnry); /* * Perform the read. */ nic_write(NIC_CR, NIC_CR_STA | NIC_CR_RD0); /* * Switch MCU data bus to 16-bit mode */ NicMcu16bitBus(); count = (count + 1) >> 1; if (nb) { buf = nb->nb_dl.vp; for (i = 0; i < count; i++) *buf++ = nic_read_dma(); } else { for (i = 0; i < count; i++) nic_read_dma(); } /* * Switch MCU data bus to 8-bit mode */ NicMcu8bitBus(); /* * Complete remote dma. */ NicCompleteDma(base); } /* * Set boundary register to the last page we read. */ if (--nextpg < NIC_FIRST_RX_PAGE) nextpg = NIC_STOP_PAGE - 1; nic_write(NIC_PG0_BNRY, nextpg); return dflg ? (NETBUF *) ((uptr_t) dflg) : nb;}/* * When a receiver buffer overflow occurs, the NIC will defer any * subsequent action until properly restarted. */static int NicOverflow(volatile u_char * base){ u_char cr; u_char resend; /* * Save the command register, so we can later determine, if NIC * transmitter has been interrupted. Then stop the NIC and wait * 5 ms for any transmission or reception in progress. */ cr = nic_read(NIC_CR); nic_write(NIC_CR, NIC_CR_STP | NIC_CR_RD2); NutDelay(WAIT5); /* * Clear remote byte count register. */ nic_write(NIC_PG0_RBCR0, 0); nic_write(NIC_PG0_RBCR1, 0); /* * Check for any incomplete transmission. */ resend = 0; if (cr & NIC_CR_TXP) { if ((nic_read(NIC_PG0_ISR) & (NIC_ISR_PTX | NIC_ISR_TXE)) == 0) resend = 1; } /* * Enter loopback mode and restart the NIC. */ nic_write(NIC_PG0_TCR, NIC_TCR_LB0); nic_write(NIC_CR, NIC_CR_STA | NIC_CR_RD2); /* * Discard all packets from the receiver buffer. */ while (NicGetPacket(base, 1)); /* * Switch from loopback to normal mode mode. */ nic_write(NIC_PG0_TCR, 0); /* * Re-invoke any interrupted transmission. */ if (resend) { nic_write(NIC_CR, NIC_CR_STA | NIC_CR_TXP | NIC_CR_RD2); } /* Finally clear the overflow flag. */ nic_write(NIC_PG0_ISR, NIC_ISR_OVW); return resend;}/* * NIC interrupt entry. */static void NicInterrupt(void *arg){ u_char isr; volatile u_char *base = (u_char *) (((NUTDEVICE *) arg)->dev_base); NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb; ni->ni_interrupts++; isr = nic_read(NIC_PG0_ISR); nic_write(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) { NicDisableInt(); NutEnableInt(); ni->ni_rx_pending++; if (NicOverflow(base)) ni->ni_tx_bsy++; else { NutEventPostFromIrq(&ni->ni_tx_rdy); } ni->ni_overruns++; NutDisableInt(); NicEnableInt(); } 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_PTX | NIC_ISR_TXE)) { ni->ni_tx_bsy = 0; NutEventPostFromIrq(&ni->ni_tx_rdy); } /* * If this is a receive interrupt, then wake up the receiver * thread. */ if (isr & NIC_ISR_PRX) { ni->ni_rx_pending++; NutEventPostFromIrq(&ni->ni_rx_rdy); } if (isr & NIC_ISR_RXE) { ni->ni_rx_frame_errors += nic_read(NIC_PG0_CNTR0); ni->ni_rx_crc_errors += nic_read(NIC_PG0_CNTR1); ni->ni_rx_missed_errors += nic_read(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; u_char rlcnt; 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); NicDisableInt(); NicStart((u_char *) (dev->dev_base), ifn->if_mac); ni->ni_curr_page = NIC_START_PAGE + TX_PAGES; NicEnableInt(); } for (;;) { /* * Wait for the arrival of new packets or check * the receiver every two second. */ if (ni->ni_rx_pending > 10) { NicDisableInt(); if (NicStart((u_char *) (dev->dev_base), ifn->if_mac) == 0) ni->ni_rx_pending = 0; ni->ni_curr_page = NIC_START_PAGE + TX_PAGES; NicEnableInt(); } NutEventWait(&ni->ni_rx_rdy, 2000); /* * Fetch all packets from the NIC's internal * buffer and pass them to the registered handler. */ rlcnt = 0; NicDisableInt(); while (rlcnt++ < 10) { if ((nb = NicGetPacket((u_char *) (dev->dev_base), 0)) == 0) break; /* The sanity check may fail because the controller is too busy. try another read before giving up and restarting the NIC. */ if (nb == (void *) -1) { if ((nb = NicGetPacket((u_char *) (dev->dev_base), 0)) == 0) break; } if (nb == (void *) -1) { if (NicStart((u_char *) (dev->dev_base), ifn->if_mac) == 0) ni->ni_rx_pending = 0; ni->ni_curr_page = NIC_START_PAGE + TX_PAGES; ni->ni_rx_pending = 0; } else { ni->ni_rx_pending = 0; ni->ni_rx_packets++; NicEnableInt(); (*ifn->if_recv) (dev, nb); NicDisableInt(); } } NicEnableInt(); }}/*! * \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; u_char retries = 10; u_char sigmod = 0; ni = (NICINFO *) dev->dev_dcb; while (ni->ni_tx_bsy && retries--) { if (NutEventWait(&ni->ni_tx_rdy, 200)) { volatile u_char *base = (u_char *) (dev->dev_base); /* * If hanging around here too long, there's something wrong * with the transmit interrupt. Force sending the packet, * if the transmitter has become inactive. */ if (NicIntIsEnabled()) { NicDisableInt(); sigmod = 1; } if ((nic_read(NIC_CR) & NIC_CR_TXP) == 0) ni->ni_tx_bsy = 0; if (sigmod) { NicEnableInt(); sigmod = 0; } } } if (NicIntIsEnabled()) { NicDisableInt(); sigmod = 1; } if (ni->ni_tx_bsy == 0) { ni->ni_tx_bsy++; if (NicPutPacket((u_char *) (dev->dev_base), nb) == 0) { ni->ni_tx_packets++; rc = 0; } } if (sigmod) NicEnableInt(); 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(). * * 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){ volatile u_char *base; 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)); base = (u_char *) (dev->dev_base); NicDisableInt(); NutInitSysIrq(); if (ifn->if_mac[0] | ifn->if_mac[1] | ifn->if_mac[2]) if (NicStart(base, ifn->if_mac)) return -1; ni->ni_curr_page = NIC_START_PAGE + TX_PAGES; /* * Start the receiver thread. */ NutThreadCreate("rxi0", NicRx, dev, 640); NutSleep(WAIT500); /* * Register interrupt handler and enable interrupts. */ if (NutRegisterIrqHandler(&RTL_SIGNAL, NicInterrupt, dev)) return -1; NicEnableInt(); return 0;}/*! * \brief Network interface device control block structure. * * Used to call. */static NICINFO dcb_eth0rtl;/*! * \brief Network interface information structure. * * Used to call. */static IFNET ifn_eth0rtl = { IFT_ETHER, /*!< \brief Interface type. */ {0, 0, 0, 0, 0, 0}, /*!< \brief Hardware net address. */ 0, /*!< \brief IP address. */ 0, /*!< \brief Remote IP address for point to point. */ 0, /*!< \brief IP network mask. */ ETHERMTU, /*!< \brief Maximum size of a transmission unit. */ 0, /*!< \brief Packet identifier. */ 0, /*!< \brief Linked list of arp entries. */ NutEtherInput, /*!< \brief Routine to pass received data to, if_recv(). */ NicOutput, /*!< \brief Driver output routine, if_send(). */ NutEtherOutput /*!< \brief Media output routine, if_output(). */};/*! * \brief Device information structure. * * A pointer to this structure must be passed to NutRegisterDevice() * to bind this Ethernet device driver to the Nut/OS kernel. * An application may then call NutNetIfConfig() with the name \em eth0 * of this driver to initialize the network interface. * */NUTDEVICE devEth0 = { 0, /* Pointer to next device. */ {'e', 't', 'h', '0', 0, 0, 0, 0, 0}, /* Unique device name. */ IFTYP_NET, /* Type of device. */ 0, /* Base address. */ 0, /* First interrupt number. */ &ifn_eth0rtl, /* Interface control block. */ &dcb_eth0rtl, /* Driver control block. */ NicInit, /* Driver initialization routine. */ 0, /* Driver specific control function. */ 0, /* Read from device. */ 0, /* Write to device. */ /* Write from program space data to device. */ 0, /* Open a device or file. */ 0, /* Close a device or file. */ 0 /* Request file size. */};/*@}*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -