feat(lwip): update lwip component according to IDF

commit ID: 79a5b0b5
This commit is contained in:
yuanjm
2020-07-16 20:23:43 +08:00
parent da3362ecab
commit ef2c714c06
23 changed files with 613 additions and 435 deletions

View File

@ -47,6 +47,7 @@ set(srcs
"lwip/src/core/ipv4/icmp.c" "lwip/src/core/ipv4/icmp.c"
"lwip/src/core/ipv4/igmp.c" "lwip/src/core/ipv4/igmp.c"
"lwip/src/core/ipv4/ip4.c" "lwip/src/core/ipv4/ip4.c"
"lwip/src/core/ipv4/ip4_napt.c"
"lwip/src/core/ipv4/ip4_addr.c" "lwip/src/core/ipv4/ip4_addr.c"
"lwip/src/core/ipv4/ip4_frag.c" "lwip/src/core/ipv4/ip4_frag.c"
"lwip/src/core/ipv6/dhcp6.c" "lwip/src/core/ipv6/dhcp6.c"
@ -92,10 +93,6 @@ set(srcs
"port/${target}/netif/dhcp_state.c" "port/${target}/netif/dhcp_state.c"
"port/${target}/netif/wlanif.c") "port/${target}/netif/wlanif.c")
if(CONFIG_IDF_TARGET_ESP32)
list(APPEND srcs
"port/${target}/netif/nettestif.c")
endif()
if(CONFIG_LWIP_PPP_SUPPORT) if(CONFIG_LWIP_PPP_SUPPORT)
list(APPEND srcs list(APPEND srcs

View File

@ -71,6 +71,20 @@ menu "LWIP"
will be redirected to lwip_select(), therefore, select can be used will be redirected to lwip_select(), therefore, select can be used
for sockets only. for sockets only.
config LWIP_SO_LINGER
bool "Enable SO_LINGER processing"
default n
help
Enabling this option allows SO_LINGER processing.
l_onoff = 1,l_linger can set the timeout.
If l_linger=0, When a connection is closed, TCP will terminate the connection.
This means that TCP will discard any data packets stored in the socket send buffer
and send an RST to the peer.
If l_linger!=0,Then closesocket() calls to block the process until
the remaining data packets has been sent or timed out.
config LWIP_SO_REUSE config LWIP_SO_REUSE
bool "Enable SO_REUSEADDR option" bool "Enable SO_REUSEADDR option"
default y default y
@ -105,18 +119,45 @@ menu "LWIP"
Enabling this option allows checking for the destination address Enabling this option allows checking for the destination address
of a received IPv4 Packet. of a received IPv4 Packet.
config LWIP_IP_FRAG config LWIP_IP4_FRAG
bool "Enable fragment outgoing IP packets" bool "Enable fragment outgoing IP4 packets"
default n default y
help help
Enabling this option allows fragmenting outgoing IP packets if their size Enabling this option allows fragmenting outgoing IP4 packets if their size
exceeds MTU. exceeds MTU.
config LWIP_IP_REASSEMBLY config LWIP_IP6_FRAG
bool "Enable reassembly incoming fragmented IP packets" bool "Enable fragment outgoing IP6 packets"
default y
help
Enabling this option allows fragmenting outgoing IP6 packets if their size
exceeds MTU.
config LWIP_IP4_REASSEMBLY
bool "Enable reassembly incoming fragmented IP4 packets"
default n default n
help help
Enabling this option allows reassemblying incoming fragmented IP packets. Enabling this option allows reassemblying incoming fragmented IP4 packets.
config LWIP_IP6_REASSEMBLY
bool "Enable reassembly incoming fragmented IP6 packets"
default n
help
Enabling this option allows reassemblying incoming fragmented IP6 packets.
config LWIP_IP_FORWARD
bool "Enable IP forwarding"
default n
help
Enabling this option allows packets forwarding across multiple interfaces.
config LWIP_IPV4_NAPT
bool "Enable NAT (new/experimental)"
depends on LWIP_IP_FORWARD
select LWIP_L2_TO_L3_COPY
default n
help
Enabling this option allows Network Address and Port Translation.
config LWIP_STATS config LWIP_STATS
bool "Enable LWIP statistics" bool "Enable LWIP statistics"
@ -254,6 +295,14 @@ menu "LWIP"
If rate limiting self-assignment requests, wait this long between If rate limiting self-assignment requests, wait this long between
each request. each request.
config LWIP_IPV6_AUTOCONFIG
bool "Enable IPV6 stateless address autoconfiguration"
default n
help
Enabling this option allows the devices to IPV6 stateless address autoconfiguration.
See RFC 4862.
menuconfig LWIP_NETIF_LOOPBACK menuconfig LWIP_NETIF_LOOPBACK
bool "Support per-interface loopback" bool "Support per-interface loopback"
default y default y
@ -344,7 +393,8 @@ menu "LWIP"
config LWIP_TCP_SND_BUF_DEFAULT config LWIP_TCP_SND_BUF_DEFAULT
int "Default send buffer size" int "Default send buffer size"
default 5744 # 4 * default MSS default 5744 # 4 * default MSS
range 2440 65535 range 2440 65535 if !LWIP_WND_SCALE
range 2440 1024000 if LWIP_WND_SCALE
help help
Set default send buffer size for new TCP sockets. Set default send buffer size for new TCP sockets.
@ -360,7 +410,8 @@ menu "LWIP"
config LWIP_TCP_WND_DEFAULT config LWIP_TCP_WND_DEFAULT
int "Default receive window size" int "Default receive window size"
default 5744 # 4 * default MSS default 5744 # 4 * default MSS
range 2440 65535 range 2440 65535 if !LWIP_WND_SCALE
range 2440 1024000 if LWIP_WND_SCALE
help help
Set default TCP receive window size for new TCP sockets. Set default TCP receive window size for new TCP sockets.
@ -400,6 +451,12 @@ menu "LWIP"
Disable this option to save some RAM during TCP sessions, at the expense Disable this option to save some RAM during TCP sessions, at the expense
of increased retransmissions if segments arrive out of order. of increased retransmissions if segments arrive out of order.
config LWIP_TCP_SACK_OUT
bool "Support sending selective acknowledgements"
default n
help
TCP will support sending selective acknowledgements (SACKs).
config LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES config LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES
bool "Keep TCP connections when IP changed" bool "Keep TCP connections when IP changed"
default n default n
@ -449,6 +506,13 @@ menu "LWIP"
help help
Enable this feature to support TCP window scaling. Enable this feature to support TCP window scaling.
config LWIP_TCP_RTO_TIME
int "Default TCP rto time"
default 3000
help
Set default TCP rto time for a reasonable initial rto.
In bad network environment, recommend set value of rto time to 1500.
endmenu # TCP endmenu # TCP
menu "UDP" menu "UDP"
@ -526,6 +590,17 @@ menu "LWIP"
PPP over serial support is experimental and unsupported. PPP over serial support is experimental and unsupported.
config LWIP_PPP_ENABLE_IPV6
bool "Enable IPV6 support for PPP connections (IPV6CP)"
depends on LWIP_PPP_SUPPORT && LWIP_IPV6
default y
help
Enable IPV6 support in PPP for the local link between the DTE (processor) and DCE (modem).
There are some modems which do not support the IPV6 addressing in the local link.
If they are requested for IPV6CP negotiation, they may time out.
This would in turn fail the configuration for the whole link.
If your modem is not responding correctly to PPP Phase Network, try to disable IPV6 support.
config LWIP_PPP_NOTIFY_PHASE_SUPPORT config LWIP_PPP_NOTIFY_PHASE_SUPPORT
bool "Enable Notify Phase Callback" bool "Enable Notify Phase Callback"
depends on LWIP_PPP_SUPPORT depends on LWIP_PPP_SUPPORT
@ -621,4 +696,11 @@ menu "LWIP"
endmenu # SNTP endmenu # SNTP
config LWIP_ESP_LWIP_ASSERT
bool "Enable LWIP ASSERT checks"
default y
help
Enable this option allows lwip to check assert.
It is recommended to keep it open, do not close it.
endmenu endmenu

View File

@ -81,12 +81,19 @@ typedef struct _list_node {
static const u32_t magic_cookie = 0x63538263; static const u32_t magic_cookie = 0x63538263;
static struct udp_pcb *pcb_dhcps = NULL; static struct netif *dhcps_netif = NULL;
static ip4_addr_t broadcast_dhcps; static ip4_addr_t broadcast_dhcps;
static ip4_addr_t server_address; static ip4_addr_t server_address;
static ip4_addr_t dns_server = {0}; static ip4_addr_t dns_server = {0};
static ip4_addr_t client_address; //added static ip4_addr_t client_address; //added
static ip4_addr_t client_address_plus; static ip4_addr_t client_address_plus;
static ip4_addr_t s_dhcps_mask = {
#ifdef USE_CLASS_B_NET
.addr = PP_HTONL(LWIP_MAKEU32(255, 240, 0, 0))
#else
.addr = PP_HTONL(LWIP_MAKEU32(255, 255, 255, 0))
#endif
};
static list_node *plist = NULL; static list_node *plist = NULL;
static bool renew = false; static bool renew = false;
@ -136,7 +143,12 @@ void *dhcps_option_info(u8_t op_id, u32_t opt_len)
} }
break; break;
case SUBNET_MASK:
if (opt_len == sizeof(s_dhcps_mask)) {
option_arg = &s_dhcps_mask;
}
break;
default: default:
break; break;
} }
@ -185,6 +197,12 @@ void dhcps_set_option_info(u8_t op_id, void *opt_info, u32_t opt_len)
} }
break; break;
case SUBNET_MASK:
if (opt_len == sizeof(s_dhcps_mask)) {
s_dhcps_mask = *(ip4_addr_t *)opt_info;
}
default: default:
break; break;
} }
@ -253,11 +271,13 @@ void node_remove_from_list(list_node **phead, list_node *pdelete)
*phead = NULL; *phead = NULL;
} else { } else {
if (plist == pdelete) { if (plist == pdelete) {
*phead = plist->pnext; // Note: Ignoring the "use after free" warnings, as it could only happen
// if the linked list contains loops
*phead = plist->pnext; // NOLINT(clang-analyzer-unix.Malloc)
pdelete->pnext = NULL; pdelete->pnext = NULL;
} else { } else {
while (plist != NULL) { while (plist != NULL) {
if (plist->pnext == pdelete) { if (plist->pnext == pdelete) { // NOLINT(clang-analyzer-unix.Malloc)
plist->pnext = pdelete->pnext; plist->pnext = pdelete->pnext;
pdelete->pnext = NULL; pdelete->pnext = NULL;
} }
@ -294,21 +314,12 @@ static u8_t *add_offer_options(u8_t *optptr)
ipadd.addr = *((u32_t *) &server_address); ipadd.addr = *((u32_t *) &server_address);
#ifdef USE_CLASS_B_NET
*optptr++ = DHCP_OPTION_SUBNET_MASK;
*optptr++ = 4; //length
*optptr++ = 255;
*optptr++ = 240;
*optptr++ = 0;
*optptr++ = 0;
#else
*optptr++ = DHCP_OPTION_SUBNET_MASK; *optptr++ = DHCP_OPTION_SUBNET_MASK;
*optptr++ = 4; *optptr++ = 4;
*optptr++ = 255; *optptr++ = ip4_addr1(&s_dhcps_mask);
*optptr++ = 255; *optptr++ = ip4_addr2(&s_dhcps_mask);
*optptr++ = 255; *optptr++ = ip4_addr3(&s_dhcps_mask);
*optptr++ = 0; *optptr++ = ip4_addr4(&s_dhcps_mask);
#endif
*optptr++ = DHCP_OPTION_LEASE_TIME; *optptr++ = DHCP_OPTION_LEASE_TIME;
*optptr++ = 4; *optptr++ = 4;
@ -355,21 +366,13 @@ static u8_t *add_offer_options(u8_t *optptr)
*optptr++ = ip4_addr4(&ipadd); *optptr++ = ip4_addr4(&ipadd);
} }
#ifdef CLASS_B_NET ip4_addr_t broadcast_addr = { .addr = (ipadd.addr & s_dhcps_mask.addr) | ~s_dhcps_mask.addr };
*optptr++ = DHCP_OPTION_BROADCAST_ADDRESS; *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS;
*optptr++ = 4; *optptr++ = 4;
*optptr++ = ip4_addr1(&ipadd); *optptr++ = ip4_addr1(&broadcast_addr);
*optptr++ = 255; *optptr++ = ip4_addr2(&broadcast_addr);
*optptr++ = 255; *optptr++ = ip4_addr3(&broadcast_addr);
*optptr++ = 255; *optptr++ = ip4_addr4(&broadcast_addr);
#else
*optptr++ = DHCP_OPTION_BROADCAST_ADDRESS;
*optptr++ = 4;
*optptr++ = ip4_addr1(&ipadd);
*optptr++ = ip4_addr2(&ipadd);
*optptr++ = ip4_addr3(&ipadd);
*optptr++ = 255;
#endif
*optptr++ = DHCP_OPTION_INTERFACE_MTU; *optptr++ = DHCP_OPTION_INTERFACE_MTU;
*optptr++ = 2; *optptr++ = 2;
@ -525,6 +528,7 @@ static void send_offer(struct dhcps_msg *m, u16_t len)
ip_addr_t ip_temp = IPADDR4_INIT(0x0); ip_addr_t ip_temp = IPADDR4_INIT(0x0);
ip4_addr_set(ip_2_ip4(&ip_temp), &broadcast_dhcps); ip4_addr_set(ip_2_ip4(&ip_temp), &broadcast_dhcps);
struct udp_pcb *pcb_dhcps = dhcps_netif->dhcps_pcb;
#if DHCPS_DEBUG #if DHCPS_DEBUG
SendOffer_err_t = udp_sendto(pcb_dhcps, p, &ip_temp, DHCPS_CLIENT_PORT); SendOffer_err_t = udp_sendto(pcb_dhcps, p, &ip_temp, DHCPS_CLIENT_PORT);
DHCPS_LOG("dhcps: send_offer>>udp_sendto result %x\n", SendOffer_err_t); DHCPS_LOG("dhcps: send_offer>>udp_sendto result %x\n", SendOffer_err_t);
@ -602,6 +606,7 @@ static void send_nak(struct dhcps_msg *m, u16_t len)
ip_addr_t ip_temp = IPADDR4_INIT(0x0); ip_addr_t ip_temp = IPADDR4_INIT(0x0);
ip4_addr_set(ip_2_ip4(&ip_temp), &broadcast_dhcps); ip4_addr_set(ip_2_ip4(&ip_temp), &broadcast_dhcps);
struct udp_pcb *pcb_dhcps = dhcps_netif->dhcps_pcb;
#if DHCPS_DEBUG #if DHCPS_DEBUG
SendNak_err_t = udp_sendto(pcb_dhcps, p, &ip_temp, DHCPS_CLIENT_PORT); SendNak_err_t = udp_sendto(pcb_dhcps, p, &ip_temp, DHCPS_CLIENT_PORT);
DHCPS_LOG("dhcps: send_nak>>udp_sendto result %x\n", SendNak_err_t); DHCPS_LOG("dhcps: send_nak>>udp_sendto result %x\n", SendNak_err_t);
@ -678,6 +683,7 @@ static void send_ack(struct dhcps_msg *m, u16_t len)
ip_addr_t ip_temp = IPADDR4_INIT(0x0); ip_addr_t ip_temp = IPADDR4_INIT(0x0);
ip4_addr_set(ip_2_ip4(&ip_temp), &broadcast_dhcps); ip4_addr_set(ip_2_ip4(&ip_temp), &broadcast_dhcps);
struct udp_pcb *pcb_dhcps = dhcps_netif->dhcps_pcb;
SendAck_err_t = udp_sendto(pcb_dhcps, p, &ip_temp, DHCPS_CLIENT_PORT); SendAck_err_t = udp_sendto(pcb_dhcps, p, &ip_temp, DHCPS_CLIENT_PORT);
#if DHCPS_DEBUG #if DHCPS_DEBUG
DHCPS_LOG("dhcps: send_ack>>udp_sendto result %x\n", SendAck_err_t); DHCPS_LOG("dhcps: send_ack>>udp_sendto result %x\n", SendAck_err_t);
@ -1135,19 +1141,20 @@ void dhcps_set_new_lease_cb(dhcps_cb_t cb)
*******************************************************************************/ *******************************************************************************/
void dhcps_start(struct netif *netif, ip4_addr_t ip) void dhcps_start(struct netif *netif, ip4_addr_t ip)
{ {
struct netif *apnetif = netif; dhcps_netif = netif;
if (apnetif->dhcps_pcb != NULL) { if (dhcps_netif->dhcps_pcb != NULL) {
udp_remove(apnetif->dhcps_pcb); udp_remove(dhcps_netif->dhcps_pcb);
} }
pcb_dhcps = udp_new(); dhcps_netif->dhcps_pcb = udp_new();
struct udp_pcb *pcb_dhcps = dhcps_netif->dhcps_pcb;
if (pcb_dhcps == NULL || ip4_addr_isany_val(ip)) { if (pcb_dhcps == NULL || ip4_addr_isany_val(ip)) {
printf("dhcps_start(): could not obtain pcb\n"); printf("dhcps_start(): could not obtain pcb\n");
} }
apnetif->dhcps_pcb = pcb_dhcps; dhcps_netif->dhcps_pcb = pcb_dhcps;
IP4_ADDR(&broadcast_dhcps, 255, 255, 255, 255); IP4_ADDR(&broadcast_dhcps, 255, 255, 255, 255);
@ -1212,6 +1219,7 @@ static void kill_oldest_dhcps_pool(void)
list_node *minpre = NULL, *minp = NULL; list_node *minpre = NULL, *minp = NULL;
struct dhcps_pool *pdhcps_pool = NULL, *pmin_pool = NULL; struct dhcps_pool *pdhcps_pool = NULL, *pmin_pool = NULL;
pre = plist; pre = plist;
assert(pre != NULL && pre->pnext != NULL); // Expect the list to have at least 2 nodes
p = pre->pnext; p = pre->pnext;
minpre = pre; minpre = pre;
minp = p; minp = p;
@ -1326,5 +1334,5 @@ dhcps_dns_getserver(void)
{ {
return dns_server; return dns_server;
} }
#endif #endif // ESP_DHCP

View File

@ -103,10 +103,10 @@ static int esp_ping_receive(esp_ping_t *ep)
int len = 0; int len = 0;
struct sockaddr_storage from; struct sockaddr_storage from;
int fromlen = sizeof(from); int fromlen = sizeof(from);
uint16_t data_head = (uint16_t)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr));
while ((len = recvfrom(ep->sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen)) > 0) { while ((len = recvfrom(ep->sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen)) > 0) {
if (len >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) { if (len >= data_head) {
ep->recv_len = (uint32_t)len;
if (from.ss_family == AF_INET) { if (from.ss_family == AF_INET) {
// IPv4 // IPv4
struct sockaddr_in *from4 = (struct sockaddr_in *)&from; struct sockaddr_in *from4 = (struct sockaddr_in *)&from;
@ -128,6 +128,7 @@ static int esp_ping_receive(esp_ping_t *ep)
if ((iecho->id == ep->packet_hdr->id) && (iecho->seqno == ep->packet_hdr->seqno)) { if ((iecho->id == ep->packet_hdr->id) && (iecho->seqno == ep->packet_hdr->seqno)) {
ep->received++; ep->received++;
ep->ttl = iphdr->_ttl; ep->ttl = iphdr->_ttl;
ep->recv_len = lwip_ntohs(IPH_LEN(iphdr)) - data_head; // The data portion of ICMP
return len; return len;
} }
} }

View File

@ -18,12 +18,14 @@
#include <sys/time.h> #include <sys/time.h>
#include "esp_log.h" #include "esp_log.h"
#include "sntp.h" #include "sntp.h"
#include "lwip/apps/sntp.h"
static const char *TAG = "sntp"; static const char *TAG = "sntp";
static volatile sntp_sync_mode_t sntp_sync_mode = SNTP_SYNC_MODE_IMMED; static volatile sntp_sync_mode_t sntp_sync_mode = SNTP_SYNC_MODE_IMMED;
static volatile sntp_sync_status_t sntp_sync_status = SNTP_SYNC_STATUS_RESET; static volatile sntp_sync_status_t sntp_sync_status = SNTP_SYNC_STATUS_RESET;
static sntp_sync_time_cb_t time_sync_notification_cb = NULL; static sntp_sync_time_cb_t time_sync_notification_cb = NULL;
static uint32_t s_sync_interval = CONFIG_LWIP_SNTP_UPDATE_DELAY;
inline void sntp_set_sync_status(sntp_sync_status_t sync_status) inline void sntp_set_sync_status(sntp_sync_status_t sync_status)
{ {
@ -91,3 +93,27 @@ sntp_sync_status_t sntp_get_sync_status(void)
} }
return ret_sync_status; return ret_sync_status;
} }
void sntp_set_sync_interval(uint32_t interval_ms)
{
if (interval_ms < 15000) {
// SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds
interval_ms = 15000;
}
s_sync_interval = interval_ms;
}
uint32_t sntp_get_sync_interval(void)
{
return s_sync_interval;
}
bool sntp_restart(void)
{
if (sntp_enabled()) {
sntp_stop();
sntp_init();
return true;
}
return false;
}

View File

@ -87,7 +87,7 @@ typedef struct {
.count = 5, \ .count = 5, \
.interval_ms = 1000, \ .interval_ms = 1000, \
.timeout_ms = 1000, \ .timeout_ms = 1000, \
.data_size = 56, \ .data_size = 64, \
.tos = 0, \ .tos = 0, \
.target_addr = PING_TARGET_ADDR, \ .target_addr = PING_TARGET_ADDR, \
.task_stack_size = 2048, \ .task_stack_size = 2048, \

View File

@ -120,6 +120,33 @@ void sntp_set_sync_status(sntp_sync_status_t sync_status);
*/ */
void sntp_set_time_sync_notification_cb(sntp_sync_time_cb_t callback); void sntp_set_time_sync_notification_cb(sntp_sync_time_cb_t callback);
/**
* @brief Set the sync interval of SNTP operation
*
* Note: SNTPv4 RFC 4330 enforces a minimum sync interval of 15 seconds.
* This sync interval will be used in the next attempt update time throught SNTP.
* To apply the new sync interval call the sntp_restart() function,
* otherwise, it will be applied after the last interval expired.
*
* @param interval_ms The sync interval in ms. It cannot be lower than 15 seconds, otherwise 15 seconds will be set.
*/
void sntp_set_sync_interval(uint32_t interval_ms);
/**
* @brief Get the sync interval of SNTP operation
*
* @return the sync interval
*/
uint32_t sntp_get_sync_interval(void);
/**
* @brief Restart SNTP
*
* @return True - Restart
* False - SNTP was not initialized yet
*/
bool sntp_restart(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -41,9 +41,7 @@
#include "lwip/stats.h" #include "lwip/stats.h"
#include "esp_log.h" #include "esp_log.h"
/* This is the number of threads that can be started with sys_thread_new() */ static const char* TAG = "lwip_arch";
#define SYS_THREAD_MAX 4
#define TAG "lwip_arch"
static sys_mutex_t g_lwip_protect_mutex = NULL; static sys_mutex_t g_lwip_protect_mutex = NULL;
@ -51,160 +49,167 @@ static pthread_key_t sys_thread_sem_key;
static void sys_thread_sem_free(void* data); static void sys_thread_sem_free(void* data);
#if !LWIP_COMPAT_MUTEX #if !LWIP_COMPAT_MUTEX
/** Create a new mutex
* @param mutex pointer to the mutex to create /**
* @return a new mutex */ * @brief Create a new mutex
*
* @param pxMutex pointer of the mutex to create
* @return ERR_OK on success, ERR_MEM when out of memory
*/
err_t err_t
sys_mutex_new(sys_mutex_t *pxMutex) sys_mutex_new(sys_mutex_t *pxMutex)
{ {
err_t xReturn = ERR_MEM;
*pxMutex = xSemaphoreCreateMutex(); *pxMutex = xSemaphoreCreateMutex();
if (*pxMutex == NULL) {
if (*pxMutex != NULL) { LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: out of mem\r\n"));
xReturn = ERR_OK; return ERR_MEM;
} }
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex)); LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex));
return xReturn; return ERR_OK;
} }
/** Lock a mutex /**
* @param mutex the mutex to lock */ * @brief Lock a mutex
void ESP_IRAM_ATTR *
* @param pxMutex pointer of mutex to lock
*/
void
sys_mutex_lock(sys_mutex_t *pxMutex) sys_mutex_lock(sys_mutex_t *pxMutex)
{ {
while (xSemaphoreTake(*pxMutex, portMAX_DELAY) != pdPASS); BaseType_t ret = xSemaphoreTake(*pxMutex, portMAX_DELAY);
LWIP_ASSERT("failed to take the mutex", ret == pdTRUE);
} }
err_t /**
sys_mutex_trylock(sys_mutex_t *pxMutex) * @brief Unlock a mutex
{ *
if (xSemaphoreTake(*pxMutex, 0) == pdPASS) return 0; * @param pxMutex pointer of mutex to unlock
else return -1; */
} void
/** Unlock a mutex
* @param mutex the mutex to unlock */
void ESP_IRAM_ATTR
sys_mutex_unlock(sys_mutex_t *pxMutex) sys_mutex_unlock(sys_mutex_t *pxMutex)
{ {
xSemaphoreGive(*pxMutex); BaseType_t ret = xSemaphoreGive(*pxMutex);
LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
} }
/** Delete a semaphore /**
* @param mutex the mutex to delete */ * @brief Delete a mutex
*
* @param pxMutex pointer of mutex to delete
*/
void void
sys_mutex_free(sys_mutex_t *pxMutex) sys_mutex_free(sys_mutex_t *pxMutex)
{ {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex)); LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex));
vQueueDelete(*pxMutex); vSemaphoreDelete(*pxMutex);
*pxMutex = NULL;
} }
#endif
/*-----------------------------------------------------------------------------------*/ #endif /* !LWIP_COMPAT_MUTEX */
// Creates and returns a new semaphore. The "count" argument specifies
// the initial state of the semaphore. TBD finish and test /**
* @brief Creates a new semaphore
*
* @param sem pointer of the semaphore
* @param count initial state of the semaphore
* @return err_t
*/
err_t err_t
sys_sem_new(sys_sem_t *sem, u8_t count) sys_sem_new(sys_sem_t *sem, u8_t count)
{ {
err_t xReturn = ERR_MEM; LWIP_ASSERT("initial_count invalid (neither 0 nor 1)",
vSemaphoreCreateBinary(*sem); (count == 0) || (count == 1));
if ((*sem) != NULL) { *sem = xSemaphoreCreateBinary();
if (count == 0) { // Means it can't be taken if (*sem == NULL) {
xSemaphoreTake(*sem, 1); LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_sem_new: out of mem\r\n"));
return ERR_MEM;
} }
xReturn = ERR_OK; if (count == 1) {
} else { BaseType_t ret = xSemaphoreGive(*sem);
; // TBD need assert LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
} }
return xReturn; return ERR_OK;
} }
/*-----------------------------------------------------------------------------------*/ /**
// Signals a semaphore * @brief Signals a semaphore
void ESP_IRAM_ATTR *
* @param sem pointer of the semaphore
*/
void
sys_sem_signal(sys_sem_t *sem) sys_sem_signal(sys_sem_t *sem)
{ {
xSemaphoreGive(*sem); BaseType_t ret = xSemaphoreGive(*sem);
/* queue full is OK, this is a signal only... */
LWIP_ASSERT("sys_sem_signal: sane return value",
(ret == pdTRUE) || (ret == errQUEUE_FULL));
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
// Signals a semaphore (from ISR) // Signals a semaphore (from ISR)
int sys_sem_signal_isr(sys_sem_t *sem) int
sys_sem_signal_isr(sys_sem_t *sem)
{ {
BaseType_t woken = pdFALSE; BaseType_t woken = pdFALSE;
xSemaphoreGiveFromISR(*sem, &woken); xSemaphoreGiveFromISR(*sem, &woken);
return woken == pdTRUE; return woken == pdTRUE;
} }
/*-----------------------------------------------------------------------------------*/ /**
/* * @brief Wait for a semaphore to be signaled
Blocks the thread while waiting for the semaphore to be *
signaled. If the "timeout" argument is non-zero, the thread should * @param sem pointer of the semaphore
only be blocked for the specified time (measured in * @param timeout if zero, will wait infinitely, or will wait for milliseconds specify by this argument
milliseconds). * @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
If the timeout argument is non-zero, the return value is the number of
milliseconds spent waiting for the semaphore to be signaled. If the
semaphore wasn't signaled within the specified time, the return value is
SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
(i.e., it was already signaled), the function may return zero.
Notice that lwIP implements a function with a similar name,
sys_sem_wait(), that uses the sys_arch_sem_wait() function.
*/ */
u32_t ESP_IRAM_ATTR u32_t
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{ {
portTickType StartTime, EndTime, Elapsed; BaseType_t ret;
unsigned long ulReturn;
StartTime = xTaskGetTickCount(); if (!timeout) {
/* wait infinite */
if (timeout != 0) { ret = xSemaphoreTake(*sem, portMAX_DELAY);
if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) == pdTRUE) { LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
EndTime = xTaskGetTickCount();
Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS;
if (Elapsed == 0) {
Elapsed = 1;
}
ulReturn = Elapsed;
} else { } else {
ulReturn = SYS_ARCH_TIMEOUT; TickType_t timeout_ticks = timeout / portTICK_RATE_MS;
ret = xSemaphoreTake(*sem, timeout_ticks);
if (ret == errQUEUE_EMPTY) {
/* timed out */
return SYS_ARCH_TIMEOUT;
} }
} else { // must block without a timeout LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE);
EndTime = xTaskGetTickCount();
Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS;
if (Elapsed == 0) {
Elapsed = 1;
} }
ulReturn = Elapsed; return 0;
} }
return ulReturn ; // return time blocked /**
} * @brief Delete a semaphore
*
/*-----------------------------------------------------------------------------------*/ * @param sem pointer of the semaphore to delete
// Deallocates a semaphore */
void void
sys_sem_free(sys_sem_t *sem) sys_sem_free(sys_sem_t *sem)
{ {
vSemaphoreDelete(*sem); vSemaphoreDelete(*sem);
*sem = NULL;
} }
/*-----------------------------------------------------------------------------------*/ /**
// Creates an empty mailbox. * @brief Create an empty mailbox.
*
* @param mbox pointer of the mailbox
* @param size size of the mailbox
* @return ERR_OK on success, ERR_MEM when out of memory
*/
err_t err_t
sys_mbox_new(sys_mbox_t *mbox, int size) sys_mbox_new(sys_mbox_t *mbox, int size)
{ {
@ -217,7 +222,7 @@ sys_mbox_new(sys_mbox_t *mbox, int size)
(*mbox)->os_mbox = xQueueCreate(size, sizeof(void *)); (*mbox)->os_mbox = xQueueCreate(size, sizeof(void *));
if ((*mbox)->os_mbox == NULL) { if ((*mbox)->os_mbox == NULL) {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox->os_mbox\n")); LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new (*mbox)->os_mbox\n"));
free(*mbox); free(*mbox);
return ERR_MEM; return ERR_MEM;
} }
@ -230,21 +235,32 @@ sys_mbox_new(sys_mbox_t *mbox, int size)
return ERR_OK; return ERR_OK;
} }
/*-----------------------------------------------------------------------------------*/ /**
// Posts the "msg" to the mailbox. * @brief Send message to mailbox
void ESP_IRAM_ATTR *
* @param mbox pointer of the mailbox
* @param msg pointer of the message to send
*/
void
sys_mbox_post(sys_mbox_t *mbox, void *msg) sys_mbox_post(sys_mbox_t *mbox, void *msg)
{ {
while (xQueueSendToBack((*mbox)->os_mbox, &msg, portMAX_DELAY) != pdTRUE); BaseType_t ret = xQueueSendToBack((*mbox)->os_mbox, &msg, portMAX_DELAY);
LWIP_ASSERT("mbox post failed", ret == pdTRUE);
} }
/*-----------------------------------------------------------------------------------*/ /**
err_t ESP_IRAM_ATTR * @brief Try to post a message to mailbox
*
* @param mbox pointer of the mailbox
* @param msg pointer of the message to send
* @return ERR_OK on success, ERR_MEM when mailbox is full
*/
err_t
sys_mbox_trypost(sys_mbox_t *mbox, void *msg) sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{ {
err_t xReturn; err_t xReturn;
if (xQueueSend((*mbox)->os_mbox, &msg, (portTickType)0) == pdPASS) { if (xQueueSend((*mbox)->os_mbox, &msg, 0) == pdTRUE) {
xReturn = ERR_OK; xReturn = ERR_OK;
} else { } else {
LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox)); LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox));
@ -254,83 +270,95 @@ sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
return xReturn; return xReturn;
} }
/*-----------------------------------------------------------------------------------*/ /**
/* * @brief Try to post a message to mailbox from ISR
Blocks the thread until a message arrives in the mailbox, but does *
not block the thread longer than "timeout" milliseconds (similar to * @param mbox pointer of the mailbox
the sys_arch_sem_wait() function). The "msg" argument is a result * @param msg pointer of the message to send
parameter that is set by the function (i.e., by doing "*msg = * @return ERR_OK on success
ptr"). The "msg" parameter maybe NULL to indicate that the message * ERR_MEM when mailbox is full
should be dropped. * ERR_NEED_SCHED when high priority task wakes up
The return values are the same as for the sys_arch_sem_wait() function:
Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
timeout.
Note that a function with a similar name, sys_mbox_fetch(), is
implemented by lwIP.
*/ */
u32_t ESP_IRAM_ATTR err_t
sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{ {
void *dummyptr; BaseType_t ret;
portTickType StartTime, EndTime, Elapsed; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
unsigned long ulReturn;
StartTime = xTaskGetTickCount(); ret = xQueueSendFromISR((*mbox)->os_mbox, &msg, &xHigherPriorityTaskWoken);
if (msg == NULL) { if (ret == pdTRUE) {
msg = &dummyptr; if (xHigherPriorityTaskWoken == pdTRUE) {
return ERR_NEED_SCHED;
}
return ERR_OK;
} else {
LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
return ERR_MEM;
}
} }
if (*mbox == NULL){ /**
*msg = NULL; * @brief Fetch message from mailbox
return -1; *
* @param mbox pointer of mailbox
* @param msg pointer of the received message, could be NULL to indicate the message should be dropped
* @param timeout if zero, will wait infinitely; or will wait milliseconds specify by this argument
* @return SYS_ARCH_TIMEOUT when timeout, 0 otherwise
*/
u32_t
sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
BaseType_t ret;
void *msg_dummy;
if (msg == NULL) {
msg = &msg_dummy;
} }
if (timeout == 0) { if (timeout == 0) {
timeout = portMAX_DELAY; /* wait infinite */
ret = xQueueReceive((*mbox)->os_mbox, &(*msg), portMAX_DELAY);
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
} else { } else {
timeout = timeout / portTICK_PERIOD_MS; TickType_t timeout_ticks = timeout / portTICK_RATE_MS;
} ret = xQueueReceive((*mbox)->os_mbox, &(*msg), timeout_ticks);
if (ret == errQUEUE_EMPTY) {
/* timed out */
*msg = NULL; *msg = NULL;
if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), timeout)) { return SYS_ARCH_TIMEOUT;
EndTime = xTaskGetTickCount(); }
Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
if (Elapsed == 0) {
Elapsed = 1;
} }
ulReturn = Elapsed; return 0;
} else { // timed out blocking for message
ulReturn = SYS_ARCH_TIMEOUT;
} }
return ulReturn ; // return time blocked TBD test /**
} * @brief try to fetch message from mailbox
*
/*-----------------------------------------------------------------------------------*/ * @param mbox pointer of mailbox
* @param msg pointer of the received message
* @return SYS_MBOX_EMPTY if mailbox is empty, 1 otherwise
*/
u32_t u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{ {
void *pvDummy; BaseType_t ret;
unsigned long ulReturn; void *msg_dummy;
if (msg == NULL) { if (msg == NULL) {
msg = &pvDummy; msg = &msg_dummy;
} }
if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), 0)) { ret = xQueueReceive((*mbox)->os_mbox, &(*msg), 0);
ulReturn = ERR_OK; if (ret == errQUEUE_EMPTY) {
} else { *msg = NULL;
ulReturn = SYS_MBOX_EMPTY; return SYS_MBOX_EMPTY;
} }
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
return ulReturn; return 0;
} }
/*-----------------------------------------------------------------------------------*/
void void
sys_mbox_set_owner(sys_mbox_t *mbox, void* owner) sys_mbox_set_owner(sys_mbox_t *mbox, void* owner)
{ {
@ -340,10 +368,10 @@ sys_mbox_set_owner(sys_mbox_t *mbox, void* owner)
} }
} }
/* /**
Deallocates a mailbox. If there are messages still present in the * @brief Delete a mailbox
mailbox when the mailbox is deallocated, it is an indication of a *
programming error in lwIP and the developer should be notified. * @param mbox pointer of the mailbox to delete
*/ */
void void
sys_mbox_free(sys_mbox_t *mbox) sys_mbox_free(sys_mbox_t *mbox)
@ -351,43 +379,54 @@ sys_mbox_free(sys_mbox_t *mbox)
if ((NULL == mbox) || (NULL == *mbox)) { if ((NULL == mbox) || (NULL == *mbox)) {
return; return;
} }
UBaseType_t msgs_waiting = uxQueueMessagesWaiting((*mbox)->os_mbox);
LWIP_ASSERT("mbox quence not empty", msgs_waiting == 0);
vQueueDelete((*mbox)->os_mbox); vQueueDelete((*mbox)->os_mbox);
free(*mbox); free(*mbox);
*mbox = NULL; *mbox = NULL;
} }
/*-----------------------------------------------------------------------------------*/ /**
/* * @brief Create a new thread
Starts a new thread with priority "prio" that will begin its execution in the *
function "thread()". The "arg" argument will be passed as an argument to the * @param name thread name
thread() function. The id of the new thread is returned. Both the id and * @param thread thread function
the priority are system dependent. * @param arg thread arguments
* @param stacksize stacksize of the thread
* @param prio priority of the thread
* @return thread ID
*/ */
sys_thread_t sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio) sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{ {
xTaskHandle created_task; TaskHandle_t rtos_task;
portBASE_TYPE result; BaseType_t ret;
result = xTaskCreatePinnedToCore(thread, name, stacksize, arg, prio, &created_task, /* LwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
thread function without adaption here. */
ret = xTaskCreatePinnedToCore(thread, name, stacksize, arg, prio, &rtos_task,
CONFIG_LWIP_TCPIP_TASK_AFFINITY); CONFIG_LWIP_TCPIP_TASK_AFFINITY);
if (result != pdPASS) { if (ret != pdTRUE) {
return NULL; return NULL;
} }
return created_task; return (sys_thread_t)rtos_task;
} }
/*-----------------------------------------------------------------------------------*/ /**
// Initialize sys arch * @brief Initialize the sys_arch layer
*
*/
void void
sys_init(void) sys_init(void)
{ {
if (!g_lwip_protect_mutex) {
if (ERR_OK != sys_mutex_new(&g_lwip_protect_mutex)) { if (ERR_OK != sys_mutex_new(&g_lwip_protect_mutex)) {
ESP_LOGE(TAG, "sys_init: failed to init lwip protect mutex\n"); ESP_LOGE(TAG, "sys_init: failed to init lwip protect mutex\n");
} }
}
// Create the pthreads key for the per-thread semaphore storage // Create the pthreads key for the per-thread semaphore storage
pthread_key_create(&sys_thread_sem_key, sys_thread_sem_free); pthread_key_create(&sys_thread_sem_key, sys_thread_sem_free);
@ -395,57 +434,62 @@ sys_init(void)
esp_vfs_lwip_sockets_register(); esp_vfs_lwip_sockets_register();
} }
/*-----------------------------------------------------------------------------------*/ /**
* @brief Get system ticks
*
* @return system tick counts
*/
u32_t u32_t
sys_jiffies(void) sys_jiffies(void)
{ {
return xTaskGetTickCount(); return xTaskGetTickCount();
} }
/*-----------------------------------------------------------------------------------*/ /**
* @brief Get current time, in miliseconds
*
* @return current time
*/
u32_t u32_t
sys_now(void) sys_now(void)
{ {
return (xTaskGetTickCount()*portTICK_PERIOD_MS); return xTaskGetTickCount() * portTICK_PERIOD_MS;
} }
/* /**
This optional function does a "fast" critical region protection and returns * @brief Protect critical region
the previous protection level. This function is only called during very short *
critical regions. An embedded system which supports ISR-based drivers might * @note This function is only called during very short critical regions.
want to implement this function by disabling interrupts. Task-based systems *
might want to implement this by using a mutex or disabling tasking. This * @return previous protection level
function should support recursive calls from the same task or interrupt. In
other words, sys_arch_protect() could be called while already protected. In
that case the return value indicates that it is already protected.
sys_arch_protect() is only required if your port is supporting an operating
system.
*/ */
sys_prot_t sys_prot_t
sys_arch_protect(void) sys_arch_protect(void)
{ {
if (!g_lwip_protect_mutex) {
sys_mutex_new(&g_lwip_protect_mutex);
}
sys_mutex_lock(&g_lwip_protect_mutex); sys_mutex_lock(&g_lwip_protect_mutex);
return (sys_prot_t) 1; return (sys_prot_t) 1;
} }
/*-----------------------------------------------------------------------------------*/ /**
/* * @brief Unprotect critical region
This optional function does a "fast" set of critical region protection to the *
value specified by pval. See the documentation for sys_arch_protect() for * @param pval protection level
more information. This function is only required if your port is supporting
an operating system.
*/ */
void void
sys_arch_unprotect(sys_prot_t pval) sys_arch_unprotect(sys_prot_t pval)
{ {
LWIP_UNUSED_ARG(pval);
sys_mutex_unlock(&g_lwip_protect_mutex); sys_mutex_unlock(&g_lwip_protect_mutex);
} }
/* /*
* get per thread semphore * get per thread semaphore
*/ */
sys_sem_t* sys_thread_sem_get(void) sys_sem_t*
sys_thread_sem_get(void)
{ {
sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key); sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
@ -456,7 +500,8 @@ sys_sem_t* sys_thread_sem_get(void)
return sem; return sem;
} }
static void sys_thread_sem_free(void* data) // destructor for TLS semaphore static void
sys_thread_sem_free(void* data) // destructor for TLS semaphore
{ {
sys_sem_t *sem = (sys_sem_t*)(data); sys_sem_t *sem = (sys_sem_t*)(data);
@ -471,7 +516,8 @@ static void sys_thread_sem_free(void* data) // destructor for TLS semaphore
} }
} }
sys_sem_t* sys_thread_sem_init(void) sys_sem_t*
sys_thread_sem_init(void)
{ {
sys_sem_t *sem = (sys_sem_t*)mem_malloc(sizeof(sys_sem_t*)); sys_sem_t *sem = (sys_sem_t*)mem_malloc(sizeof(sys_sem_t*));
@ -491,7 +537,8 @@ sys_sem_t* sys_thread_sem_init(void)
return sem; return sem;
} }
void sys_thread_sem_deinit(void) void
sys_thread_sem_deinit(void)
{ {
sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key); sys_sem_t *sem = pthread_getspecific(sys_thread_sem_key);
if (sem != NULL) { if (sem != NULL) {
@ -500,9 +547,8 @@ void sys_thread_sem_deinit(void)
} }
} }
void sys_delay_ms(uint32_t ms) void
sys_delay_ms(uint32_t ms)
{ {
vTaskDelay(ms / portTICK_PERIOD_MS); vTaskDelay(ms / portTICK_PERIOD_MS);
} }

