cancel
Showing results for 
Search instead for 
Did you mean: 

Ethernet close connection and restart MCU

Pilous Droip
Senior

Hi,

I have STM32F767 - Nucleo kit. I implement FreeRTOS, lwIP. And I create this connect.

struct tcp_pcb *server_pcb;
 
struct server_struct
{
	uint8_t state; 			/* current connection state */
	uint8_t retries;
	struct tcp_pcb *pcb; 	/* pointer on the current tcp_pcb */
	struct pbuf *p; 		/* pointer on the received/to be transmitted pbuf */
};
 
struct server_struct *ss;
 
enum server_states
{
	ES_NONE = 0,
	ES_ACCEPTED,
	ES_RECEIVED,
	ES_CLOSING
};
 
static void tcp_server_send(struct tcp_pcb *tpcb, struct server_struct *es)
{
	struct pbuf *ptr;
	err_t wr_err = ERR_OK;
	while ((wr_err == ERR_OK) && (es->p != NULL) && (es->p->len <= tcp_sndbuf(tpcb)))
	{
		ptr = es->p;
		wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
		if (wr_err == ERR_OK)
		{
			uint16_t plen;
			uint8_t freed;
			plen = ptr->len;
			es->p = ptr->next;
			if(es->p != NULL)
			{
				pbuf_ref(es->p);
			}
			do
			{
				freed = pbuf_free(ptr);
			}
			while(freed == 0);
			tcp_recved(tpcb, plen);
		}
		else if(wr_err == ERR_MEM)
		{
			es->p = ptr;
		}
		else
		{
			//other problem
		}
	}
}
 
static void tcp_server_connection_close(struct tcp_pcb *tpcb, struct server_struct *es)
{
	// remove all callbacks
	tcp_arg(tpcb, NULL);
	tcp_sent(tpcb, NULL);
	tcp_recv(tpcb, NULL);
	tcp_err(tpcb, NULL);
	tcp_poll(tpcb, NULL, 1);
	if (es != NULL)
	{
		mem_free(es);
	}
	tcp_close(tpcb);
}
 
static void tcp_server_error(void *arg, err_t err)
{
	//-TODO-
}
 
static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb)
{
	err_t ret_err;
	struct server_struct *es;
	es = (struct server_struct *)arg;
 
	if (es != NULL)
	{
		if (es->p != NULL)
		{
		}
		else
		{
			if(es->state == ES_CLOSING)
		    {
				tcp_server_connection_close(tpcb, es);
		    }
		}
		ret_err = ERR_OK;
	}
	else
	{
		tcp_abort(tpcb);
		ret_err = ERR_ABRT;
	}
	return ret_err;
}
 
static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len)
{
	struct server_struct *es;
	LWIP_UNUSED_ARG(len);
	es = (struct server_struct *)arg;
	es->retries = 0;
	if(es->p != NULL)
	{
		tcp_server_send(tpcb, es);
	}
	else
	{
		if(es->state == ES_CLOSING)
		{
			tcp_server_connection_close(tpcb, es);
		}
	}
	return ERR_OK;
}
 
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
	char str1[100];
	struct server_struct *es;
	err_t ret_err;
	LWIP_ASSERT("arg != NULL",arg != NULL);
	es = (struct server_struct *)arg;
 
	if (p == NULL)
	{
		es->state = ES_CLOSING;
		if(es->p == NULL)
		{
			tcp_recved(tpcb, p->tot_len);
		}
		else
		{
			//acknowledge received packet
			tcp_sent(tpcb, tcp_server_sent);
			//send remaining data
			tcp_server_send(tpcb, es);
		}
		ret_err = ERR_OK;
	}
	else if(err != ERR_OK)
	{
		if (p != NULL)
		{
			es->p = NULL;
			pbuf_free(p);
		}
		ret_err = err;
	}
	else if(es->state == ES_ACCEPTED)
	{
		tcp_recved(tpcb, p->tot_len);
		strncpy(str1,p->payload,p->len);
		str1[p->len] = '\0';
/* -- Send data to Serial PORT */
		PrintSerial(str1);
		ret_err = ERR_OK;
	}
	else if (es->state == ES_RECEIVED)
	{
		if(es->p == NULL)
		{
			ret_err = ERR_OK;
		}
		else
		{
			struct pbuf *ptr;
			ptr = es->p;
			pbuf_chain(ptr,p);
		}
		ret_err = ERR_OK;
	}
	else if(es->state == ES_CLOSING)
	{
		tcp_recved(tpcb, p->tot_len);
		es->p = NULL;
		pbuf_free(p);
		ret_err = ERR_OK;
	}
	else
	{
		tcp_recved(tpcb, p->tot_len);
		es->p = NULL;
		pbuf_free(p);
		ret_err = ERR_OK;
	}
	return ret_err;
}
 
