/* * ESPRESSIF MIT License * * Copyright (c) 2017 * * 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/netif.h" #include "lwip/inet.h" #include "netif/etharp.h" #include "lwip/tcp.h" #include "lwip/ip.h" #include "lwip/init.h" #include "lwip/tcp_impl.h" #include "lwip/memp.h" #include "lwip/mem.h" #include "esp_common.h" #include "espconn/espconn_tcp.h" #ifdef MEMLEAK_DEBUG static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; #endif extern espconn_msg* plink_active; extern espconn_msg* pserver_list; extern struct espconn_packet pktinfo[2]; extern struct tcp_pcb** const tcp_pcb_lists[]; #if !NO_SYS static void os_post(uint8 prio, uint32 type, uint32 arg); #else os_event_t espconn_TaskQueue[espconn_TaskQueueLen]; #endif static err_t espconn_client_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err); static void espconn_client_close(void* arg, struct tcp_pcb* pcb, u8 type); static err_t espconn_server_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err); static void espconn_server_close(void* arg, struct tcp_pcb* pcb, u8 type); ///////////////////////////////common function///////////////////////////////// /****************************************************************************** * FunctionName : espconn_kill_oldest * Description : kill the oldest TCP block * Parameters : none * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_kill_oldest(void) { struct tcp_pcb* pcb, *inactive; u32_t inactivity; inactivity = 0; inactive = NULL; /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { inactivity = tcp_ticks - pcb->tmr; inactive = pcb; } } if (inactive != NULL) { tcp_abort(inactive); } /* Go through the list of FIN_WAIT_2 pcbs and get the oldest pcb. */ inactivity = 0; inactive = NULL; for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { if (pcb->state == FIN_WAIT_1 || pcb->state == FIN_WAIT_2) { if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { inactivity = tcp_ticks - pcb->tmr; inactive = pcb; } } } /*Purges the PCB, removes it from a PCB list and frees the memory*/ if (inactive != NULL) { tcp_pcb_remove(&tcp_active_pcbs, inactive); memp_free(MEMP_TCP_PCB, inactive); } /* Go through the list of LAST_ACK pcbs and get the oldest pcb. */ inactivity = 0; inactive = NULL; for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { if (pcb->state == LAST_ACK) { if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { inactivity = tcp_ticks - pcb->tmr; inactive = pcb; } } } /*Purges the PCB, removes it from a PCB list and frees the memory*/ if (inactive != NULL) { tcp_pcb_remove(&tcp_active_pcbs, inactive); memp_free(MEMP_TCP_PCB, inactive); } } /****************************************************************************** * FunctionName : espconn_kill_oldest_pcb * Description : find the oldest TCP block by state * Parameters : none * Returns : none *******************************************************************************/ void ICACHE_FLASH_ATTR espconn_kill_oldest_pcb(void) { struct tcp_pcb* cpcb = NULL; uint8 i = 0; uint8 num_tcp_fin = 0; for (i = 2; i < 4; i ++) { for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->state == TIME_WAIT) { num_tcp_fin ++; if (num_tcp_fin == MEMP_NUM_TCP_PCB) { break; } } if (cpcb->state == FIN_WAIT_1 || cpcb->state == FIN_WAIT_2 || cpcb->state == LAST_ACK) { num_tcp_fin++; if (num_tcp_fin == MEMP_NUM_TCP_PCB) { break; } } } if (num_tcp_fin == MEMP_NUM_TCP_PCB) { num_tcp_fin = 0; espconn_kill_oldest(); } else if (cpcb == NULL) { num_tcp_fin = 0; } } } /****************************************************************************** * FunctionName : espconn_kill_pcb * Description : kill all the TCP block by port * Parameters : none * Returns : none *******************************************************************************/ void ICACHE_FLASH_ATTR espconn_kill_pcb(u16_t port) { struct tcp_pcb* cpcb = NULL; uint8 i = 0; struct tcp_pcb* inactive = NULL; struct tcp_pcb* prev = NULL; u8_t pcb_remove; /* Check if the address already is in use (on all lists) */ for (i = 1; i < 4; i++) { cpcb = *tcp_pcb_lists[i]; while (cpcb != NULL) { pcb_remove = 0; if (cpcb->local_port == port) { ++pcb_remove; } /* If the PCB should be removed, do it. */ if (pcb_remove) { /* Remove PCB from tcp_pcb_lists list. */ inactive = cpcb; cpcb = inactive->next; tcp_pcb_remove(tcp_pcb_lists[i], inactive); memp_free(MEMP_TCP_PCB, inactive); } else { cpcb = cpcb->next; } } } } /****************************************************************************** * FunctionName : espconn_find_current_pcb * Description : find the TCP block which option * Parameters : pcurrent_msg -- the node in the list which active * Returns : TCP block point *******************************************************************************/ struct tcp_pcb* ICACHE_FLASH_ATTR espconn_find_current_pcb(espconn_msg* pcurrent_msg) { uint16 local_port = pcurrent_msg->pcommon.local_port; uint32 local_ip = pcurrent_msg->pcommon.local_ip; uint16 remote_port = pcurrent_msg->pcommon.remote_port; uint32 remote_ip = *((uint32*)&pcurrent_msg->pcommon.remote_ip); struct tcp_pcb* find_pcb = NULL; if (pcurrent_msg ->preverse == NULL) { /*Find the server's TCP block*/ if (local_ip == 0 || local_port == 0) { return pcurrent_msg->pcommon.pcb; } for (find_pcb = tcp_active_pcbs; find_pcb != NULL; find_pcb = find_pcb->next) { if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.ip4.addr == remote_ip) && (find_pcb->local_port == local_port) && (find_pcb->local_ip.ip4.addr == local_ip)) { return find_pcb; } } for (find_pcb = tcp_tw_pcbs; find_pcb != NULL; find_pcb = find_pcb->next) { if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.ip4.addr == remote_ip) && (find_pcb->local_port == local_port) && (find_pcb->local_ip.ip4.addr == local_ip)) { return find_pcb; } } } else {/*Find the client's TCP block*/ if (remote_ip == 0 || remote_port == 0) { return pcurrent_msg->pcommon.pcb; } for (find_pcb = tcp_active_pcbs; find_pcb != NULL; find_pcb = find_pcb->next) { if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.ip4.addr == remote_ip)) { return find_pcb; } } for (find_pcb = tcp_tw_pcbs; find_pcb != NULL; find_pcb = find_pcb->next) { if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.ip4.addr == remote_ip)) { return find_pcb; } } } return NULL; } /****************************************************************************** * FunctionName : espconn_tcp_memp_free * Description : frees the connection memory in the server mode * Parameters : arg -- Additional argument to pass to the function * Returns : none *******************************************************************************/ void ICACHE_FLASH_ATTR espconn_tcp_memp_free(espconn_msg* pmemp) { struct espconn* espconn = NULL; if (pmemp == NULL) { return; } if (pmemp->espconn_mode == ESPCONN_TCPSERVER_MODE) { if (pmemp->pespconn != NULL && pmemp->pespconn->proto.tcp != NULL) { os_free(pmemp->pespconn->proto.tcp); } pmemp->pespconn->proto.tcp = NULL; os_free(pmemp->pespconn); pmemp->pespconn = NULL; } if (pmemp->readbuf != NULL) { ringbuf_free(&pmemp->readbuf); } os_free(pmemp); pmemp = NULL; } /****************************************************************************** * FunctionName : espconn_tcp_reconnect * Description : reconnect with host,MUST be only used in espconn_thread * Parameters : arg -- Additional argument to pass to the callback function * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_tcp_reconnect(void* arg) { espconn_msg* precon_cb = arg; sint8 re_err = 0; espconn_buf* perr_buf = NULL; espconn_buf* perr_back = NULL; espconn_kill_oldest_pcb(); if (precon_cb != NULL) { struct espconn* espconn = precon_cb->preverse; re_err = precon_cb->pcommon.err; if (precon_cb->pespconn != NULL) { if (espconn != NULL) { /*Process the server's message block*/ if (precon_cb->pespconn->proto.tcp != NULL) { espconn_copy_partial(espconn, precon_cb->pespconn); } } else {/*Process the client's message block*/ espconn = precon_cb->pespconn; } } /*to prevent memory leaks, ensure that each allocated is deleted*/ perr_buf = precon_cb->pcommon.pbuf; while (perr_buf != NULL) { perr_back = perr_buf; perr_buf = perr_back->pnext; espconn_pbuf_delete(&precon_cb->pcommon.pbuf, perr_back); os_free(perr_back); perr_back = NULL; } bzero(&pktinfo[1], sizeof(struct espconn_packet)); memcpy(&pktinfo[1], (void*)&precon_cb->pcommon.packet_info, sizeof(struct espconn_packet)); if (espconn && espconn->proto.tcp && espconn->proto.tcp->reconnect_callback != NULL) { espconn_reconnect_callback reconnect_callback = espconn->proto.tcp->reconnect_callback; ESPCONN_API_MUTEX_GIVE(); reconnect_callback(espconn, re_err); ESPCONN_API_MUTEX_TAKE(); } /*frees the connection memory*/ espconn_tcp_memp_free(precon_cb); } else { espconn_printf("espconn_tcp_reconnect err\n"); } } /****************************************************************************** * FunctionName : espconn_tcp_disconnect * Description : disconnect with host,MUST be only used in espconn_thread * Parameters : arg -- Additional argument to pass to the callback function * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_tcp_disconnect_successful(void* arg) { espconn_msg* pdiscon_cb = arg; sint8 dis_err = 0; espconn_buf* pdis_buf = NULL; espconn_buf* pdis_back = NULL; espconn_kill_oldest_pcb(); if (pdiscon_cb != NULL) { struct espconn* espconn = pdiscon_cb->preverse; dis_err = pdiscon_cb->pcommon.err; if (pdiscon_cb->pespconn != NULL) { struct tcp_pcb* pcb = NULL; if (espconn != NULL) { /*Process the server's message block*/ if (pdiscon_cb->pespconn->proto.tcp != NULL && espconn->proto.tcp) { espconn_copy_partial(espconn, pdiscon_cb->pespconn); } } else {/*Process the client's message block*/ espconn = pdiscon_cb->pespconn; } /*process the current TCP block*/ pcb = espconn_find_current_pcb(pdiscon_cb); if (pcb != NULL) { if (espconn_reuse_disabled(pdiscon_cb)) { struct tcp_pcb* cpcb = NULL; struct tcp_pcb* prev = NULL; u8_t pcb_remove; espconn_printf("espconn_tcp_disconnect_successful %d, %d\n", pcb->state, pcb->local_port); cpcb = tcp_tw_pcbs; while (cpcb != NULL) { pcb_remove = 0; if (cpcb->local_port == pcb->local_port) { ++pcb_remove; } /* If the PCB should be removed, do it. */ if (pcb_remove) { struct tcp_pcb* backup_pcb = NULL; tcp_pcb_purge(cpcb); /* Remove PCB from tcp_tw_pcbs list. */ if (prev != NULL) { LWIP_ASSERT("espconn_tcp_delete: middle cpcb != tcp_tw_pcbs", cpcb != tcp_tw_pcbs); prev->next = cpcb->next; } else { /* This PCB was the first. */ LWIP_ASSERT("espconn_tcp_delete: first cpcb == tcp_tw_pcbs", tcp_tw_pcbs == cpcb); tcp_tw_pcbs = cpcb->next; } backup_pcb = cpcb; cpcb = cpcb->next; memp_free(MEMP_TCP_PCB, backup_pcb); } else { prev = cpcb; cpcb = cpcb->next; } } } else { tcp_arg(pcb, NULL); tcp_err(pcb, NULL); } } } /*to prevent memory leaks, ensure that each allocated is deleted*/ pdis_buf = pdiscon_cb->pcommon.pbuf; while (pdis_buf != NULL) { pdis_back = pdis_buf; pdis_buf = pdis_back->pnext; espconn_pbuf_delete(&pdiscon_cb->pcommon.pbuf, pdis_back); os_free(pdis_back); pdis_back = NULL; } bzero(&pktinfo[0], sizeof(struct espconn_packet)); memcpy(&pktinfo[0], (void*)&pdiscon_cb->pcommon.packet_info, sizeof(struct espconn_packet)); if (espconn->proto.tcp && espconn->proto.tcp->disconnect_callback != NULL) { espconn_connect_callback disconnect_callback = espconn->proto.tcp->disconnect_callback; ESPCONN_API_MUTEX_GIVE(); disconnect_callback(espconn); ESPCONN_API_MUTEX_TAKE(); } /*frees the connection memory*/ espconn_tcp_memp_free(pdiscon_cb); } else { espconn_printf("espconn_tcp_disconnect err\n"); } } /****************************************************************************** * FunctionName : espconn_tcp_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_tcp_sent(void* arg, uint8* psent, uint16 length) { espconn_msg* ptcp_sent = arg; struct tcp_pcb* pcb = NULL; err_t err = 0; u16_t len = 0; u8_t data_to_send = false; espconn_printf("espconn_tcp_sent ptcp_sent %p psent %p length %d\n", ptcp_sent, psent, length); /*Check the parameters*/ if (ptcp_sent == NULL || psent == NULL || length == 0) { return ESPCONN_ARG; } /*Set the packet length depend on the sender buffer space*/ pcb = ptcp_sent->pcommon.pcb; if (tcp_sndbuf(pcb) < length) { len = tcp_sndbuf(pcb); } else { len = length; LWIP_ASSERT("length did not fit into uint16!", (len == length)); } if (len > (2 * pcb->mss)) { len = 2 * pcb->mss; } /*Write data for sending, but does not send it immediately*/ do { espconn_printf("espconn_tcp_sent writing %d bytes %p\n", len, pcb); if (espconn_copy_disabled(ptcp_sent)) { err = tcp_write(pcb, psent, len, 1); } else { err = tcp_write(pcb, psent, len, 0); } if (err == ERR_MEM) { if (len < 3) { len--; } else { len /= 2; } } } while (err == ERR_MEM && len > 0); /*Find out what we can send and send it, offset the buffer point for next send*/ if (err == ERR_OK) { ptcp_sent->pcommon.ptail->punsent = psent + len; ptcp_sent->pcommon.ptail->unsent = length - len; err = tcp_output(pcb); /*If enable the copy option, change the flag for next write*/ if (espconn_copy_disabled(ptcp_sent)) { if (ptcp_sent->pcommon.ptail->unsent == 0) { ptcp_sent->pcommon.write_flag = true; os_post(espconn_TaskPrio, SIG_ESPCONN_WRITE, (uint32_t)ptcp_sent); } } espconn_printf("espconn_tcp_sent %d\n", err); } return err; } /****************************************************************************** * FunctionName : espconn_close * Description : The connection has been successfully closed. * Parameters : arg -- Additional argument to pass to the callback function * Returns : none *******************************************************************************/ void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg* pdiscon, u8 type) { if (pdiscon != NULL) { /*disconnect with the host by send the FIN frame*/ if (pdiscon->preverse != NULL) { espconn_server_close(pdiscon, pdiscon->pcommon.pcb, type); } else { espconn_client_close(pdiscon, pdiscon->pcommon.pcb, type); } } else { espconn_printf("espconn_tcp_disconnect err.\n"); } } ///////////////////////////////client function///////////////////////////////// /****************************************************************************** * FunctionName : espconn_client_close * Description : The connection shall be actively closed. * Parameters : pcb -- Additional argument to pass to the callback function * pcb -- the pcb to close * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_client_close(void* arg, struct tcp_pcb* pcb, u8 type) { err_t err; espconn_msg* pclose = arg; pclose->pcommon.pcb = pcb; /*avoid recalling the disconnect function*/ tcp_recv(pcb, NULL); if (type == 0) { err = tcp_close(pcb); } else { tcp_sent(pcb, NULL); tcp_err(pcb, NULL); tcp_abort(pcb); err = ERR_OK; } if (err != ERR_OK) { /* closing failed, try again later */ tcp_recv(pcb, espconn_client_recv); } else { /* closing succeeded */ if (type == 0) { tcp_sent(pcb, NULL); tcp_err(pcb, NULL); } /*switch the state of espconn for application process*/ pclose->pespconn->state = ESPCONN_CLOSE; os_post(espconn_TaskPrio, SIG_ESPCONN_CLOSE, (uint32_t)pclose); } } //***********Code for WIFI_BLOCK from upper************** sint8 ICACHE_FLASH_ATTR espconn_recv_hold(struct espconn* pespconn) { //1st, according to espconn code, have to find out the escpconn_msg by pespconn; espconn_msg* pnode = NULL; bool value = false; if (pespconn == NULL) { return ESPCONN_ARG; } value = espconn_find_connection(pespconn, &pnode); if (value != true) { espconn_printf("RecvHold, By pespconn,find conn_msg fail\n"); return ESPCONN_ARG; } //2nd, the actual operation if (pnode->recv_hold_flag == 0) { pnode->recv_hold_flag = 1; pnode->recv_holded_buf_Len = 0; } return ESPCONN_OK; } sint8 ICACHE_FLASH_ATTR espconn_recv_unhold(struct espconn* pespconn) { //1st, according to espconn code, have to find out the escpconn_msg by pespconn; espconn_msg* pnode = NULL; bool value = false; if (pespconn == NULL) { return ESPCONN_ARG; } value = espconn_find_connection(pespconn, &pnode); if (value != true) { espconn_printf("RecvHold, By pespconn,find conn_msg fail\n"); return ESPCONN_ARG; } //2nd, the actual operation if (pnode->recv_hold_flag == 1) { if (pespconn->type == ESPCONN_TCP) { tcp_recved(pnode->pcommon.pcb, pnode->recv_holded_buf_Len); } pnode->recv_holded_buf_Len = 0; pnode->recv_hold_flag = 0; } return ESPCONN_OK; } //***********Code for WIFI_BLOCK from upper************** sint8 ICACHE_FLASH_ATTR espconn_lock_recv(espconn_msg* plockmsg) { if (plockmsg == NULL || plockmsg->pespconn == NULL) { return ESPCONN_ARG; } if (plockmsg->pespconn->recv_callback == NULL) { if (plockmsg->readbuf == NULL) { plockmsg->readbuf = ringbuf_new(TCP_WND); if (plockmsg->readbuf == NULL) { return ESPCONN_MEM; } } return espconn_recv_hold(plockmsg->pespconn); } return ESPCONN_OK; } sint8 ICACHE_FLASH_ATTR espconn_unlock_recv(espconn_msg* punlockmsg) { if (punlockmsg == NULL || punlockmsg->pespconn == NULL) { return ESPCONN_ARG; } if (punlockmsg->pespconn->recv_callback != NULL) { return espconn_recv_unhold(punlockmsg->pespconn); } return ESPCONN_OK; } /****************************************************************************** * FunctionName : espconn_client_recv * Description : Data has been received on this pcb. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb which received data * p -- The received data (or NULL when the connection has been closed!) * err -- An error code if there has been an error receiving * Returns : ERR_ABRT: if you have called tcp_abort from within the function! *******************************************************************************/ static err_t ICACHE_FLASH_ATTR espconn_client_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) { espconn_msg* precv_cb = arg; //ESPCONN_API_MUTEX_TAKE(); tcp_arg(pcb, arg); /*lock the window because of application layer don't need the data*/ espconn_lock_recv(precv_cb); if (p != NULL) { /*To update and advertise a larger window*/ if (precv_cb->recv_hold_flag == 0) { tcp_recved(pcb, p->tot_len); } else { precv_cb->recv_holded_buf_Len += p->tot_len; } } if (precv_cb->pespconn->recv_callback != NULL) { if (err == ERR_OK && p != NULL) { char* pdata = NULL; u16_t length = 0; /*Copy the contents of a packet buffer to an application buffer. *to prevent memory leaks, ensure that each allocated is deleted*/ pdata = (char*)os_zalloc(p ->tot_len + 1); length = pbuf_copy_partial(p, pdata, p ->tot_len, 0); pbuf_free(p); if (length != 0) { /*switch the state of espconn for application process*/ precv_cb->pespconn ->state = ESPCONN_READ; precv_cb->pcommon.pcb = pcb; precv_cb->pespconn->recv_callback(precv_cb->pespconn, pdata, length); /*switch the state of espconn for next packet copy*/ if (pcb->state == ESTABLISHED) { precv_cb->pespconn ->state = ESPCONN_CONNECT; } } /*to prevent memory leaks, ensure that each allocated is deleted*/ os_free(pdata); pdata = NULL; } } else { /*unregister receive function*/ struct pbuf* pthis = NULL; for (pthis = p; pthis != NULL; pthis = pthis->next) { ringbuf_memcpy_into(precv_cb->readbuf, pthis->payload, pthis->len); pbuf_free(pthis); } } if (err == ERR_OK && p == NULL) { espconn_client_close(precv_cb, pcb, 0); } //ESPCONN_API_MUTEX_GIVE(); return ERR_OK; } /****************************************************************************** * FunctionName : espconn_tcp_write * Description : write the packet which in the active connection's list. * Parameters : arg -- the node pointer which reverse the packet * Returns : ESPCONN_MEM: memory error * ESPCONN_OK:have enough space for write packet *******************************************************************************/ err_t ICACHE_FLASH_ATTR espconn_tcp_write(void* arg) { espconn_msg* pwrite = arg; err_t err = ERR_OK; struct tcp_pcb* pcb = pwrite->pcommon.pcb; /*for one active connection,limit the sender buffer space*/ if (tcp_nagle_disabled(pcb) && (pcb->snd_queuelen >= TCP_SND_QUEUELEN)) { return ESPCONN_MEM; } while (tcp_sndbuf(pcb) != 0) { if (pwrite->pcommon.ptail != NULL) { /*Find the node whether in the list's tail or not*/ if (pwrite->pcommon.ptail->unsent == 0) { pwrite->pcommon.ptail = pwrite->pcommon.ptail->pnext; continue; } /*Send the packet for the active connection*/ err = espconn_tcp_sent(pwrite, pwrite->pcommon.ptail->punsent, pwrite->pcommon.ptail->unsent); if (err != ERR_OK) { break; } } else { break; } } return err; } /****************************************************************************** * FunctionName : espconn_tcp_reconnect * Description : reconnect with host * Parameters : arg -- Additional argument to pass to the callback function * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_tcp_finish(void* arg) { espconn_msg* pfinish = arg; espconn_buf* premove = NULL; uint16 len = 0; espconn_tcp_write(pfinish); while (pfinish->pcommon.pbuf != NULL) { premove = pfinish->pcommon.pbuf; pfinish->pcommon.pbuf->tot_len += len; /*application packet has been sent and acknowledged by the remote host, * to prevent memory leaks, ensure that each allocated is deleted*/ if (premove->tot_len >= premove->len) { espconn_pbuf_delete(&pfinish->pcommon.pbuf, premove); len = premove->tot_len - premove->len; pfinish->pcommon.packet_info.sent_length = premove->len; os_free(premove); premove = NULL; pfinish->pespconn->state = ESPCONN_CONNECT; if (pfinish->pespconn->sent_callback != NULL) { pfinish->pespconn->sent_callback(pfinish->pespconn); } pfinish->pcommon.packet_info.sent_length = len; } else { break; } } } /****************************************************************************** * FunctionName : espconn_client_sent * Description : Data has been sent and acknowledged by the remote host. * This means that more data can be sent. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb for which data has been acknowledged * len -- The amount of bytes acknowledged * Returns : ERR_OK: try to send some data by calling tcp_output * ERR_ABRT: if you have called tcp_abort from within the function! *******************************************************************************/ static err_t ICACHE_FLASH_ATTR espconn_client_sent(void* arg, struct tcp_pcb* pcb, u16_t len) { espconn_msg* psent_cb = arg; psent_cb->pcommon.pcb = pcb; psent_cb->pcommon.pbuf->tot_len += len; psent_cb->pcommon.packet_info.sent_length = len; /*Send more data for one active connection*/ espconn_tcp_finish(psent_cb); return ERR_OK; } /****************************************************************************** * FunctionName : espconn_client_err * Description : The pcb had an error and is already deallocated. * The argument might still be valid (if != NULL). * Parameters : arg -- Additional argument to pass to the callback function * err -- Error code to indicate why the pcb has been closed * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_client_err(void* arg, err_t err) { espconn_msg* perr_cb = arg; struct tcp_pcb* pcb = NULL; LWIP_UNUSED_ARG(err); if (perr_cb != NULL) { pcb = perr_cb->pcommon.pcb; perr_cb->pespconn->state = ESPCONN_CLOSE; espconn_printf("espconn_client_err %d %d %d\n", pcb->state, pcb->nrtx, err); // /*remove the node from the client's active connection list*/ // espconn_list_delete(&plink_active, perr_cb); /*Set the error code depend on the error type and control block state*/ if (err == ERR_ABRT) { switch (pcb->state) { case SYN_SENT: if (pcb->nrtx == TCP_SYNMAXRTX) { perr_cb->pcommon.err = ESPCONN_CONN; } else { perr_cb->pcommon.err = err; } break; case ESTABLISHED: if (pcb->nrtx == TCP_MAXRTX) { perr_cb->pcommon.err = ESPCONN_TIMEOUT; } else { perr_cb->pcommon.err = err; } break; case FIN_WAIT_1: if (pcb->nrtx == TCP_MAXRTX) { perr_cb->pcommon.err = ESPCONN_CLSD; } else { perr_cb->pcommon.err = err; } break; case FIN_WAIT_2: perr_cb->pcommon.err = ESPCONN_CLSD; break; case CLOSED: perr_cb->pcommon.err = ESPCONN_CONN; break; } } else { perr_cb->pcommon.err = err; } /*post the singer to the task for processing the connection*/ os_post(espconn_TaskPrio, SIG_ESPCONN_ERRER, (uint32_t)perr_cb); } } /****************************************************************************** * FunctionName : espconn_client_connect * Description : A new incoming connection has been connected. * Parameters : arg -- Additional argument to pass to the callback function * tpcb -- The connection pcb which is connected * err -- An unused error code, always ERR_OK currently * Returns : connection result *******************************************************************************/ static err_t ICACHE_FLASH_ATTR espconn_client_connect(void* arg, struct tcp_pcb* tpcb, err_t err) { espconn_msg* pcon = arg; espconn_printf("espconn_client_connect pcon %p tpcb %p\n", pcon, tpcb); if (err == ERR_OK) { /*Reserve the remote information for current active connection*/ pcon->pespconn->state = ESPCONN_CONNECT; pcon->pcommon.err = err; pcon->pcommon.pcb = tpcb; pcon->pcommon.local_port = tpcb->local_port; pcon->pcommon.local_ip = tpcb->local_ip.ip4.addr; pcon->pcommon.remote_port = tpcb->remote_port; pcon->pcommon.remote_ip[0] = ip4_addr1_16(&tpcb->remote_ip); pcon->pcommon.remote_ip[1] = ip4_addr2_16(&tpcb->remote_ip); pcon->pcommon.remote_ip[2] = ip4_addr3_16(&tpcb->remote_ip); pcon->pcommon.remote_ip[3] = ip4_addr4_16(&tpcb->remote_ip); pcon->pcommon.write_flag = true; tcp_arg(tpcb, (void*) pcon); /*Set the specify function that should be called * when TCP data has been successfully delivered, * when active connection receives data*/ tcp_sent(tpcb, espconn_client_sent); tcp_recv(tpcb, espconn_client_recv); /*Disable Nagle algorithm default*/ tcp_nagle_disable(tpcb); /*Default set the total number of espconn_buf on the unsent lists for one*/ espconn_tcp_set_buf_count(pcon->pespconn, 1); if (pcon->pespconn->proto.tcp->connect_callback != NULL) { pcon->pespconn->proto.tcp->connect_callback(pcon->pespconn); } /*Enable keep alive option*/ if (espconn_keepalive_disabled(pcon)) { espconn_keepalive_enable(tpcb); } // /*lock the window because of application layer don't need the data*/ // espconn_lock_recv(pcon); } else { espconn_printf("err in host connected (%s)\n", lwip_strerr(err)); } return err; } /****************************************************************************** * FunctionName : espconn_tcp_client * Description : Initialize the client: set up a connect PCB and bind it to * the defined port * Parameters : espconn -- the espconn used to build client * Returns : none *******************************************************************************/ sint8 ICACHE_FLASH_ATTR espconn_tcp_client(struct espconn* espconn) { struct tcp_pcb* pcb = NULL; struct ip_addr ipaddr; espconn_msg* pclient = NULL; /*Creates a new client control message*/ pclient = (espconn_msg*)os_zalloc(sizeof(espconn_msg)); if (pclient == NULL) { return ESPCONN_MEM; } /*Set an IP address given for Little-endian.*/ IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0], espconn->proto.tcp->remote_ip[1], espconn->proto.tcp->remote_ip[2], espconn->proto.tcp->remote_ip[3]); /*Creates a new TCP protocol control block*/ pcb = tcp_new(); if (pcb == NULL) { /*to prevent memory leaks, ensure that each allocated is deleted*/ os_free(pclient); pclient = NULL; return ESPCONN_MEM; } else { /*insert the node to the active connection list*/ espconn_list_creat(&plink_active, pclient); tcp_arg(pcb, (void*)pclient); tcp_err(pcb, espconn_client_err); pclient->preverse = NULL; pclient->pespconn = espconn; pclient->pespconn->state = ESPCONN_WAIT; pclient->pcommon.pcb = pcb; tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); #if 0 pclient->pcommon.err = tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); if (pclient->pcommon.err != ERR_OK) { /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, pclient); memp_free(MEMP_TCP_PCB, pcb); os_free(pclient); pclient = NULL; return ERR_USE; } #endif /*Establish the connection*/ pclient->espconn_mode = ESPCONN_TCPCLIENT_MODE; pclient->pcommon.err = tcp_connect(pcb, &ipaddr, pclient->pespconn->proto.tcp->remote_port, espconn_client_connect); if (pclient->pcommon.err == ERR_RTE) { /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, pclient); espconn_kill_pcb(pcb->local_port); os_free(pclient); pclient = NULL; return ESPCONN_RTE; } return pclient->pcommon.err; } } ///////////////////////////////server function///////////////////////////////// /****************************************************************************** * FunctionName : espconn_server_close * Description : The connection shall be actively closed. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- the pcb to close * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_server_close(void* arg, struct tcp_pcb* pcb, u8 type) { err_t err; espconn_msg* psclose = arg; psclose->pcommon.pcb = pcb; /*avoid recalling the disconnect function*/ tcp_recv(pcb, NULL); if (type == 0) { err = tcp_close(pcb); } else { tcp_poll(pcb, NULL, 0); tcp_sent(pcb, NULL); tcp_err(pcb, NULL); tcp_abort(pcb); err = ERR_OK; } if (err != ERR_OK) { /* closing failed, try again later */ tcp_recv(pcb, espconn_server_recv); } else { /* closing succeeded */ if (type == 0) { tcp_poll(pcb, NULL, 0); tcp_sent(pcb, NULL); tcp_err(pcb, NULL); } /*switch the state of espconn for application process*/ psclose->pespconn->state = ESPCONN_CLOSE; #if TCP_LISTEN_BACKLOG struct tcp_pcb* lpcb = NULL; struct espconn* pespconn = psclose->preverse; espconn_msg* plist = NULL; /*find the active server node*/ for (plist = pserver_list; plist != NULL; plist = plist->pnext) { if (pespconn == plist->pespconn) { /*find the tcp_pcb in the LISTEN state*/ lpcb = plist->preverse; tcp_accepted(lpcb); break; } } #endif os_post(espconn_TaskPrio, SIG_ESPCONN_CLOSE, (uint32_t)psclose); } } /****************************************************************************** * FunctionName : espconn_server_recv * Description : Data has been received on this pcb. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb which received data * p -- The received data (or NULL when the connection has been closed!) * err -- An error code if there has been an error receiving * Returns : ERR_ABRT: if you have called tcp_abort from within the function! *******************************************************************************/ static err_t ICACHE_FLASH_ATTR espconn_server_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) { espconn_msg* precv_cb = arg; //ESPCONN_API_MUTEX_TAKE(); tcp_arg(pcb, arg); espconn_printf("server has application data received: %d\n", system_get_free_heap_size()); /*lock the window because of application layer don't need the data*/ espconn_lock_recv(precv_cb); if (p != NULL) { /*To update and advertise a larger window*/ if (precv_cb->recv_hold_flag == 0) { tcp_recved(pcb, p->tot_len); } else { precv_cb->recv_holded_buf_Len += p->tot_len; } } /*register receive function*/ if (precv_cb->pespconn->recv_callback != NULL) { if (err == ERR_OK && p != NULL) { u8_t* data_ptr = NULL; u32_t data_cntr = 0; /*clear the count for connection timeout*/ precv_cb->pcommon.recv_check = 0; /*Copy the contents of a packet buffer to an application buffer. *to prevent memory leaks, ensure that each allocated is deleted*/ data_ptr = (u8_t*) os_zalloc(p ->tot_len + 1); data_cntr = pbuf_copy_partial(p, data_ptr, p->tot_len, 0); pbuf_free(p); if (data_cntr != 0) { /*switch the state of espconn for application process*/ precv_cb->pespconn->state = ESPCONN_READ; precv_cb->pcommon.pcb = pcb; precv_cb->pespconn->recv_callback(precv_cb->pespconn, data_ptr, data_cntr); /*switch the state of espconn for next packet copy*/ if (pcb->state == ESTABLISHED) { precv_cb->pespconn->state = ESPCONN_CONNECT; } } /*to prevent memory leaks, ensure that each allocated is deleted*/ os_free(data_ptr); data_ptr = NULL; espconn_printf("server's application data has been processed: %d\n", system_get_free_heap_size()); } } else { /*unregister receive function*/ struct pbuf* pthis = NULL; for (pthis = p; pthis != NULL; pthis = pthis->next) { ringbuf_memcpy_into(precv_cb->readbuf, pthis->payload, pthis->len); pbuf_free(pthis); } } if (err == ERR_OK && p == NULL) { espconn_server_close(precv_cb, pcb, 0); } //ESPCONN_API_MUTEX_GIVE(); return ERR_OK; } /****************************************************************************** * FunctionName : espconn_server_sent * Description : Data has been sent and acknowledged by the remote host. * This means that more data can be sent. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb for which data has been acknowledged * len -- The amount of bytes acknowledged * Returns : ERR_OK: try to send some data by calling tcp_output * ERR_ABRT: if you have called tcp_abort from within the function! *******************************************************************************/ static err_t ICACHE_FLASH_ATTR espconn_server_sent(void* arg, struct tcp_pcb* pcb, u16_t len) { espconn_msg* psent_cb = arg; psent_cb->pcommon.pcb = pcb; psent_cb->pcommon.recv_check = 0; psent_cb->pcommon.pbuf->tot_len += len; psent_cb->pcommon.packet_info.sent_length = len; /*Send more data for one active connection*/ espconn_tcp_finish(psent_cb); return ERR_OK; } /****************************************************************************** * FunctionName : espconn_server_poll * Description : The poll function is called every 3nd second. * If there has been no data sent (which resets the retries) in 3 seconds, close. * If the last portion of a file has not been sent in 3 seconds, close. * * This could be increased, but we don't want to waste resources for bad connections. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb for which data has been acknowledged * Returns : ERR_OK: try to send some data by calling tcp_output * ERR_ABRT: if you have called tcp_abort from within the function! *******************************************************************************/ static err_t ICACHE_FLASH_ATTR espconn_server_poll(void* arg, struct tcp_pcb* pcb) { espconn_msg* pspoll_cb = arg; /*exception calling abandon the connection for send a RST frame*/ if (arg == NULL) { tcp_abandon(pcb, 0); tcp_poll(pcb, NULL, 0); return ERR_OK; } espconn_printf("espconn_server_poll %d %d\n", pspoll_cb->pcommon.recv_check, pcb->state); pspoll_cb->pcommon.pcb = pcb; if (pcb->state == ESTABLISHED) { pspoll_cb->pcommon.recv_check++; if (pspoll_cb->pcommon.timeout != 0) { /*no data sent in one active connection's set timeout, close.*/ if (pspoll_cb->pcommon.recv_check >= pspoll_cb->pcommon.timeout) { pspoll_cb->pcommon.recv_check = 0; espconn_server_close(pspoll_cb, pcb, 0); } } else { espconn_msg* ptime_msg = pserver_list; while (ptime_msg != NULL) { if (ptime_msg->pespconn == pspoll_cb->preverse) { if (ptime_msg->pcommon.timeout != 0) { /*no data sent in server's set timeout, close.*/ if (pspoll_cb->pcommon.recv_check >= ptime_msg->pcommon.timeout) { pspoll_cb->pcommon.recv_check = 0; espconn_server_close(pspoll_cb, pcb, 0); } } else {/*don't close for ever*/ pspoll_cb->pcommon.recv_check = 0; } break; } ptime_msg = ptime_msg->pnext; } } } else { espconn_server_close(pspoll_cb, pcb, 0); } return ERR_OK; } /****************************************************************************** * FunctionName : esponn_server_err * Description : The pcb had an error and is already deallocated. * The argument might still be valid (if != NULL). * Parameters : arg -- Additional argument to pass to the callback function * err -- Error code to indicate why the pcb has been closed * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR esponn_server_err(void* arg, err_t err) { espconn_msg* pserr_cb = arg; struct tcp_pcb* pcb = NULL; if (pserr_cb != NULL) { pcb = pserr_cb->pcommon.pcb; pserr_cb->pespconn->state = ESPCONN_CLOSE; // /*remove the node from the server's active connection list*/ // espconn_list_delete(&plink_active, pserr_cb); /*Set the error code depend on the error type and control block state*/ if (err == ERR_ABRT) { switch (pcb->state) { case SYN_RCVD: if (pcb->nrtx == TCP_SYNMAXRTX) { pserr_cb->pcommon.err = ESPCONN_CONN; } else { pserr_cb->pcommon.err = err; } break; case ESTABLISHED: if (pcb->nrtx == TCP_MAXRTX) { pserr_cb->pcommon.err = ESPCONN_TIMEOUT; } else { pserr_cb->pcommon.err = err; } break; case CLOSE_WAIT: if (pcb->nrtx == TCP_MAXRTX) { pserr_cb->pcommon.err = ESPCONN_CLSD; } else { pserr_cb->pcommon.err = err; } break; case LAST_ACK: pserr_cb->pcommon.err = ESPCONN_CLSD; break; case CLOSED: pserr_cb->pcommon.err = ESPCONN_CONN; break; default : break; } } else { pserr_cb->pcommon.err = err; } /*post the singer to the task for processing the connection*/ os_post(espconn_TaskPrio, SIG_ESPCONN_ERRER, (uint32_t)pserr_cb); } } /****************************************************************************** * FunctionName : espconn_tcp_accept * Description : A new incoming connection has been accepted. * Parameters : arg -- Additional argument to pass to the callback function * pcb -- The connection pcb which is accepted * err -- An unused error code, always ERR_OK currently * Returns : acception result *******************************************************************************/ static err_t ICACHE_FLASH_ATTR espconn_tcp_accept(void* arg, struct tcp_pcb* pcb, err_t err) { struct espconn* espconn = arg; espconn_msg* paccept = NULL; remot_info* pinfo = NULL; LWIP_UNUSED_ARG(err); if (!espconn || !espconn->proto.tcp) { return ERR_ARG; } tcp_arg(pcb, paccept); tcp_err(pcb, esponn_server_err); /*Ensure the active connection is less than the count of active connections on the server*/ espconn_get_connection_info(espconn, &pinfo , 0); espconn_printf("espconn_tcp_accept link_cnt: %d\n", espconn->link_cnt); if (espconn->link_cnt == espconn_tcp_get_max_con_allow(espconn)) { return ERR_ISCONN; } /*Creates a new active connect control message*/ paccept = (espconn_msg*)os_zalloc(sizeof(espconn_msg)); tcp_arg(pcb, paccept); if (paccept == NULL) { return ERR_MEM; } /*Insert the node to the active connection list*/ espconn_list_creat(&plink_active, paccept); paccept->preverse = espconn; paccept->espconn_mode = ESPCONN_TCPSERVER_MODE; paccept->pespconn = (struct espconn*)os_zalloc(sizeof(struct espconn)); if (paccept->pespconn == NULL) { return ERR_MEM; } paccept->pespconn->proto.tcp = (esp_tcp*)os_zalloc(sizeof(esp_tcp)); if (paccept->pespconn->proto.tcp == NULL) { return ERR_MEM; } /*Reserve the remote information for current active connection*/ paccept->pcommon.pcb = pcb; paccept->pcommon.remote_port = pcb->remote_port; paccept->pcommon.remote_ip[0] = ip4_addr1_16(&pcb->remote_ip); paccept->pcommon.remote_ip[1] = ip4_addr2_16(&pcb->remote_ip); paccept->pcommon.remote_ip[2] = ip4_addr3_16(&pcb->remote_ip); paccept->pcommon.remote_ip[3] = ip4_addr4_16(&pcb->remote_ip); paccept->pcommon.write_flag = true; memcpy(espconn->proto.tcp->remote_ip, paccept->pcommon.remote_ip, 4); espconn->proto.tcp->remote_port = pcb->remote_port; espconn->state = ESPCONN_CONNECT; espconn_copy_partial(paccept->pespconn, espconn); /*Set the specify function that should be called * when TCP data has been successfully delivered, * when active connection receives data, * or periodically from active connection*/ tcp_sent(pcb, espconn_server_sent); tcp_recv(pcb, espconn_server_recv); tcp_poll(pcb, espconn_server_poll, 4); /* every 1 seconds */ /*Disable Nagle algorithm default*/ tcp_nagle_disable(pcb); /*Default set the total number of espconn_buf on the unsent lists for one*/ espconn_tcp_set_buf_count(paccept->pespconn, 1); if (paccept->pespconn->proto.tcp->connect_callback != NULL) { paccept->pespconn->proto.tcp->connect_callback(paccept->pespconn); } /*Enable keep alive option*/ if (espconn_keepalive_disabled(paccept)) { espconn_keepalive_enable(pcb); } // /*lock the window because of application layer don't need the data*/ // espconn_lock_recv(paccept); return ERR_OK; } /****************************************************************************** * FunctionName : espconn_tcp_server * Description : Initialize the server: set up a listening PCB and bind it to * the defined port * Parameters : espconn -- the espconn used to build server * Returns : none *******************************************************************************/ sint8 ICACHE_FLASH_ATTR espconn_tcp_server(struct espconn* espconn) { struct tcp_pcb* pcb = NULL; espconn_msg* pserver = NULL; /*Creates a new server control message*/ pserver = (espconn_msg*)os_zalloc(sizeof(espconn_msg)); if (pserver == NULL) { return ESPCONN_MEM; } /*Creates a new TCP protocol control block*/ pcb = tcp_new(); if (pcb == NULL) { /*to prevent memory leaks, ensure that each allocated is deleted*/ os_free(pserver); pserver = NULL; return ESPCONN_MEM; } else { struct tcp_pcb* lpcb = NULL; /*Binds the connection to a local port number and any IP address*/ tcp_bind(pcb, IP_ADDR_ANY, espconn->proto.tcp->local_port); lpcb = pcb; /*malloc and set the state of the connection to be LISTEN*/ pcb = tcp_listen(pcb); if (pcb != NULL) { /*insert the node to the active connection list*/ espconn_list_creat(&pserver_list, pserver); pserver->preverse = pcb; pserver->pespconn = espconn; pserver->count_opt = MEMP_NUM_TCP_PCB; pserver->pcommon.timeout = 0x0a; espconn ->state = ESPCONN_LISTEN; /*set the specify argument that should be passed callback function*/ tcp_arg(pcb, (void*)espconn); /*accept callback function to call for this control block*/ tcp_accept(pcb, espconn_tcp_accept); return ESPCONN_OK; } else { /*to prevent memory leaks, ensure that each allocated is deleted*/ memp_free(MEMP_TCP_PCB, lpcb); os_free(pserver); pserver = NULL; return ESPCONN_MEM; } } } /****************************************************************************** * FunctionName : espconn_tcp_delete * Description : delete the server: delete a listening PCB and free it * Parameters : pdeletecon -- the espconn used to delete a server * Returns : none *******************************************************************************/ sint8 ICACHE_FLASH_ATTR espconn_tcp_delete(struct espconn* pdeletecon) { err_t err; remot_info* pinfo = NULL; espconn_msg* pdelete_msg = NULL; struct tcp_pcb* pcb = NULL; if (pdeletecon == NULL) { return ESPCONN_ARG; } espconn_get_connection_info(pdeletecon, &pinfo , 0); /*make sure all the active connection have been disconnect*/ if (pdeletecon->link_cnt != 0) { return ESPCONN_INPROGRESS; } else { espconn_printf("espconn_tcp_delete %p\n", pdeletecon); pdelete_msg = pserver_list; while (pdelete_msg != NULL) { if (pdelete_msg->pespconn == pdeletecon) { /*remove the node from the client's active connection list*/ espconn_list_delete(&pserver_list, pdelete_msg); pcb = pdelete_msg->preverse; espconn_printf("espconn_tcp_delete %d, %d\n", pcb->state, pcb->local_port); espconn_kill_pcb(pcb->local_port); err = tcp_close(pcb); os_free(pdelete_msg); pdelete_msg = NULL; break; } pdelete_msg = pdelete_msg->pnext; } if (err == ERR_OK) { return err; } else { return ESPCONN_ARG; } } } #if !NO_SYS /****************************************************************************** * FunctionName : espconn_thread * Description : This thread has exclusive access to espconn task functions * (unless access to them is not locked). * Other threads communicate with this thread using message boxes. * Parameters : arg -- message boxes * Returns : none *****************************************************************************/ static sys_mutex_t EspMsgMutex = NULL; #if ESPCONN_LOCKING sys_mutex_t lock_espconn_task; #endif static sys_mbox_t EspMbox; static void ICACHE_FLASH_ATTR espconn_thread(void* arg) { espconn_msg* thread_msg = NULL; struct espconn* pespconn = NULL; LOCK_ESPCONN_TASK(); while (1) { UNLOCK_ESPCONN_TASK(); /* wait for a message, timeouts are processed while waiting */ sys_arch_mbox_fetch(&EspMbox, (void**) &thread_msg, ESPCONN_MAX_DELAY); LOCK_ESPCONN_TASK(); // printf("%s %d,message[%p]\n",__func__,__LINE__, thread_msg); ESPCONN_API_MUTEX_TAKE(); bool active_flag = false; espconn_msg* plist = NULL; /*find the active connection node*/ for (plist = plink_active; plist != NULL; plist = plist->pnext) { if (thread_msg == plist) { active_flag = true; break; } } if (active_flag) { pespconn = (thread_msg != NULL) ? thread_msg->pespconn : NULL; if (pespconn == NULL) { goto THREAD_END; } switch (pespconn->type) { case ESPCONN_TCP: if (pespconn->proto.tcp == NULL) { goto THREAD_END; } break; case ESPCONN_UDP: if (pespconn->proto.udp == NULL) { goto THREAD_END; } break; default: goto THREAD_END; break; } // printf("%s %d,sig_type[%d]\n", __func__, __LINE__, thread_msg->sig_type); switch (thread_msg->sig_type) { case SIG_ESPCONN_WRITE: if (pespconn ->proto.tcp->write_finish_fn != NULL) { espconn_connect_callback write_finish_fn = pespconn->proto.tcp->write_finish_fn; ESPCONN_API_MUTEX_GIVE(); write_finish_fn(pespconn); ESPCONN_API_MUTEX_TAKE(); } break; case SIG_ESPCONN_SEND: if (pespconn ->sent_callback != NULL) { espconn_sent_callback sent_callback = pespconn->sent_callback; ESPCONN_API_MUTEX_GIVE(); sent_callback(pespconn); ESPCONN_API_MUTEX_TAKE(); } break; case SIG_ESPCONN_ERRER: /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, thread_msg); espconn_tcp_reconnect(thread_msg); break; case SIG_ESPCONN_CLOSE: /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, thread_msg); espconn_tcp_disconnect_successful(thread_msg); break; default: break; } } THREAD_END: ESPCONN_API_MUTEX_GIVE(); // printf("%s %d\n", __func__, __LINE__); } } /****************************************************************************** * FunctionName : os_post * Description : Call the part of a call back function * This function is then running in the thread context * of espconn_thread and has exclusive access to lwIP core code. * * Parameters : apimsg -- a struct containing the function to call and its parameters * Returns : none *****************************************************************************/ static void os_post(uint8 prio, uint32 type, uint32 arg) { espconn_msg* post_msg = (espconn_msg*)arg; if (sys_mbox_valid(&EspMbox)) { post_msg->sig_type = type; sys_mbox_post(&EspMbox, post_msg); } } /****************************************************************************** * FunctionName : espconn_init * Description : Initialize espconn module: * initialize all sub modules * start the espconn_thread. * * Parameters : none * Returns : none *****************************************************************************/ void ICACHE_FLASH_ATTR espconn_init(void) { if (sys_mbox_new(&EspMbox, ESPCONN_MBOX_SIZE) != ERR_OK) { LWIP_ASSERT("failed to create espconn_thread mbox", 0); } #if ESPCONN_LOCKING if (sys_mutex_new(&lock_espconn_task) != ERR_OK) { LWIP_ASSERT("failed to create lock_espconn_task", 0); } #endif /* ESPCONN_LOCKING */ sys_mutex_new(&EspMsgMutex); if (EspMsgMutex == NULL) { printf("EspMsgMutex create fail\n"); } else { printf("EspMsgMutex created\n"); } sys_thread_new(ESPCONN_THREAD_NAME, espconn_thread, NULL, ESPCONN_THREAD_STACKSIZE, ESPCONN_THREAD_PRIO); } #else /****************************************************************************** * FunctionName : espconn_Task * Description : espconn processing task * Parameters : events -- contain the espconn processing data * Returns : none *******************************************************************************/ static void ICACHE_FLASH_ATTR espconn_Task(os_event_t* events) { espconn_msg* plist = NULL; bool active_flag = false; espconn_msg* task_msg = NULL; struct espconn* pespconn = NULL; task_msg = (espconn_msg*) events->par; /*find the active connection node*/ for (plist = plink_active; plist != NULL; plist = plist->pnext) { if (task_msg == plist) { active_flag = true; break; } } if (active_flag) { switch (events->sig) { case SIG_ESPCONN_WRITE: { pespconn = task_msg->pespconn; if (pespconn == NULL) { return; } if (pespconn->proto.tcp->write_finish_fn != NULL) { pespconn->proto.tcp->write_finish_fn(pespconn); } } break; case SIG_ESPCONN_ERRER: /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, task_msg); espconn_tcp_reconnect(task_msg); break; case SIG_ESPCONN_CLOSE: /*remove the node from the client's active connection list*/ espconn_list_delete(&plink_active, task_msg); espconn_tcp_disconnect_successful(task_msg); break; default: break; } } } /****************************************************************************** * FunctionName : espconn_init * Description : used to init the function that should be used when * Parameters : none * Returns : none *******************************************************************************/ void ICACHE_FLASH_ATTR espconn_init(void) { os_task(espconn_Task, espconn_TaskPrio, espconn_TaskQueue, espconn_TaskQueueLen); } #endif