478 lines
16 KiB
C
478 lines
16 KiB
C
|
/*
|
||
|
* ESPRESSIF MIT License
|
||
|
*
|
||
|
* Copyright (c) 2017 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||
|
*
|
||
|
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
|
||
|
* it is free of charge, to any person obtaining a copy of this software and associated
|
||
|
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||
|
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||
|
* to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all copies or
|
||
|
* substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "lwip/inet.h"
|
||
|
#include "lwip/err.h"
|
||
|
#include "lwip/pbuf.h"
|
||
|
#include "lwip/mem.h"
|
||
|
#include "lwip/tcp_impl.h"
|
||
|
#include "lwip/udp.h"
|
||
|
|
||
|
#include "esp_common.h"
|
||
|
#include "espconn/espconn_udp.h"
|
||
|
|
||
|
#ifdef MEMLEAK_DEBUG
|
||
|
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
#define IP_MAX_MTU 1500
|
||
|
extern espconn_msg* plink_active;
|
||
|
uint8 default_interface;
|
||
|
|
||
|
enum send_opt {
|
||
|
ESPCONN_SENDTO,
|
||
|
ESPCONN_SEND
|
||
|
};
|
||
|
|
||
|
static void ICACHE_FLASH_ATTR espconn_data_sentcb(struct espconn* pespconn)
|
||
|
{
|
||
|
if (pespconn == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pespconn->sent_callback != NULL) {
|
||
|
pespconn->sent_callback(pespconn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ICACHE_FLASH_ATTR espconn_data_sent(void* arg, enum send_opt opt)
|
||
|
{
|
||
|
espconn_msg* psent = arg;
|
||
|
|
||
|
if (psent == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (psent->pcommon.cntr == 0) {
|
||
|
psent->pespconn->state = ESPCONN_CONNECT;
|
||
|
|
||
|
if (psent->pcommon.err == 0) {
|
||
|
espconn_data_sentcb(psent->pespconn);
|
||
|
}
|
||
|
} else {
|
||
|
if (opt == ESPCONN_SEND) {
|
||
|
espconn_udp_sent(arg, psent->pcommon.ptrbuf, psent->pcommon.cntr);
|
||
|
} else {
|
||
|
espconn_udp_sendto(arg, psent->pcommon.ptrbuf, psent->pcommon.cntr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* FunctionName : espconn_udp_sent
|
||
|
* Description : sent data for client or server
|
||
|
* Parameters : void *arg -- client or server to send
|
||
|
* uint8* psent -- Data to send
|
||
|
* uint16 length -- Length of data to send
|
||
|
* Returns : return espconn error code.
|
||
|
* - ESPCONN_OK. Successful. No error occured.
|
||
|
* - ESPCONN_MEM. Out of memory.
|
||
|
* - ESPCONN_RTE. Could not find route to destination address.
|
||
|
* - More errors could be returned by lower protocol layers.
|
||
|
*******************************************************************************/
|
||
|
err_t ICACHE_FLASH_ATTR
|
||
|
espconn_udp_sent(void* arg, uint8* psent, uint16 length)
|
||
|
{
|
||
|
espconn_msg* pudp_sent = arg;
|
||
|
struct udp_pcb* upcb = pudp_sent->pcommon.pcb;
|
||
|
struct pbuf* p, *q , *p_temp;
|
||
|
u8_t* data = NULL;
|
||
|
u16_t cnt = 0;
|
||
|
u16_t datalen = 0;
|
||
|
u16_t i = 0;
|
||
|
err_t err;
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb));
|
||
|
|
||
|
if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) {
|
||
|
return ESPCONN_ARG;
|
||
|
}
|
||
|
|
||
|
if ((IP_MAX_MTU - 20 - 8) < length) {
|
||
|
datalen = IP_MAX_MTU - 20 - 8;
|
||
|
} else {
|
||
|
datalen = length;
|
||
|
}
|
||
|
|
||
|
p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p));
|
||
|
|
||
|
if (p != NULL) {
|
||
|
q = p;
|
||
|
|
||
|
while (q != NULL) {
|
||
|
data = (u8_t*)q->payload;
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data));
|
||
|
|
||
|
for (i = 0; i < q->len; i++) {
|
||
|
data[i] = ((u8_t*) psent)[cnt++];
|
||
|
}
|
||
|
|
||
|
q = q->next;
|
||
|
}
|
||
|
} else {
|
||
|
return ESPCONN_MEM;
|
||
|
}
|
||
|
|
||
|
upcb->remote_port = pudp_sent->pespconn->proto.udp->remote_port;
|
||
|
IP4_ADDR(&(upcb->remote_ip.ip4), pudp_sent->pespconn->proto.udp->remote_ip[0],
|
||
|
pudp_sent->pespconn->proto.udp->remote_ip[1],
|
||
|
pudp_sent->pespconn->proto.udp->remote_ip[2],
|
||
|
pudp_sent->pespconn->proto.udp->remote_ip[3]);
|
||
|
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port));
|
||
|
|
||
|
struct netif* sta_netif = (struct netif*)eagle_lwip_getif(0x00);
|
||
|
struct netif* ap_netif = (struct netif*)eagle_lwip_getif(0x01);
|
||
|
|
||
|
if (wifi_get_opmode() == ESPCONN_AP_STA && default_interface == ESPCONN_AP_STA && sta_netif != NULL && ap_netif != NULL) {
|
||
|
if (netif_is_up(sta_netif) && netif_is_up(ap_netif) && \
|
||
|
ip_addr_isbroadcast(&(upcb->remote_ip.ip4), sta_netif) && \
|
||
|
ip_addr_isbroadcast(&(upcb->remote_ip.ip4), ap_netif)) {
|
||
|
|
||
|
p_temp = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
|
||
|
|
||
|
if (pbuf_copy(p_temp, p) != ERR_OK) {
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent: copying to new pbuf failed\n"));
|
||
|
return ESPCONN_ARG;
|
||
|
}
|
||
|
|
||
|
netif_set_default(sta_netif);
|
||
|
err = udp_send(upcb, p_temp);
|
||
|
pbuf_free(p_temp);
|
||
|
netif_set_default(ap_netif);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = udp_send(upcb, p);
|
||
|
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d\n", __LINE__, err));
|
||
|
|
||
|
if (p->ref != 0) {
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p));
|
||
|
pbuf_free(p);
|
||
|
pudp_sent->pcommon.ptrbuf = psent + datalen;
|
||
|
pudp_sent->pcommon.cntr = length - datalen;
|
||
|
pudp_sent->pcommon.err = err;
|
||
|
espconn_data_sent(pudp_sent, ESPCONN_SEND);
|
||
|
|
||
|
if (err > 0) {
|
||
|
return ESPCONN_IF;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
} else {
|
||
|
pbuf_free(p);
|
||
|
return ESPCONN_RTE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* FunctionName : espconn_udp_sendto
|
||
|
* Description : sent data for UDP
|
||
|
* Parameters : void *arg -- UDP to send
|
||
|
* uint8* psent -- Data to send
|
||
|
* uint16 length -- Length of data to send
|
||
|
* Returns : return espconn error code.
|
||
|
* - ESPCONN_OK. Successful. No error occured.
|
||
|
* - ESPCONN_MEM. Out of memory.
|
||
|
* - ESPCONN_RTE. Could not find route to destination address.
|
||
|
* - More errors could be returned by lower protocol layers.
|
||
|
*******************************************************************************/
|
||
|
err_t ICACHE_FLASH_ATTR
|
||
|
espconn_udp_sendto(void* arg, uint8* psent, uint16 length)
|
||
|
{
|
||
|
espconn_msg* pudp_sent = arg;
|
||
|
struct udp_pcb* upcb = pudp_sent->pcommon.pcb;
|
||
|
struct espconn* pespconn = pudp_sent->pespconn;
|
||
|
struct pbuf* p, *q , *p_temp;
|
||
|
struct ip_addr dst_ip;
|
||
|
u16_t dst_port;
|
||
|
u8_t* data = NULL;
|
||
|
u16_t cnt = 0;
|
||
|
u16_t datalen = 0;
|
||
|
u16_t i = 0;
|
||
|
err_t err;
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb));
|
||
|
|
||
|
if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) {
|
||
|
return ESPCONN_ARG;
|
||
|
}
|
||
|
|
||
|
if ((IP_MAX_MTU - 20 - 8) < length) {
|
||
|
datalen = IP_MAX_MTU - 20 - 8;
|
||
|
} else {
|
||
|
datalen = length;
|
||
|
}
|
||
|
|
||
|
p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p));
|
||
|
|
||
|
if (p != NULL) {
|
||
|
q = p;
|
||
|
|
||
|
while (q != NULL) {
|
||
|
data = (u8_t*)q->payload;
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data));
|
||
|
|
||
|
for (i = 0; i < q->len; i++) {
|
||
|
data[i] = ((u8_t*) psent)[cnt++];
|
||
|
}
|
||
|
|
||
|
q = q->next;
|
||
|
}
|
||
|
} else {
|
||
|
return ESPCONN_MEM;
|
||
|
}
|
||
|
|
||
|
dst_port = pespconn->proto.udp->remote_port;
|
||
|
IP4_ADDR(&dst_ip, pespconn->proto.udp->remote_ip[0],
|
||
|
pespconn->proto.udp->remote_ip[1], pespconn->proto.udp->remote_ip[2],
|
||
|
pespconn->proto.udp->remote_ip[3]);
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port));
|
||
|
|
||
|
#if LWIP_IPV6 || LWIP_IGMP
|
||
|
ipX_addr_t* dst_ip_route = ip_2_ipX(&dst_ip);
|
||
|
|
||
|
if (ipX_addr_ismulticast(PCB_ISIPV6(upcb), dst_ip_route)) {
|
||
|
/* For multicast, find a netif based on source address. */
|
||
|
#if LWIP_IPV6
|
||
|
if (PCB_ISIPV6(upcb)) {
|
||
|
dst_ip_route = &upcb->local_ip;
|
||
|
} else
|
||
|
#endif /* LWIP_IPV6 */
|
||
|
{
|
||
|
#if LWIP_IGMP
|
||
|
upcb->multicast_ip.addr = dst_ip.addr;
|
||
|
#endif /* LWIP_IGMP */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif /* LWIP_IPV6 || LWIP_IGMP */
|
||
|
|
||
|
struct netif* sta_netif = (struct netif*)eagle_lwip_getif(0x00);
|
||
|
struct netif* ap_netif = (struct netif*)eagle_lwip_getif(0x01);
|
||
|
|
||
|
if (wifi_get_opmode() == ESPCONN_AP_STA && default_interface == ESPCONN_AP_STA && sta_netif != NULL && ap_netif != NULL) {
|
||
|
if (netif_is_up(sta_netif) && netif_is_up(ap_netif) && \
|
||
|
ip_addr_isbroadcast(&(upcb->remote_ip.ip4), sta_netif) && \
|
||
|
ip_addr_isbroadcast(&(upcb->remote_ip.ip4), ap_netif)) {
|
||
|
|
||
|
p_temp = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
|
||
|
|
||
|
if (pbuf_copy(p_temp, p) != ERR_OK) {
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sendto: copying to new pbuf failed\n"));
|
||
|
return ESPCONN_ARG;
|
||
|
}
|
||
|
|
||
|
netif_set_default(sta_netif);
|
||
|
err = udp_sendto(upcb, p_temp, &dst_ip, dst_port);
|
||
|
pbuf_free(p_temp);
|
||
|
netif_set_default(ap_netif);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = udp_sendto(upcb, p, &dst_ip, dst_port);
|
||
|
|
||
|
if (p->ref != 0) {
|
||
|
pbuf_free(p);
|
||
|
pudp_sent->pcommon.ptrbuf = psent + datalen;
|
||
|
pudp_sent->pcommon.cntr = length - datalen;
|
||
|
pudp_sent->pcommon.err = err;
|
||
|
espconn_data_sent(pudp_sent, ESPCONN_SENDTO);
|
||
|
|
||
|
if (err > 0) {
|
||
|
return ESPCONN_IF;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
} else {
|
||
|
pbuf_free(p);
|
||
|
return ESPCONN_RTE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* FunctionName : espconn_udp_server_recv
|
||
|
* Description : This callback will be called when receiving a datagram.
|
||
|
* Parameters : arg -- user supplied argument
|
||
|
* upcb -- the udp_pcb which received data
|
||
|
* p -- the packet buffer that was received
|
||
|
* addr -- the remote IP address from which the packet was received
|
||
|
* port -- the remote port from which the packet was received
|
||
|
* Returns : none
|
||
|
*******************************************************************************/
|
||
|
static void ICACHE_FLASH_ATTR
|
||
|
espconn_udp_recv(void* arg, struct udp_pcb* upcb, struct pbuf* p,
|
||
|
struct ip_addr* addr, u16_t port)
|
||
|
{
|
||
|
espconn_msg* precv = arg;
|
||
|
u8_t* pdata = NULL;
|
||
|
u16_t length = 0;
|
||
|
struct ip_info ipconfig;
|
||
|
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %p\n", __LINE__, upcb));
|
||
|
//ESPCONN_API_MUTEX_TAKE();
|
||
|
|
||
|
precv->pcommon.remote_ip[0] = ip4_addr1_16(addr);
|
||
|
precv->pcommon.remote_ip[1] = ip4_addr2_16(addr);
|
||
|
precv->pcommon.remote_ip[2] = ip4_addr3_16(addr);
|
||
|
precv->pcommon.remote_ip[3] = ip4_addr4_16(addr);
|
||
|
precv->pcommon.remote_port = port;
|
||
|
precv->pcommon.pcb = upcb;
|
||
|
|
||
|
if (wifi_get_opmode() != 1) {
|
||
|
wifi_get_ip_info(1, &ipconfig);
|
||
|
|
||
|
if (!ip_addr_netcmp(addr, &ipconfig.ip, &ipconfig.netmask)) {
|
||
|
wifi_get_ip_info(0, &ipconfig);
|
||
|
}
|
||
|
} else {
|
||
|
wifi_get_ip_info(0, &ipconfig);
|
||
|
}
|
||
|
|
||
|
precv->pespconn->proto.udp->local_ip[0] = ip4_addr1_16(&ipconfig.ip);
|
||
|
precv->pespconn->proto.udp->local_ip[1] = ip4_addr2_16(&ipconfig.ip);
|
||
|
precv->pespconn->proto.udp->local_ip[2] = ip4_addr3_16(&ipconfig.ip);
|
||
|
precv->pespconn->proto.udp->local_ip[3] = ip4_addr4_16(&ipconfig.ip);
|
||
|
|
||
|
if (p != NULL) {
|
||
|
pdata = (u8_t*)os_zalloc(p ->tot_len + 1);
|
||
|
length = pbuf_copy_partial(p, pdata, p ->tot_len, 0);
|
||
|
precv->pcommon.pcb = upcb;
|
||
|
pbuf_free(p);
|
||
|
|
||
|
if (length != 0) {
|
||
|
if (precv->pespconn->recv_callback != NULL) {
|
||
|
precv->pespconn->recv_callback(precv->pespconn, pdata, length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
os_free(pdata);
|
||
|
//ESPCONN_API_MUTEX_GIVE();
|
||
|
} else {
|
||
|
//ESPCONN_API_MUTEX_GIVE();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* FunctionName : espconn_udp_disconnect
|
||
|
* Description : A new incoming connection has been disconnected.
|
||
|
* Parameters : espconn -- the espconn used to disconnect with host
|
||
|
* Returns : none
|
||
|
*******************************************************************************/
|
||
|
void ICACHE_FLASH_ATTR espconn_udp_disconnect(espconn_msg* pdiscon)
|
||
|
{
|
||
|
if (pdiscon == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
struct udp_pcb* upcb = pdiscon->pcommon.pcb;
|
||
|
|
||
|
udp_disconnect(upcb);
|
||
|
|
||
|
udp_remove(upcb);
|
||
|
|
||
|
espconn_list_delete(&plink_active, pdiscon);
|
||
|
|
||
|
os_free(pdiscon);
|
||
|
|
||
|
pdiscon = NULL;
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* FunctionName : espconn_udp_server
|
||
|
* Description : Initialize the server: set up a PCB and bind it to the port
|
||
|
* Parameters : pespconn -- the espconn used to build server
|
||
|
* Returns : none
|
||
|
*******************************************************************************/
|
||
|
sint8 ICACHE_FLASH_ATTR
|
||
|
espconn_udp_server(struct espconn* pespconn)
|
||
|
{
|
||
|
struct udp_pcb* upcb = NULL;
|
||
|
espconn_msg* pserver = NULL;
|
||
|
upcb = udp_new();
|
||
|
|
||
|
if (upcb == NULL) {
|
||
|
return ESPCONN_MEM;
|
||
|
} else {
|
||
|
pserver = (espconn_msg*)os_zalloc(sizeof(espconn_msg));
|
||
|
|
||
|
if (pserver == NULL) {
|
||
|
udp_remove(upcb);
|
||
|
return ESPCONN_MEM;
|
||
|
}
|
||
|
|
||
|
pserver->pcommon.pcb = upcb;
|
||
|
pserver->pespconn = pespconn;
|
||
|
espconn_list_creat(&plink_active, pserver);
|
||
|
udp_bind(upcb, IP_ADDR_ANY, pserver->pespconn->proto.udp->local_port);
|
||
|
udp_recv(upcb, espconn_udp_recv, (void*)pserver);
|
||
|
return ESPCONN_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* FunctionName : espconn_igmp_leave
|
||
|
* Description : leave a multicast group
|
||
|
* Parameters : host_ip -- the ip address of udp server
|
||
|
* multicast_ip -- multicast ip given by user
|
||
|
* Returns : none
|
||
|
*******************************************************************************/
|
||
|
sint8 ICACHE_FLASH_ATTR
|
||
|
espconn_igmp_leave(ip_addr_t* host_ip, ip_addr_t* multicast_ip)
|
||
|
{
|
||
|
#if LWIP_IGMP
|
||
|
|
||
|
if (igmp_leavegroup(host_ip, multicast_ip) != ERR_OK) {
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_leave_multigrup failed!\n"));
|
||
|
return -1;
|
||
|
};
|
||
|
|
||
|
#endif
|
||
|
return ESPCONN_OK;
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* FunctionName : espconn_igmp_join
|
||
|
* Description : join a multicast group
|
||
|
* Parameters : host_ip -- the ip address of udp server
|
||
|
* multicast_ip -- multicast ip given by user
|
||
|
* Returns : none
|
||
|
*******************************************************************************/
|
||
|
sint8 ICACHE_FLASH_ATTR
|
||
|
espconn_igmp_join(ip_addr_t* host_ip, ip_addr_t* multicast_ip)
|
||
|
{
|
||
|
#if LWIP_IGMP
|
||
|
|
||
|
if (igmp_joingroup(host_ip, multicast_ip) != ERR_OK) {
|
||
|
LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_join_multigrup failed!\n"));
|
||
|
return -1;
|
||
|
};
|
||
|
|
||
|
#endif
|
||
|
/* join to any IP address at the port */
|
||
|
return ESPCONN_OK;
|
||
|
}
|