static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
	err_t ret_err;
	struct server_struct *es;
	LWIP_UNUSED_ARG(arg);
	LWIP_UNUSED_ARG(err);
	tcp_setprio(newpcb, TCP_PRIO_MIN);
	es = (struct server_struct *)mem_malloc(sizeof(struct server_struct));
	ss = (struct server_struct *)mem_malloc(sizeof(struct server_struct));
	if (es != NULL)
	{
		es->state = ES_ACCEPTED;
		es->pcb = newpcb;
	    ss->pcb = newpcb;
	    es->retries = 0;
	    es->p = NULL;
	    tcp_arg(newpcb, es);
	    tcp_recv(newpcb, tcp_server_recv);
	    tcp_err(newpcb, tcp_server_error);
	    tcp_sent(newpcb, tcp_server_sent);
	    tcp_poll(newpcb, tcp_server_poll, 1);
	    ret_err = ERR_OK;
	}
	else
	{
		tcp_server_connection_close(newpcb, es);
		ret_err = ERR_MEM;
	}
	return ret_err;
}
 
 
void Hello(void *pvParameters)
{
	ip_addr_t ipaddr;
	IP4_ADDR(&ipaddr,  192, 168,   11, 208);
 
	server_pcb = tcp_new();
	if (server_pcb != NULL)
	{
		err_t err;
		err = tcp_bind(server_pcb, IP_ADDR_ANY, 502);
		if (err == ERR_OK)
		{
			server_pcb = tcp_listen(server_pcb);
			tcp_accept(server_pcb, tcp_server_accept);
		}
		else
		{
			memp_free(MEMP_TCP_PCB, server_pcb);
		}
	}
 
 	while(1)
 	{
 		vTaskDelay(100);
 	}
}
 

In main function I create a task:

xTaskCreate( Hello, "Hello", 4096, ( void * ) NULL, 2, NULL );

And program works (when I send anything over TCP i see it in serial output). Problem is when I try disconnect. MCU is restart.

Any idea, what is wrong?

I always get this massage:

HardFault Exception 

Here is output:

  • >>>> HardFault Exception <<<<

  • Fault registers:
  • SCB->HFSR = 0x40000000
  •  DEBUGEVT = 0
  •  FORCED  = 1
  •  VECTTBL  = 0
  • SCB->CFSR = 0x00010000
  •  IACCVIOL = 0
  •  DACCVIOL = 0
  •  MUNSTKERR = 0
  •  MSTKERR  = 0
  •  MLSPERR  = 0
  •  MMARVALID = 0
  •  IBUSERR  = 0
  •  PRECISE E = 0
  •  IMPRECISE = 0
  •  UNSTKERR = 0
  •  STKERR  = 0
  •  LSPERR  = 0
  •  BFARVALID = 0
  •  UNDEFINSTR= 1
  •  INVSTATE = 0
  •  INVPC   = 0
  •  NOCP   = 0
  •  UNALIGNED = 0
  •  DIVBYZERO = 0
  • SCB->DFSR = 0x00000000
  • SCB->MMFAR = 0x00000000
  • SCB->BFAR = 0x00000000
  • SCB->AFSR = 0x00000000
  • SCB->ABFSR = 0x00000000

  • Exception stack frame:
  • R0  0x20045c98
  • R1  0x20068d8c
  • R2  0x00000003
  • R3  0x00009a90
  • R12  0x20068a54
  • LR  0x08007e87
  • PC  0x0800fd5c
  • xPSR 0x61000000

  •  >>> forcing SYSTEM RESET
10 REPLIES 10
Piranha
Chief II

Don't do locking in callback functions! Also LWIP_TCPIP_CORE_LOCKING_INPUT is not required - it is optional and should be enabled when You know that You need it. LWIP_TCPIP_CORE_LOCKING is the one required with RTOS and SYS_LIGHTWEIGHT_PROT very desirable. Defaults in file opt.h are good in this regard.

Now I looked on Your code with more attention. By the way, tcp_recved() is the data acknowledge function, not tcp_sent() - wrong comment on line 142. And tcp_sent() only sets up callback function pointer so it's enough to do it once in tcp_server_accept() - line 143 is needless. Lines 181-187 are also needless, because the "else" section at line 188 does exactly the same. But the main problem is related to connection closing code...

https://www.nongnu.org/lwip/2_1_x/group__tcp__raw.html#ga8afd0b316a87a5eeff4726dc95006ed0

"The callback function will be passed a NULL pbuf to indicate that the remote host has closed the connection."

https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a780cfac08b02c66948ab94ea974202e8

"p The received data (or NULL when the connection has been closed!)"

So, when tcp_server_recv() is called with p == NULL, You should close the connection on Your side with tcp_close() and return ERR_OK. At that point You can't acknowledge or send data on it anymore. And it doesn't make sense anyway, because the actual network connection has already been closed. Actually everything related to ES_CLOSING seems to be broken and tcp_server_poll() is needless altogether. You should rework that connection closing logic, but start with lines 136-146.

Additionally I suggest setting up LWIP_PLATFORM_ASSERT() in such a way that it prints out (for example to ST-LINK's virtual COM port) the message, disables interrupts and goes into infinite loop, from which You can break out by changing a loop variable with a debugger. After that loop interrupts must be enabled again for debugging purposes. Actually such a function is useful for all code in a firmware, not only lwIP... =)