View File

@ -45,6 +45,10 @@
#define BYTE_ORDER LITTLE_ENDIAN #define BYTE_ORDER LITTLE_ENDIAN
#endif // BYTE_ORDER #endif // BYTE_ORDER
#ifndef CONFIG_LWIP_ESP_LWIP_ASSERT
#define LWIP_NOASSERT 1
#endif
typedef uint8_t u8_t; typedef uint8_t u8_t;
typedef int8_t s8_t; typedef int8_t s8_t;
typedef uint16_t u16_t; typedef uint16_t u16_t;

View File

@ -44,15 +44,22 @@ extern "C" {
#endif #endif
typedef xSemaphoreHandle sys_sem_t; typedef SemaphoreHandle_t sys_sem_t;
typedef xSemaphoreHandle sys_mutex_t; typedef SemaphoreHandle_t sys_mutex_t;
typedef xTaskHandle sys_thread_t; typedef TaskHandle_t sys_thread_t;
typedef struct sys_mbox_s { typedef struct sys_mbox_s {
xQueueHandle os_mbox; QueueHandle_t os_mbox;
void *owner; void *owner;
}* sys_mbox_t; }* sys_mbox_t;
/** This is returned by _fromisr() sys functions to tell the outermost function
* that a higher priority task was woken and the scheduler needs to be invoked.
*/
#define ERR_NEED_SCHED 123
void sys_delay_ms(uint32_t ms);
#define sys_msleep(ms) sys_delay_ms(ms)
#define LWIP_COMPAT_MUTEX 0 #define LWIP_COMPAT_MUTEX 0

View File

@ -15,6 +15,6 @@
#ifndef INET_H_ #ifndef INET_H_
#define INET_H_ #define INET_H_
#include "../../../lwip/src/include/lwip/inet.h" #include "lwip/inet.h"
#endif /* INET_H_ */ #endif /* INET_H_ */

View File

@ -169,18 +169,32 @@
-------------------------------- --------------------------------
*/ */
/** /**
* IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that * IP_REASSEMBLY==1: Reassemble incoming fragmented IP4 packets. Note that
* this option does not affect outgoing packet sizes, which can be controlled * this option does not affect outgoing packet sizes, which can be controlled
* via IP_FRAG. * via IP_FRAG.
*/ */
#define IP_REASSEMBLY CONFIG_LWIP_IP_REASSEMBLY #define IP_REASSEMBLY CONFIG_LWIP_IP4_REASSEMBLY
/** /**
* IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note * LWIP_IPV6_REASS==1: reassemble incoming IP6 packets that fragmented. Note that
* this option does not affect outgoing packet sizes, which can be controlled
* via LWIP_IPV6_FRAG.
*/
#define LWIP_IPV6_REASS CONFIG_LWIP_IP6_REASSEMBLY
/**
* IP_FRAG==1: Fragment outgoing IP4 packets if their size exceeds MTU. Note
* that this option does not affect incoming packet sizes, which can be * that this option does not affect incoming packet sizes, which can be
* controlled via IP_REASSEMBLY. * controlled via IP_REASSEMBLY.
*/ */
#define IP_FRAG CONFIG_LWIP_IP_FRAG #define IP_FRAG CONFIG_LWIP_IP4_FRAG
/**
* LWIP_IPV6_FRAG==1: Fragment outgoing IP6 packets if their size exceeds MTU. Note
* that this option does not affect incoming packet sizes, which can be
* controlled via IP_REASSEMBLY.
*/
#define LWIP_IPV6_FRAG CONFIG_LWIP_IP6_FRAG
/** /**
* IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally)
@ -197,6 +211,20 @@
*/ */
#define IP_REASS_MAX_PBUFS 10 #define IP_REASS_MAX_PBUFS 10
/**
* IP_FORWARD==1: Enables the ability to forward IP packets across network
* interfaces. If you are going to run lwIP on a device with only one network
* interface, define this to 0.
*/
#define IP_FORWARD CONFIG_LWIP_IP_FORWARD
/**
* IP_NAPT==1: Enables IPv4 Network Address and Port Translation.
* Note that both CONFIG_LWIP_IP_FORWARD and CONFIG_LWIP_L2_TO_L3_COPY options
* need to be enabled in system configuration for the NAPT to work on ESP platform
*/
#define IP_NAPT CONFIG_LWIP_IPV4_NAPT
/* /*
---------------------------------- ----------------------------------
---------- ICMP options ---------- ---------- ICMP options ----------
@ -323,6 +351,11 @@
*/ */
#define TCP_QUEUE_OOSEQ CONFIG_LWIP_TCP_QUEUE_OOSEQ #define TCP_QUEUE_OOSEQ CONFIG_LWIP_TCP_QUEUE_OOSEQ
/**
* LWIP_TCP_SACK_OUT==1: TCP will support sending selective acknowledgements (SACKs).
*/
#define LWIP_TCP_SACK_OUT CONFIG_LWIP_TCP_SACK_OUT
/** /**
* ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES==1: Keep TCP connection when IP changed * ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES==1: Keep TCP connection when IP changed
* scenario happens: 192.168.0.2 -> 0.0.0.0 -> 192.168.0.2 or 192.168.0.2 -> 0.0.0.0 * scenario happens: 192.168.0.2 -> 0.0.0.0 -> 192.168.0.2 or 192.168.0.2 -> 0.0.0.0
@ -393,6 +426,12 @@
#define TCP_RCV_SCALE CONFIG_LWIP_TCP_RCV_SCALE #define TCP_RCV_SCALE CONFIG_LWIP_TCP_RCV_SCALE
#endif #endif
/**
* LWIP_TCP_RTO_TIME: TCP rto time.
* Default is 3 second.
*/
#define LWIP_TCP_RTO_TIME CONFIG_LWIP_TCP_RTO_TIME
/* /*
---------------------------------- ----------------------------------
---------- Pbuf options ---------- ---------- Pbuf options ----------
@ -562,6 +601,11 @@
*/ */
#define LWIP_TCP_KEEPALIVE 1 #define LWIP_TCP_KEEPALIVE 1
/**
* LWIP_SO_LINGER==1: Enable SO_LINGER processing.
*/
#define LWIP_SO_LINGER CONFIG_LWIP_SO_LINGER
/** /**
* LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing.
*/ */
@ -625,6 +669,14 @@
#if PPP_SUPPORT #if PPP_SUPPORT
/**
* PPP_IPV6_SUPPORT == 1: Enable IPV6 support for local link
* between modem and lwIP stack.
* Some modems do not support IPV6 addressing in local link and
* the only option available is to disable IPV6 address negotiation.
*/
#define PPP_IPV6_SUPPORT CONFIG_LWIP_PPP_ENABLE_IPV6
/** /**
* PPP_NOTIFY_PHASE==1: Support PPP notify phase. * PPP_NOTIFY_PHASE==1: Support PPP notify phase.
*/ */
@ -667,6 +719,8 @@
#if PPP_DEBUG_ON #if PPP_DEBUG_ON
#define PPP_DEBUG LWIP_DBG_ON #define PPP_DEBUG LWIP_DBG_ON
#define PRINTPKT_SUPPORT 1
#define PPP_PROTOCOLNAME 1
#else #else
#define PPP_DEBUG LWIP_DBG_OFF #define PPP_DEBUG LWIP_DBG_OFF
#endif #endif
@ -793,7 +847,6 @@
#define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF #define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF
#define ESP_DHCP 1 #define ESP_DHCP 1
#define ESP_DNS 1 #define ESP_DNS 1
#define ESP_IPV6_AUTOCONFIG LWIP_IPV6
#define ESP_PERF 0 #define ESP_PERF 0
#define ESP_RANDOM_TCP_PORT 1 #define ESP_RANDOM_TCP_PORT 1
#define ESP_IP4_ATON 1 #define ESP_IP4_ATON 1
@ -817,6 +870,10 @@
#define ESP_LWIP_SELECT 1 #define ESP_LWIP_SELECT 1
#define ESP_LWIP_LOCK 1 #define ESP_LWIP_LOCK 1
#ifdef CONFIG_LWIP_IPV6_AUTOCONFIG
#define ESP_IPV6_AUTOCONFIG CONFIG_LWIP_IPV6_AUTOCONFIG
#endif
#ifdef ESP_IRAM_ATTR #ifdef ESP_IRAM_ATTR
#undef ESP_IRAM_ATTR #undef ESP_IRAM_ATTR
#endif #endif
@ -873,7 +930,10 @@ void tcp_print_status(int status, void* p, uint32_t tmp1, uint32_t tmp2, uint32_
*/ */
#define SNTP_SERVER_DNS 1 #define SNTP_SERVER_DNS 1
#define SNTP_UPDATE_DELAY CONFIG_LWIP_SNTP_UPDATE_DELAY // It disables a check of SNTP_UPDATE_DELAY it is done in sntp_set_sync_interval
#define SNTP_SUPPRESS_DELAY_CHECK
#define SNTP_UPDATE_DELAY (sntp_get_sync_interval())
#define SNTP_SET_SYSTEM_TIME_US(sec, us) \ #define SNTP_SET_SYSTEM_TIME_US(sec, us) \
do { \ do { \

View File

@ -1,4 +1,5 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
// Copyright 2020 Francesco Giancane <francesco.giancane@accenture.com>
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -12,22 +13,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#ifndef _NETINET_TCP_H
#define _NETINET_TCP_H
#ifndef _NETTEST_LWIP_IF_H_ #include "lwip/tcp.h"
#define _NETTEST_LWIP_IF_H_
#include "lwip/err.h" #endif /* _NETINET_TCP_H */
#ifdef __cplusplus
extern "C" {
#endif
err_t nettestif_init(struct netif *netif);
void nettestif_input(void *buffer, u16_t len);
#ifdef __cplusplus
}
#endif
#endif /* _NETTEST_LWIP_IF_H_ */

View File

@ -1,105 +0,0 @@
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/ethip6.h"
#include "netif/etharp.h"
#include "netif/wlanif.h"
#include <stdio.h>
#include <string.h>
#include "tcpip_adapter.h"
static struct netif *g_last_netif = NULL;
err_t nettestif_output(struct netif *netif, struct pbuf *p)
{
int i;
char *dat = p->payload;
/* output the packet to stdout */
printf("\nPacketOut:[");
for (i=0; i<p->len; i++) {
printf("%02x", *dat++);
}
printf("]\n");
return ERR_OK;
}
err_t nettestif_init(struct netif *netif)
{
g_last_netif = netif;
netif->hostname = CONFIG_LWIP_LOCAL_HOSTNAME;
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 100);
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
netif->output = etharp_output;
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
netif->linkoutput = nettestif_output;
/* set MAC hardware address length */
netif->hwaddr_len = ETHARP_HWADDR_LEN;
/* set MAC hardware address */
/* maximum transfer unit */
netif->mtu = 1500;
/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
#if ESP_LWIP
#if LWIP_IGMP
netif->flags |= NETIF_FLAG_IGMP;
#endif
#endif
return ERR_OK;
}
void nettestif_input(void *buffer, u16_t len)
{
struct pbuf *p;
if (g_last_netif == NULL) {
printf("error!");
return;
}
printf("simul in: %d\n", len);
if (len==0) return;
p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
p->l2_owner = NULL;
memcpy(p->payload, buffer, len);
/* full packet send to tcpip_thread to process
* on success - the packet is processed and deallocated in tcpip stack
* on failure - log error and deallocate the packet
*/
if (g_last_netif->input(p, g_last_netif) != ERR_OK) {
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
pbuf_free(p);
}
}

View File

@ -319,12 +319,17 @@ static void low_level_init(struct netif* netif)
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
#if ESP_LWIP
#if LWIP_IGMP #if LWIP_IGMP
netif->flags |= NETIF_FLAG_IGMP; netif->flags |= NETIF_FLAG_IGMP;
#endif #endif
#if LWIP_IPV6_AUTOCONFIG #endif
netif->ip6_autoconfig_enabled = 1;
#endif /* LWIP_IPV6_AUTOCONFIG */ #if ESP_IPV6
#if LWIP_IPV6 && LWIP_IPV6_MLD
netif->flags |= NETIF_FLAG_MLD6;
#endif
#endif
/* Do whatever else is needed to initialize interface. */ /* Do whatever else is needed to initialize interface. */
} }
@ -593,7 +598,9 @@ int8_t ethernetif_init(struct netif* netif)
/* Initialize interface hostname */ /* Initialize interface hostname */
#if ESP_LWIP #if ESP_LWIP
if (tcpip_adapter_get_hostname(tcpip_adapter_get_esp_if(netif), &netif->hostname) != ESP_OK) {
netif->hostname = CONFIG_LWIP_LOCAL_HOSTNAME; netif->hostname = CONFIG_LWIP_LOCAL_HOSTNAME;
}
#else #else
netif->hostname = "lwip"; netif->hostname = "lwip";
#endif #endif

View File

@ -1,27 +1,13 @@
import re import re
import os import os
import sys
import socket import socket
from threading import Thread, Event from threading import Thread, Event
import subprocess import subprocess
import time import time
from shutil import copyfile from shutil import copyfile
try: from tiny_test_fw import Utility, DUT
import IDF import ttfw_idf
from IDF.IDFDUT import ESP32DUT
except ImportError:
# this is a test case write with tiny-test-fw.
# to run test cases outside tiny-test-fw,
# we need to set environment variable `TEST_FW_PATH`,
# then get and insert `TEST_FW_PATH` to sys path before import FW module
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import IDF
import DUT
import Utility
stop_sock_listener = Event() stop_sock_listener = Event()
stop_io_listener = Event() stop_io_listener = Event()
@ -73,7 +59,7 @@ def sock_listener(dut1):
sock = None sock = None
@IDF.idf_example_test(env_tag="Example_WIFI") @ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def lwip_test_suite(env, extra_data): def lwip_test_suite(env, extra_data):
global stop_io_listener global stop_io_listener
global stop_sock_listener global stop_sock_listener
@ -84,12 +70,12 @@ def lwip_test_suite(env, extra_data):
3. Execute ttcn3 test suite 3. Execute ttcn3 test suite
4. Collect result from ttcn3 4. Collect result from ttcn3
""" """
dut1 = env.get_dut("net_suite", "examples/system/network_tests", dut_class=ESP32DUT) dut1 = env.get_dut("net_suite", "examples/system/network_tests", dut_class=ttfw_idf.ESP32DUT)
# check and log bin size # check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "net_suite.bin") binary_file = os.path.join(dut1.app.binary_path, "net_suite.bin")
bin_size = os.path.getsize(binary_file) bin_size = os.path.getsize(binary_file)
IDF.log_performance("net_suite", "{}KB".format(bin_size // 1024)) ttfw_idf.log_performance("net_suite", "{}KB".format(bin_size // 1024))
IDF.check_performance("net_suite", bin_size // 1024) ttfw_idf.check_performance("net_suite", bin_size // 1024, dut1.TARGET)
dut1.start_app() dut1.start_app()
thread1 = Thread(target=sock_listener, args=(dut1, )) thread1 = Thread(target=sock_listener, args=(dut1, ))
thread2 = Thread(target=io_listener, args=(dut1, )) thread2 = Thread(target=io_listener, args=(dut1, ))

View File

@ -168,6 +168,7 @@ typedef enum{
} tcpip_adapter_option_mode_t; } tcpip_adapter_option_mode_t;
typedef enum{ typedef enum{
TCPIP_ADAPTER_SUBNET_MASK = 1, /**< network mask */
TCPIP_ADAPTER_DOMAIN_NAME_SERVER = 6, /**< domain name server */ TCPIP_ADAPTER_DOMAIN_NAME_SERVER = 6, /**< domain name server */
TCPIP_ADAPTER_ROUTER_SOLICITATION_ADDRESS = 32, /**< solicitation router address */ TCPIP_ADAPTER_ROUTER_SOLICITATION_ADDRESS = 32, /**< solicitation router address */
TCPIP_ADAPTER_REQUESTED_IP_ADDRESS = 50, /**< request IP address pool */ TCPIP_ADAPTER_REQUESTED_IP_ADDRESS = 50, /**< request IP address pool */

View File

@ -719,6 +719,7 @@ esp_err_t tcpip_adapter_dhcps_option(tcpip_adapter_option_mode_t opt_op, tcpip_a
*(uint32_t *)opt_val = *(uint32_t *)opt_info; *(uint32_t *)opt_val = *(uint32_t *)opt_info;
break; break;
} }
case SUBNET_MASK:
case REQUESTED_IP_ADDRESS: { case REQUESTED_IP_ADDRESS: {
memcpy(opt_val, opt_info, opt_len); memcpy(opt_val, opt_info, opt_len);
break; break;
@ -756,6 +757,10 @@ esp_err_t tcpip_adapter_dhcps_option(tcpip_adapter_option_mode_t opt_op, tcpip_a
} }
break; break;
} }
case SUBNET_MASK: {
memcpy(opt_info, opt_val, opt_len);
break;
}
case REQUESTED_IP_ADDRESS: { case REQUESTED_IP_ADDRESS: {
tcpip_adapter_ip_info_t info; tcpip_adapter_ip_info_t info;
uint32_t softap_ip = 0; uint32_t softap_ip = 0;
@ -1226,11 +1231,11 @@ esp_err_t tcpip_adapter_get_hostname(tcpip_adapter_if_t tcpip_if, const char **h
} }
p_netif = esp_netif[tcpip_if]; p_netif = esp_netif[tcpip_if];
if (p_netif != NULL) { if (p_netif != NULL && p_netif->hostname != NULL) {
*hostname = p_netif->hostname; *hostname = p_netif->hostname;
return ESP_OK; return ESP_OK;
} else { } else {
return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY;
} }
#else #else
return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY;

View File

@ -13,10 +13,33 @@ menu "Example Connection Configuration"
Can be left blank if the network has no security set. Can be left blank if the network has no security set.
config EXAMPLE_CONNECT_IPV6 config EXAMPLE_CONNECT_IPV6
bool "Obtain IPv6 link-local address" bool "Obtain IPv6 address"
default n default n
select LWIP_IPV6 select LWIP_IPV6
help help
By default, examples will wait until IPv4 and IPv6 addresses are obtained. By default, examples will wait until IPv4 and IPv6 local link addresses are obtained.
Disable this option if the network does not support IPv6. Disable this option if the network does not support IPv6.
Choose the preferred IPv6 address type if the connection code should wait until other than
the local link address gets assigned.
if EXAMPLE_CONNECT_IPV6
choice EXAMPLE_CONNECT_PREFERRED_IPV6
prompt "Preferred IPv6 Type"
default EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
help
Select which kind of IPv6 address the connect logic waits for.
config EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
bool "Local Link Address"
help
Blocks until Local link address assigned.
config EXAMPLE_CONNECT_IPV6_PREF_GLOBAL
bool "Global Address"
select LWIP_IPV6_AUTOCONFIG
help
Blocks until Global address assigned.
endchoice
endif
endmenu endmenu

View File

@ -27,6 +27,11 @@
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 #ifdef CONFIG_EXAMPLE_CONNECT_IPV6
#define CONNECTED_BITS (GOT_IPV4_BIT | GOT_IPV6_BIT) #define CONNECTED_BITS (GOT_IPV4_BIT | GOT_IPV6_BIT)
#if defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK)
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE 0
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_GLOBAL)
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE 1
#endif
#else #else
#define CONNECTED_BITS (GOT_IPV4_BIT) #define CONNECTED_BITS (GOT_IPV4_BIT)
#endif #endif
@ -78,8 +83,14 @@ static void on_got_ipv6(void *arg, esp_event_base_t event_base,
{ {
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
memcpy(&s_ipv6_addr, &event->ip6_info.ip, sizeof(s_ipv6_addr)); memcpy(&s_ipv6_addr, &event->ip6_info.ip, sizeof(s_ipv6_addr));
if (EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) {
if (ip6_addr_isglobal(&s_ipv6_addr)) {
xEventGroupSetBits(s_connect_event_group, GOT_IPV6_BIT); xEventGroupSetBits(s_connect_event_group, GOT_IPV6_BIT);
} }
} else {
xEventGroupSetBits(s_connect_event_group, GOT_IPV6_BIT);
}
}
#endif // CONFIG_EXAMPLE_CONNECT_IPV6 #endif // CONFIG_EXAMPLE_CONNECT_IPV6

View File

@ -57,6 +57,7 @@ static void tcp_client_task(void *pvParameters)
inet6_aton(HOST_IP_ADDR, &destAddr.sin6_addr); inet6_aton(HOST_IP_ADDR, &destAddr.sin6_addr);
destAddr.sin6_family = AF_INET6; destAddr.sin6_family = AF_INET6;
destAddr.sin6_port = htons(PORT); destAddr.sin6_port = htons(PORT);
destAddr.sin6_scope_id = tcpip_adapter_get_netif_index(TCPIP_ADAPTER_IF_STA);
addr_family = AF_INET6; addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6; ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1); inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
@ -72,6 +73,8 @@ static void tcp_client_task(void *pvParameters)
int err = connect(sock, (struct sockaddr *)&destAddr, sizeof(destAddr)); int err = connect(sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err != 0) { if (err != 0) {
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno); ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
close(sock);
continue;
} }
ESP_LOGI(TAG, "Successfully connected"); ESP_LOGI(TAG, "Successfully connected");

View File

@ -57,6 +57,7 @@ static void udp_client_task(void *pvParameters)
inet6_aton(HOST_IP_ADDR, &destAddr.sin6_addr); inet6_aton(HOST_IP_ADDR, &destAddr.sin6_addr);
destAddr.sin6_family = AF_INET6; destAddr.sin6_family = AF_INET6;
destAddr.sin6_port = htons(PORT); destAddr.sin6_port = htons(PORT);
destAddr.sin6_scope_id = tcpip_adapter_get_netif_index(TCPIP_ADAPTER_IF_STA);
addr_family = AF_INET6; addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6; ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1); inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);