/* * Copyright (C) 2007 - 2019 Xilinx, Inc. * Copyright (C) 2021 WangHuachen. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. * */ #include "lwipopts.h" #include "xlwipconfig.h" #include "xemac_ieee_reg.h" #if !NO_SYS #ifdef OS_IS_XILKERNEL #include "xmk.h" #include "sys/process.h" #endif #endif #include "lwip/mem.h" #include "lwip/stats.h" #include "lwip/sys.h" #include "lwip/ip.h" #include "lwip/tcp.h" #include "lwip/udp.h" #include "lwip/priv/tcp_priv.h" #include "netif/etharp.h" #include "netif/xadapter.h" #ifdef XLWIP_CONFIG_INCLUDE_EMACLITE #include "netif/xemacliteif.h" #endif #ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET #include "netif/xaxiemacif.h" #endif #ifdef XLWIP_CONFIG_INCLUDE_GEM #include "netif/xemacpsif.h" #endif #if !NO_SYS #include "lwip/tcpip.h" #endif #ifdef OS_IS_FREERTOS #define THREAD_STACKSIZE 256 #define LINK_DETECT_THREAD_INTERVAL 1000 /* one second */ void link_detect_thread(void *p); #endif /* global lwip debug variable used for debugging */ int lwip_runtime_debug = 0; enum ethernet_link_status eth_link_status = ETH_LINK_UNDEFINED; u32_t phyaddrforemac; void lwip_raw_init() { ip_init(); /* Doesn't do much, it should be called to handle future changes. */ #if LWIP_UDP udp_init(); /* Clears the UDP PCB list. */ #endif #if LWIP_TCP tcp_init(); /* Clears the TCP PCB list and clears some internal TCP timers. */ /* Note: you must call tcp_fasttmr() and tcp_slowtmr() at the */ /* predefined regular intervals after this initialization. */ #endif } static enum xemac_types find_mac_type(unsigned base) { int i; for (i = 0; i < xtopology_n_emacs; i++) { if (xtopology[i].emac_baseaddr == base) return xtopology[i].emac_type; } return xemac_type_unknown; } int xtopology_find_index(unsigned base) { int i; for (i = 0; i < xtopology_n_emacs; i++) { if (xtopology[i].emac_baseaddr == base) return i; } return -1; } /* * xemac_add: this is a wrapper around lwIP's netif_add function. * The objective is to provide portability between the different Xilinx MAC's * This function can be used to add both xps_ethernetlite and xps_ll_temac * based interfaces */ struct netif * xemac_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw, unsigned char *mac_ethernet_address, unsigned mac_baseaddr) { int i; #ifdef OS_IS_FREERTOS /* Start thread to detect link periodically for Hot Plug autodetect */ sys_thread_new("link_detect_thread", link_detect_thread, netif, THREAD_STACKSIZE, tskIDLE_PRIORITY); #endif /* set mac address */ netif->hwaddr_len = 6; for (i = 0; i < 6; i++) netif->hwaddr[i] = mac_ethernet_address[i]; /* initialize based on MAC type */ switch (find_mac_type(mac_baseaddr)) { case xemac_type_xps_emaclite: #ifdef XLWIP_CONFIG_INCLUDE_EMACLITE return netif_add(netif, ipaddr, netmask, gw, (void*)(UINTPTR)mac_baseaddr, xemacliteif_init, #if NO_SYS ethernet_input #else tcpip_input #endif ); #else return NULL; #endif case xemac_type_axi_ethernet: #ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET return netif_add(netif, ipaddr, netmask, gw, (void*)(UINTPTR)mac_baseaddr, xaxiemacif_init, #if NO_SYS ethernet_input #else tcpip_input #endif ); #else return NULL; #endif #if defined (__arm__) || defined (__aarch64__) case xemac_type_emacps: #ifdef XLWIP_CONFIG_INCLUDE_GEM return netif_add(netif, ipaddr, netmask, gw, (void*)(UINTPTR)mac_baseaddr, xemacpsif_init, #if NO_SYS ethernet_input #else tcpip_input #endif ); #endif #endif default: xil_printf("unable to determine type of EMAC with baseaddress 0x%08x\r\n", mac_baseaddr); return NULL; } } #if 0 /* * The input thread calls lwIP to process any received packets. * This thread waits until a packet is received (sem_rx_data_available), * and then calls xemacif_input which processes 1 packet at a time. */ void xemacif_input_thread(struct netif *netif) { struct xemac_s *emac = (struct xemac_s *)netif->state; while (1) { /* sleep until there are packets to process * This semaphore is set by the packet receive interrupt * routine. */ sys_sem_wait(&emac->sem_rx_data_available); /* move all received packets to lwIP */ xemacif_input(netif); } } #endif int xemacif_input(struct netif *netif) { struct xemac_s *emac = (struct xemac_s *)netif->state; int n_packets = 0; switch (emac->type) { case xemac_type_xps_emaclite: #ifdef XLWIP_CONFIG_INCLUDE_EMACLITE n_packets = xemacliteif_input(netif); break; #else // print("incorrect configuration: xps_ethernetlite drivers not present?"); while(1); return 0; #endif case xemac_type_axi_ethernet: #ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET n_packets = xaxiemacif_input(netif); break; #else // print("incorrect configuration: axi_ethernet drivers not present?"); while(1); return 0; #endif #if defined (__arm__) || defined (__aarch64__) case xemac_type_emacps: #ifdef XLWIP_CONFIG_INCLUDE_GEM n_packets = xemacpsif_input(netif); break; #else xil_printf("incorrect configuration: ps7_ethernet drivers not present?\r\n"); while(1); return 0; #endif #endif default: // print("incorrect configuration: unknown temac type"); while(1); return 0; } return n_packets; } #if defined(XLWIP_CONFIG_INCLUDE_GEM) u32_t phy_link_detect(XEmacPs *xemacp, u32_t phy_addr) { u16_t status; /* Read Phy Status register twice to get the confirmation of the current * link status. */ XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); if (status & IEEE_STAT_LINK_STATUS) return 1; return 0; } #elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) static u32_t phy_link_detect(XAxiEthernet *xemacp, u32_t phy_addr) { u16_t status; /* Read Phy Status register twice to get the confirmation of the current * link status. */ XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); if (status & IEEE_STAT_LINK_STATUS) return 1; return 0; } #elif defined(XLWIP_CONFIG_INCLUDE_EMACLITE) static u32_t phy_link_detect(XEmacLite *xemacp, u32_t phy_addr) { u16_t status; /* Read Phy Status register twice to get the confirmation of the current * link status. */ XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); if (status & IEEE_STAT_LINK_STATUS) return 1; return 0; } #endif #if defined(XLWIP_CONFIG_INCLUDE_GEM) u32_t phy_autoneg_status(XEmacPs *xemacp, u32_t phy_addr) { u16_t status; /* Read Phy Status register twice to get the confirmation of the current * link status. */ XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) return 1; return 0; } #elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) static u32_t phy_autoneg_status(XAxiEthernet *xemacp, u32_t phy_addr) { u16_t status; /* Read Phy Status register twice to get the confirmation of the current * link status. */ XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) return 1; return 0; } #elif defined(XLWIP_CONFIG_INCLUDE_EMACLITE) static u32_t phy_autoneg_status(XEmacLite *xemacp, u32_t phy_addr) { u16_t status; /* Read Phy Status register twice to get the confirmation of the current * link status. */ XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) return 1; return 0; } #endif void eth_link_detect(struct netif *netif) { u32_t link_speed, phy_link_status; struct xemac_s *xemac = (struct xemac_s *)(netif->state); #if defined(XLWIP_CONFIG_INCLUDE_GEM) xemacpsif_s *xemacs = (xemacpsif_s *)(xemac->state); XEmacPs *xemacp = &xemacs->emacps; #elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) xaxiemacif_s *xemacs = (xaxiemacif_s *)(xemac->state); XAxiEthernet *xemacp = &xemacs->axi_ethernet; #elif defined(XLWIP_CONFIG_INCLUDE_EMACLITE) xemacliteif_s *xemacs = (xemacliteif_s *)(xemac->state); XEmacLite *xemacp = xemacs->instance; #endif if ((xemacp->IsReady != (u32)XIL_COMPONENT_IS_READY) || (eth_link_status == ETH_LINK_UNDEFINED)) return; phy_link_status = phy_link_detect(xemacp, phyaddrforemac); if ((eth_link_status == ETH_LINK_UP) && (!phy_link_status)) eth_link_status = ETH_LINK_DOWN; switch (eth_link_status) { case ETH_LINK_UNDEFINED: case ETH_LINK_UP: return; case ETH_LINK_DOWN: netif_set_link_down(netif); eth_link_status = ETH_LINK_NEGOTIATING; xil_printf("Ethernet Link down\r\n"); break; case ETH_LINK_NEGOTIATING: if (phy_link_status && phy_autoneg_status(xemacp, phyaddrforemac)) { /* Initiate Phy setup to get link speed */ #if defined(XLWIP_CONFIG_INCLUDE_GEM) link_speed = phy_setup_emacps(xemacp, phyaddrforemac); XEmacPs_SetOperatingSpeed(xemacp, link_speed); #elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) link_speed = phy_setup_axiemac(xemacp); XAxiEthernet_SetOperatingSpeed(xemacp, link_speed); #endif netif_set_link_up(netif); eth_link_status = ETH_LINK_UP; xil_printf("Ethernet Link up\r\n"); } break; } } #ifdef OS_IS_FREERTOS void link_detect_thread(void *p) { struct netif *netif = (struct netif *) p; while (1) { /* Call eth_link_detect() every second to detect Ethernet link * change. */ eth_link_detect(netif); vTaskDelay(LINK_DETECT_THREAD_INTERVAL / portTICK_RATE_MS); } } #endif