blob: 8c2e818222e1382797250785dcfc7c9eaa3c4e60 [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "em_bus.h"
#include "em_cmu.h"
#include "em_gpio.h"
#include "em_ldma.h"
#include "em_usart.h"
#ifndef WF200_WIFI
#include "FreeRTOS.h"
#include "event_groups.h"
#include "task.h"
#endif
#include "wfx_host_events.h"
#include "wifi_config.h"
#ifdef WF200_WIFI
#include "sl_wfx.h"
#endif
/* LwIP includes. */
#include "ethernetif.h"
#include "lwip/ethip6.h"
#include "lwip/timeouts.h"
#include "netif/etharp.h"
#ifndef EFR32_LOG
extern "C" {
void efr32Log(const char * aFormat, ...);
#define EFR32_LOG(...) efr32Log(__VA_ARGS__);
}
#endif
StaticSemaphore_t xEthernetIfSemaBuffer;
/*****************************************************************************
* Defines
******************************************************************************/
#define STATION_NETIF0 's'
#define STATION_NETIF1 't'
#define LWIP_FRAME_ALIGNMENT 60
uint32_t gOverrunCount = 0;
/*****************************************************************************
* Variables
******************************************************************************/
/*****************************************************************************
* @fn static void low_level_init(struct netif *netif)
* @brief
* Initializes the hardware parameters. Called from ethernetif_init().
*
* @param[in] netif: the already initialized lwip network interface structure
*
* @return
* None
******************************************************************************/
static void low_level_init(struct netif * netif)
{
/* set netif MAC hardware address length */
netif->hwaddr_len = ETH_HWADDR_LEN;
/* Set netif MAC hardware address */
sl_wfx_mac_address_t mac_addr;
wfx_get_wifi_mac_addr(SL_WFX_STA_INTERFACE, &mac_addr);
netif->hwaddr[0] = mac_addr.octet[0];
netif->hwaddr[1] = mac_addr.octet[1];
netif->hwaddr[2] = mac_addr.octet[2];
netif->hwaddr[3] = mac_addr.octet[3];
netif->hwaddr[4] = mac_addr.octet[4];
netif->hwaddr[5] = mac_addr.octet[5];
/* Set netif maximum transfer unit */
netif->mtu = 1500;
/* Accept broadcast address and ARP traffic */
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;
#if LWIP_IPV6_MLD
netif->flags |= NETIF_FLAG_MLD6;
#endif /* LWIP_IPV6_MLD */
}
/********************************************************************************
* @fn static void low_level_input(struct netif *netif, uint8_t *b, uint16_t len)
* @brief
* Make PBUF out of a linear buffer - that can be fed into lwip
* @param[in] netif: the already initialized lwip network interface structure
* @param[in] len: length
* @return
* None
************************************************************************************/
static void low_level_input(struct netif * netif, uint8_t * b, uint16_t len)
{
struct pbuf *p, *q;
uint32_t bufferoffset;
if (len <= 0)
{
return;
}
if (len < LWIP_FRAME_ALIGNMENT)
{ /* 60 : LWIP frame alignment */
len = LWIP_FRAME_ALIGNMENT;
}
/* Drop packets originated from the same interface and is not destined for the said interface */
const uint8_t * src_mac = b + netif->hwaddr_len;
const uint8_t * dst_mac = b;
if (!(ip6_addr_ispreferred(netif_ip6_addr_state(netif, 0))) && (memcmp(netif->hwaddr, src_mac, netif->hwaddr_len) == 0) &&
(memcmp(netif->hwaddr, dst_mac, netif->hwaddr_len) != 0))
{
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("%s: DROP, [%02x:%02x:%02x:%02x:%02x:%02x]<-[%02x:%02x:%02x:%02x:%02x:%02x] type=%02x%02x", __func__,
dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5],
src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5],
b[12], b[13]);
#endif
return;
}
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool
* and copy the data to the pbuf chain
*/
if ((p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL)) != STRUCT_PBUF)
{
for (q = p, bufferoffset = 0; q != NULL; q = q->next)
{
memcpy((uint8_t *) q->payload, (uint8_t *) b + bufferoffset, q->len);
bufferoffset += q->len;
}
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("%s: ACCEPT %d, [%02x:%02x:%02x:%02x:%02x:%02x]<-[%02x:%02x:%02x:%02x:%02x:%02x] type=%02x%02x", __func__,
bufferoffset,
dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5],
src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5],
b[12], b[13]);
#endif
if (netif->input(p, netif) != ERR_OK)
{
gOverrunCount++;
EFR32_LOG("overrun count entering when fail to alloc value %d", gOverrunCount);
pbuf_free(p);
}
}
else
{
gOverrunCount++;
EFR32_LOG("overrun count entering when fail to alloc value %d", gOverrunCount);
}
}
/*****************************************************************************
* @fn static err_t low_level_output(struct netif *netif, struct pbuf *p)
* @brief
* This function should does the actual transmission of the packet(s).
* The packet is contained in the pbuf that is passed to the function.
* This pbuf might be chained.
*
* @param[in] netif: the lwip network interface structure
*
* @param[in] p: the packet to send
*
* @return
* ERR_OK if successful
******************************************************************************/
#ifdef WF200_WIFI
static err_t low_level_output(struct netif * netif, struct pbuf * p)
{
struct pbuf * q;
sl_wfx_send_frame_req_t * tx_buffer;
uint8_t * buffer;
uint32_t framelength, asize;
uint32_t bufferoffset;
uint32_t padding;
sl_status_t result;
for (q = p, framelength = 0; q != NULL; q = q->next)
{
framelength += q->len;
}
if (framelength < LWIP_FRAME_ALIGNMENT)
{ /* 60 : Frame alignment for LWIP */
padding = LWIP_FRAME_ALIGNMENT - framelength;
}
else
{
padding = 0;
}
/* choose padding of 64 */
asize = SL_WFX_ROUND_UP(framelength + padding, 64) + sizeof(sl_wfx_send_frame_req_t);
// 12 is size of other data in buffer struct, user shouldn't have to care about this?
if (sl_wfx_host_allocate_buffer((void **) &tx_buffer, SL_WFX_TX_FRAME_BUFFER, asize) != SL_STATUS_OK)
{
EFR32_LOG("*ERR*EN-Out: No mem frame len=%d", framelength);
gOverrunCount++;
EFR32_LOG("overrun count exiting when faied to alloc value %d", gOverrunCount);
return ERR_MEM;
}
buffer = tx_buffer->body.packet_data;
/* copy frame from pbufs to driver buffers */
for (q = p, bufferoffset = 0; q != NULL; q = q->next)
{
/* Get bytes in current lwIP buffer */
memcpy((uint8_t *) ((uint8_t *) buffer + bufferoffset), (uint8_t *) ((uint8_t *) q->payload), q->len);
bufferoffset += q->len;
}
/* No requirement to do this - but we should for security */
if (padding)
{
memset(buffer + bufferoffset, 0, padding);
framelength += padding;
}
/* transmit */
int i = 0;
result = SL_STATUS_FAIL;
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("WF200: Out %d", (int) framelength);
#endif
/* send the generated frame over Wifi network */
while ((result != SL_STATUS_OK) && (i++ < 10))
{
result = sl_wfx_send_ethernet_frame(tx_buffer, framelength, SL_WFX_STA_INTERFACE, PRIORITY_0);
}
sl_wfx_host_free_buffer(tx_buffer, SL_WFX_TX_FRAME_BUFFER);
if (result != SL_STATUS_OK)
{
EFR32_LOG("*ERR*Send enet %d", (int) framelength);
return ERR_IF;
}
return ERR_OK;
}
/*****************************************************************************
* @fn void sl_wfx_host_received_frame_callback(sl_wfx_received_ind_t *rx_buffer)
* @brief
* This function implements the wf200 received frame callback.
* Called from the context of the bus_task (not ISR)
*
* @param[in] rx_buffer: the ethernet frame received by the wf200
*
* @return
* None
******************************************************************************/
void sl_wfx_host_received_frame_callback(sl_wfx_received_ind_t * rx_buffer)
{
struct netif * netif;
/* Check packet interface to send to AP or STA interface */
if ((rx_buffer->header.info & SL_WFX_MSG_INFO_INTERFACE_MASK) == (SL_WFX_STA_INTERFACE << SL_WFX_MSG_INFO_INTERFACE_OFFSET))
{
/* Send received frame to station interface */
if ((netif = wfx_get_netif(SL_WFX_STA_INTERFACE)) != NULL)
{
uint8_t * buffer;
uint16_t len;
len = rx_buffer->body.frame_length;
buffer = (uint8_t *) &(rx_buffer->body.frame[rx_buffer->body.frame_padding]);
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("WF200: In %d", (int) len);
#endif
low_level_input(netif, buffer, len);
}
else
{
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("WF200: NO-INTF");
#endif
}
}
else
{
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("WF200: Invalid frame IN");
#endif
}
}
#else /* For RS911x - using LWIP */
static SemaphoreHandle_t ethout_sem;
/*****************************************************************************
* @fn static err_t low_level_output(struct netif *netif, struct pbuf *p)
* @brief
* This function is called from LWIP task when LWIP stack
* has some data to be forwarded over WiFi Network
*
* @param[in] netif: lwip network interface
*
* @param[in] p: the packet to send
*
* @return
* ERR_OK if successful
******************************************************************************/
static err_t low_level_output(struct netif * netif, struct pbuf * p)
{
void * rsipkt;
struct pbuf * q;
uint16_t framelength;
if (xSemaphoreTake(ethout_sem, portMAX_DELAY) != pdTRUE)
{
return ERR_IF;
}
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("EN-RSI: Output");
#endif
if ((netif->flags & (NETIF_FLAG_LINK_UP | NETIF_FLAG_UP)) != (NETIF_FLAG_LINK_UP | NETIF_FLAG_UP))
{
EFR32_LOG("EN-RSI:NOT UP");
xSemaphoreGive(ethout_sem);
return ERR_IF;
}
/* Confirm if packet is allocated */
rsipkt = wfx_rsi_alloc_pkt();
if (!rsipkt)
{
EFR32_LOG("EN-RSI:No buf");
xSemaphoreGive(ethout_sem);
return ERR_IF;
}
#ifdef WIFI_DEBUG_ENABLED
uint8_t * b = (uint8_t *) p->payload;
EFR32_LOG("EN-RSI: Out [%02x:%02x:%02x:%02x:%02x:%02x][%02x:%02x:%02x:%02x:%02x:%02x]type=%02x%02x", b[0], b[1], b[2], b[3],
b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13]);
#endif
/* Generate the packet */
for (q = p, framelength = 0; q != NULL; q = q->next)
{
wfx_rsi_pkt_add_data(rsipkt, (uint8_t *) (q->payload), (uint16_t) q->len, framelength);
framelength += q->len;
}
if (framelength < LWIP_FRAME_ALIGNMENT)
{
/* Add junk data to the end for frame alignment if framelength is less than 60 */
wfx_rsi_pkt_add_data(rsipkt, (uint8_t *) (p->payload), LWIP_FRAME_ALIGNMENT - framelength, framelength);
}
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("EN-RSI: Sending %d", framelength);
#endif
/* forward the generated packet to RSI to
* send the data over wifi network
*/
if (wfx_rsi_send_data(rsipkt, framelength))
{
EFR32_LOG("*ERR*EN-RSI:Send fail");
xSemaphoreGive(ethout_sem);
return ERR_IF;
}
#ifdef WIFI_DEBUG_ENABLED
EFR32_LOG("EN-RSI:Xmit %d", framelength);
#endif
xSemaphoreGive(ethout_sem);
return ERR_OK;
}
/*****************************************************************************
* @fn void wfx_host_received_sta_frame_cb(uint8_t *buf, int len)
* @brief
* host received frame cb
*
* @param[in] buf: buffer
*
* @param[in] len: length
*
* @return
* None
******************************************************************************/
void wfx_host_received_sta_frame_cb(uint8_t * buf, int len)
{
struct netif * ifp;
/* get the network interface for STATION interface,
* and forward the received frame buffer to LWIP
*/
if ((ifp = wfx_get_netif(SL_WFX_STA_INTERFACE)) != (struct netif *) 0)
{
low_level_input(ifp, buf, len);
}
}
#endif /* RS911x - with LWIP */
/*****************************************************************************
* @fn err_t sta_ethernetif_init(struct netif *netif)
* @brief
* sta ethernet if initialization
*
* @param[in] netif: the lwip network interface structure
*
* @return
* ERR_OK if successful
******************************************************************************/
err_t sta_ethernetif_init(struct netif * netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
/* Set the netif name to identify the interface */
netif->name[0] = STATION_NETIF0;
netif->name[1] = STATION_NETIF1;
#if LWIP_IPV4 && LWIP_ARP
netif->output = etharp_output;
#endif /* #if LWIP_IPV4 && LWIP_ARP */
#if LWIP_IPV6 && LWIP_ETHERNET
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 && LWIP_ETHERNET */
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
#ifndef WF200_WIFI
/* Need single output only */
ethout_sem = xSemaphoreCreateBinaryStatic(&xEthernetIfSemaBuffer);
xSemaphoreGive(ethout_sem);
#endif
return ERR_OK;
}