2018-12-18 02:46 AM
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:
2018-12-18 03:36 AM
Tried to decode the SCB register contents ?
Here is a document for reference: www.keil.com/appnotes/files/apnt209.pdf
2018-12-18 04:16 AM
I think this is the answer to your question?
Or what specifically should I look for?
2018-12-18 05:07 AM
I looked into disassembly. But I don't see a problem.
xTaskCreate( BlinkLED, "BlinkLED", 4096, ( void * ) NULL, 2, NULL );
0800f8ce: mov r3, r4
0800f8d0: mov.w r2, #4096 ; 0x1000
0800f8d4: ldr r1, [pc, #20] ; (0x800f8ec <NUCLEO_CB_SetupMyTasks+56>)
0800f8d6: str r4, [sp, #4]
0800f8d8: str r5, [sp, #0]
0800f8da: ldr r0, [pc, #20] ; (0x800f8f0 <NUCLEO_CB_SetupMyTasks+60>)
0800f8dc: bl 0x800236c <xTaskCreate>
146 }
0800f8e0: add sp, #12
0800f8e2: pop {r4, r5, pc}
0800f8e4: strb r4, [r5, r4]
0800f8e6: lsrs r1, r0, #32
0800f8e8: ldrb.w r0, [r5, <undefined>]
0800f8ec: strb r4, [r6, r4]
0800f8ee: lsrs r1, r0, #32
0800f8f0: ; <UNDEFINED> instruction: 0xf7990800
NMI_Handler:
0800f8f4: bkpt 0x0000
0800f8f6: b.n 0x800f8f6 <NMI_Handler+2>
88 {
HardFault_InfoDump:
0800f8f8: stmdb sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}
0800f8fc: mov r1, r0
102 uint32_t hfsr = SCB->HFSR;
0800f8fe: ldr r5, [pc, #452] ; (0x800fac4 <HardFault_InfoDump+460>)
0800f900: sub sp, #36 ; 0x24
110 hfprintf("\n\n\n>>>> HardFault Exception <<<<\n");
2018-12-18 05:16 AM
Did you check the call stack in the toolchain's debugger ?
Or could you identify the C/Assembler intruction causing the fault ?
Reproduction should be easy if I understood correctly.
Perhaps an insufficient stack (task stack) size, or the mem_free() gets haywire ?
2018-12-18 02:56 PM
Maybe this is not the main problem, but You have made probably the most popular lwIP+RTOS related mistake in the world - "raw" APIs are not thread-safe!
https://www.nongnu.org/lwip/2_1_x/multithreading.html
http://lwip.100.n7.nabble.com/lwip-2-0-2-freertos-ARM7-td29599.html
From the second link: "If you use threads, you may want to use netconn or socket API if that's easier for you. You still can use the callback API, but you need to use tcpip_callback() or LOCK_TCPIP_CORE (if you configured lwIP to use core locking) to sync with lwIP thread."
This ir true also for netif_***(), dhcp_***() and other callback style APIs. The only exceptions are pbuf and memory alloc/free functions when SYS_LIGHTWEIGHT_PROT is enabled (enabled by default) - these are thread-safe. Also NETIF, DHCP and AUTOIP functions have thread-safe netifapi_***() alternatives.
Connection callbacks by default are called from lwIP core "tcpip_thread" and, if You enable LWIP_TCPIP_CORE_LOCKING_INPUT (disabled by default), then from the thread in which You call netif->input(). In both cases You must not use tcpip_callback() or LOCK_TCPIP_CORE() in callbacks.
If You don't do this right, then the firmware will always seem to work but fail occasionally. My suggested solution is to put LOCK_TCPIP_CORE() in line 234 and UNLOCK_TCPIP_CORE() in line 250. Test it and report if anything changes.
2018-12-18 03:08 PM
Ok, and what is the reported hard fault status related to this code?
Can't relate the prior reported PC to anything here.
Need to show the 5 instructions either side of the faulting instruction to get some minimal context.
2018-12-18 09:51 PM
I enable core locking input in lwipopts.h
#define LWIP_TCPIP_CORE_LOCKING_INPUT (1)
And modificate my hello function:
void Hello(void *pvParameters)
{
ip_addr_t ipaddr;
IP4_ADDR(&ipaddr, 192, 168, 11, 208);
LOCK_TCPIP_CORE();
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);
}
}
UNLOCK_TCPIP_CORE();
while(1)
{
vTaskDelay(100);
}
}
And problem is the same.
>>>> 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 0x20045c7c
R1 0x2006896c
R2 0x00000003
R3 0x00009a90
R12 0x20068634
LR 0x08007e87
PC 0x0800fd48
xPSR 0x61000000
>>> forcing SYSTEM RESET
2018-12-18 11:48 PM
I try debug.
When I send close connection, I go here:
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;
}
And when I get here (line 6):
tcp_recved(tpcb, p->tot_len);
Program crash
__attribute((naked))
void HardFault_Handler(void)
{
/* Determine MSP/PSP usage and pass the value as first argument
* to get the stack frame dump */
__asm volatile (
"tst lr, #4 \n"
"ite eq \n"
"mrseq r0, msp \n"
"mrsne r0, psp \n"
"bl HardFault_InfoDump");
}
And program is restart.
2018-12-19 12:11 AM
So I modificate my receive funciton, and now I close connection and program don't crash.
if (p == NULL)
{
LOCK_TCPIP_CORE();
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);
}
UNLOCK_TCPIP_CORE();
ret_err = ERR_OK;
}
But connection is bad closed.