433 lines
11 KiB
C
433 lines
11 KiB
C
|
/*
|
||
|
* 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
|