📄 dm9000e.c
字号:
len = (len + 1) / 2; while (len--) { *wp++ = inw(NIC_DATA_ADDR); }}/*! * \brief Fetch the next packet out of the receive ring buffer. * * \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 int NicGetPacket(NICINFO * ni, NETBUF ** nbp){ int rc = -1; u_short fsw; u_short fbc; *nbp = NULL; /* Disable NIC interrupts. */ NutIrqDisable(&NIC_SIGNAL); /* * Read the status word w/o auto increment. If zero, no packet is * available. Otherwise it should be set to one. Any other value * indicates a weird chip crying for reset. */ nic_inb(NIC_MRCMDX); fsw = inb(NIC_DATA_ADDR); if (fsw > 1) { ni->ni_insane = 1; } else if (fsw) { /* Now read status word and byte count with auto increment. */ outb(NIC_BASE_ADDR, NIC_MRCMD); if (ni->ni_iomode == NIC_ISR_M16) { fsw = inw(NIC_DATA_ADDR); fbc = inw(NIC_DATA_ADDR); } else { fsw = inb(NIC_DATA_ADDR) + ((u_short) inb(NIC_DATA_ADDR) << 8); fbc = inb(NIC_DATA_ADDR) + ((u_short) inb(NIC_DATA_ADDR) << 8); } /* * Receiving long packets is unexpected, because we disabled * this during initialization. Let's declare the chip insane. * Short packets will be handled by the caller. */ if (fbc > 1536) { ni->ni_insane = 1; } else { /* * The high byte of the status word contains a copy of the * receiver status register. */ fsw >>= 8; fsw &= NIC_RSR_ERRORS;#ifdef NUT_PERMON /* Update statistics. */ if (fsw) { if (RxStatus & NIC_RSR_CE) { ni->ni_crc_errors++; } else if (RxStatus & NIC_RSR_FOE) { ni->ni_overruns++; } else { ni->ni_rx_missed_errors++; } } else { ni->ni_rx_packets++; }#endif /* * If we got an error packet or failed to allocated the * buffer, then silently discard the packet. */ if (fsw || (*nbp = NutNetBufAlloc(0, NBAF_DATALINK, fbc - 4)) == NULL) { if (ni->ni_iomode == NIC_ISR_M16) { fbc = (fbc + 1) / 2; while (fbc--) { fsw = inw(NIC_DATA_ADDR); } } else { while (fbc--) { fsw = inb(NIC_DATA_ADDR); } } } else { if (ni->ni_iomode == NIC_ISR_M16) { /* Read packet data from 16 bit bus. */ NicRead16((*nbp)->nb_dl.vp, (*nbp)->nb_dl.sz); /* Read packet CRC. */ fsw = inw(NIC_DATA_ADDR); fsw = inw(NIC_DATA_ADDR); } else { /* Read packet data from 8 bit bus. */ NicRead8((*nbp)->nb_dl.vp, (*nbp)->nb_dl.sz); /* Read packet CRC. */ fsw = inb(NIC_DATA_ADDR); fsw = inb(NIC_DATA_ADDR); fsw = inb(NIC_DATA_ADDR); fsw = inb(NIC_DATA_ADDR); } /* Return success. */ rc = 0; } } } /* Enable NIC interrupts if the chip is sane. */ if (ni->ni_insane == 0) { NutIrqEnable(&NIC_SIGNAL); } return rc;}/*! * \brief Load a packet into the nic's transmit ring buffer. * * \param nb Network buffer structure containing the packet to be sent. * The structure must have been allocated by a previous * call NutNetBufAlloc(). This routine will automatically * release the buffer in case of an error. * * \return 0 on success, -1 in case of any errors. Errors * will automatically release the network buffer * structure. */static int NicPutPacket(NICINFO * ni, NETBUF * nb){ int rc = -1; u_short sz; /* * Calculate the number of bytes to be send. Do not send packets * larger than the Ethernet maximum transfer unit. The MTU * consist of 1500 data bytes plus the 14 byte Ethernet header * plus 4 bytes CRC. We check the data bytes only. */ if ((sz = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz) > ETHERMTU) { return -1; } sz += nb->nb_dl.sz; if (sz & 1) { sz++; } /* Disable interrupts. */ NutIrqDisable(&NIC_SIGNAL); /* TODO: Check for link. */ if (ni->ni_insane == 0) { /* Enable data write. */ outb(NIC_BASE_ADDR, NIC_MWCMD); /* Transfer the Ethernet frame. */ if (ni->ni_iomode == NIC_ISR_M16) { NicWrite16(nb->nb_dl.vp, nb->nb_dl.sz); NicWrite16(nb->nb_nw.vp, nb->nb_nw.sz); NicWrite16(nb->nb_tp.vp, nb->nb_tp.sz); NicWrite16(nb->nb_ap.vp, nb->nb_ap.sz); } else { NicWrite8(nb->nb_dl.vp, nb->nb_dl.sz); NicWrite8(nb->nb_nw.vp, nb->nb_nw.sz); NicWrite8(nb->nb_tp.vp, nb->nb_tp.sz); NicWrite8(nb->nb_ap.vp, nb->nb_ap.sz); } /* If no packet is queued, start the transmission. */ if (ni->ni_tx_queued == 0) { nic_outb(NIC_TXPL, (u_char) sz); nic_outb(NIC_TXPL + 1, (u_char) (sz >> 8)); nic_outb(NIC_TCR, NIC_TCR_TXREQ); } /* ...otherwise mark this packet queued. */ else { ni->ni_tx_quelen = sz; } ni->ni_tx_queued++; rc = 0;#ifdef NUT_PERFMON ni->ni_tx_packets++;#endif } /* Enable interrupts. */ NutIrqEnable(&NIC_SIGNAL); /* If the controller buffer is filled with two packets, then wait for the first being sent out. */ if (rc == 0 && ni->ni_tx_queued > 1) { NutEventWait(&ni->ni_tx_rdy, 500); } return rc;}/*! * \brief Fire up the network interface. * * NIC interrupts must be disabled when calling this function. * * \param mac Six byte unique MAC address. */static int NicStart(CONST u_char * mac){ int i; int link_wait = 20; /* Power up the PHY. */ nic_outb(NIC_GPR, 0); NutDelay(5); /* Software reset with MAC loopback. */ nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC); NutDelay(5); nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC); NutDelay(5); /* * PHY power down followed by PHY power up. This should activate * the auto sense link. */ nic_outb(NIC_GPR, 1); nic_outb(NIC_GPR, 0); /* Set MAC address. */ for (i = 0; i < 6; i++) { nic_outb(NIC_PAR + i, mac[i]); } /* Enable broadcast receive. */ for (i = 0; i < 7; i++) { nic_outb(NIC_MAR + i, 0); } nic_outb(NIC_MAR + 7, 0x80); /* Clear interrupts. */ nic_outb(NIC_ISR, NIC_ISR_ROOS | NIC_ISR_ROS | NIC_ISR_PTS | NIC_ISR_PRS); /* Enable receiver. */ nic_outb(NIC_RCR, NIC_RCR_DIS_LONG | NIC_RCR_DIS_CRC | NIC_RCR_RXEN); /* Wait for link. */ for (link_wait = 20;; link_wait--) { if (phy_inw(NIC_PHY_BMSR) & NIC_PHY_BMSR_ANCOMPL) { break; } if (link_wait == 0) { return -1; } NutSleep(200); } /* Enable interrupts. */ nic_outb(NIC_IMR, NIC_IMR_PAR | NIC_IMR_PTM | NIC_IMR_PRM); return 0;}/*! \fn NicRxLanc(void *arg) * \brief NIC receiver thread. * */THREAD(NicRxLanc, arg){ NUTDEVICE *dev; IFNET *ifn; NICINFO *ni; NETBUF *nb; dev = arg; ifn = (IFNET *) dev->dev_icb; ni = (NICINFO *) dev->dev_dcb; /* * 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. */ for (;;) { if (*((u_long *) (ifn->if_mac)) && *((u_long *) (ifn->if_mac)) != 0xFFFFFFFFUL) { break; } NutSleep(63); } /* * Do not continue unless we managed to start the NIC. We are * trapped here if the Ethernet link cannot be established. * This happens, for example, if no Ethernet cable is plugged * in. */ while (NicStart(ifn->if_mac)) { NutSleep(1000); } /* Initialize the access mutex. */ NutEventPost(&ni->ni_mutex); /* Run at high priority. */ NutThreadSetPriority(9); /* Enable interrupts for P10. */ outr(PIO_PDR, _BV(10)); NutIrqEnable(&NIC_SIGNAL); for (;;) { /* * Wait for the arrival of new packets or poll the receiver * every two seconds. */ NutEventWait(&ni->ni_rx_rdy, 2000); /* * Fetch all packets from the NIC's internal buffer and pass * them to the registered handler. */ while (NicGetPacket(ni, &nb) == 0) { /* Discard short packets. */ if (nb->nb_dl.sz < 60) { NutNetBufFree(nb); } else { (*ifn->if_recv) (dev, nb); } } /* We got a weird chip, try to restart it. */ while (ni->ni_insane) { if (NicStart(ifn->if_mac) == 0) { ni->ni_insane = 0; ni->ni_tx_queued = 0; ni->ni_tx_quelen = 0; NutIrqEnable(&NIC_SIGNAL); } else { NutSleep(1000); } } }}/*! * \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 DmOutput(NUTDEVICE * dev, NETBUF * nb){ static u_long mx_wait = 5000; int rc = -1; NICINFO *ni = (NICINFO *) dev->dev_dcb; /* * After initialization we are waiting for a long time to give * the PHY a chance to establish an Ethernet link. */ while (rc) { if (NutEventWait(&ni->ni_mutex, mx_wait)) { break; } /* Check for packet queue space. */ if (ni->ni_tx_queued > 1) { if (NutEventWait(&ni->ni_tx_rdy, 500)) { /* No queue space. Release the lock and give up. */ NutEventPost(&ni->ni_mutex); break; } } else if (NicPutPacket(ni, nb) == 0) { /* Ethernet works. Set a long waiting time in case we temporarly lose the link next time. */ rc = 0; mx_wait = 5000; } NutEventPost(&ni->ni_mutex); } /* * Probably no Ethernet link. Significantly reduce the waiting * time, so following transmission will soon return an error. */ if (rc) { mx_wait = 500; } return rc;}/*! * \brief Initialize Ethernet hardware. * * Resets the LAN91C111 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 DmInit(NUTDEVICE * dev){ u_long id; NICINFO *ni = (NICINFO *) dev->dev_dcb; /* Probe chip by verifying the identifier registers. */ id = (u_long) nic_inb(NIC_VID); id |= (u_long) nic_inb(NIC_VID + 1) << 8; id |= (u_long) nic_inb(NIC_PID) << 16; id |= (u_long) nic_inb(NIC_PID + 1) << 24; if (id != 0x90000A46) { return -1; } /* Reset chip. */ if (NicReset()) { return -1; } /* Clear NICINFO structure. */ memset(ni, 0, sizeof(NICINFO)); /* Determine bus mode. We do not support 32 bit access. */ ni->ni_iomode = nic_inb(NIC_ISR) & NIC_ISR_IOM; if (ni->ni_iomode == NIC_ISR_M32) { return -1; } /* Register interrupt handler. */ if (NutRegisterIrqHandler(&NIC_SIGNAL, NicInterrupt, dev)) { return -1; } /* Start the receiver thread. */ if (NutThreadCreate("rxi1", NicRxLanc, dev, NUT_THREAD_NICRXSTACK) == NULL) { return -1; } return 0;}static NICINFO dcb_eth0;/*! * \brief Network interface information structure. * * Used to call. */static IFNET ifn_eth0 = { IFT_ETHER, /*!< \brief Interface type, if_type. */ {0, 0, 0, 0, 0, 0}, /*!< \brief Hardware net address, if_mac. */ 0, /*!< \brief IP address, if_local_ip. */ 0, /*!< \brief Remote IP address for point to point, if_remote_ip. */ 0, /*!< \brief IP network mask, if_mask. */ ETHERMTU, /*!< \brief Maximum size of a transmission unit, if_mtu. */ 0, /*!< \brief Packet identifier, if_pkt_id. */ 0, /*!< \brief Linked list of arp entries, arpTable. */ NutEtherInput, /*!< \brief Routine to pass received data to, if_recv(). */ DmOutput, /*!< \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 devDM9000E = { 0, /*!< \brief Pointer to next device. */ {'e', 't', 'h', '0', 0, 0, 0, 0, 0}, /*!< \brief Unique device name. */ IFTYP_NET, /*!< \brief Type of device. */ 0, /*!< \brief Base address. */ 0, /*!< \brief First interrupt number. */ &ifn_eth0, /*!< \brief Interface control block. */ &dcb_eth0, /*!< \brief Driver control block. */ DmInit, /*!< \brief Driver initialization routine. */ 0, /*!< \brief Driver specific control function. */ 0, /*!< \brief Read from device. */ 0, /*!< \brief Write to device. */#ifdef __HARVARD_ARCH__ 0, /*!< \brief Write from program space data to device. */#endif 0, /*!< \brief Open a device or file. */ 0, /*!< \brief Close a device or file. */ 0 /*!< \brief Request file size. */};/*@}*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -