connect 시 Sn_SR 값이 0x10 이 되는 경우 문의

안녕하세요.

STM32 FreeRTOS 상에서 w5100 을 테스트하고 있습니다.
connect 시에 빈번히 에러가 발생하여 Debugging 중에 있습니다.

connect 에러 값은 -4 를 반환하는데 이때 이상한 것이 반환하는 Sn_SR 값이 0x10 으로
정의되지 않은 상태입니다.

이 때 동작을 wireshark 로 보면,
w5100 과 pc 가 정상적으로 SYN, SYN+ACK, ACK 를 수행하는데 w5100 이 ACK 를 보냈음에도
connect 가 에러를 return 하여 다시 접속을 시도하는 현상이 보입니다.

192.168.1.180 이 w5100 이고 192.168.1.227 이 pc 입니다.

사용한 코드는 아래와 같습니다.

while (1) {
    		int32_t ret;

    		//create socket
    		ret = socket(CLIENT_SOCKET, Sn_MR_TCP, localPort++, 0);
    		if(localPort == 0xFFFF) {
    			localPort = 50000;
    		}

    		if (ret < 0) {
    			HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
    			printf("socket failed{%ld}.\n", ret);
    			close(CLIENT_SOCKET);
    			osDelay(500);
    			continue;
    		}

    		//connect to the server
    		ret = connect(CLIENT_SOCKET, serverIP, SERVER_PORT);
    		if (ret < 0) {
    			HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
    			printf("connect failed{%ld}.Sn_SR()={%d}\n", ret, getSn_SR(CLIENT_SOCKET));
    			osDelay(500);
    			continue;
    		}
...
}

socket() 함수와 connect() 함수 사이의 delay 가 현상을 줄여주기는 합니다만 완벽하게 없어지지는 않습니다.

//create socket
ret = socket(CLIENT_SOCKET, Sn_MR_TCP, localPort++, 0);
if(localPort == 0xFFFF) {
	localPort = 50000;
}

if (ret < 0) {
	HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
	printf("socket failed{%ld}.\n", ret);
	close(CLIENT_SOCKET);
	osDelay(500);
	continue;
}

osDelay(20);

//connect to the server
ret = connect(CLIENT_SOCKET, serverIP, SERVER_PORT);
if (ret < 0) {
	HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
	printf("connect failed{%ld}.Sn_SR()={%d}\n", ret, getSn_SR(CLIENT_SOCKET));
	close(CLIENT_SOCKET);
	osDelay(500);
	continue;
}

혹시 유사한 케이스에 대한 답변이 있을 것 같았는데 없는 상황이어서 제가 수정한 내용만 혹시 동일한 문제가 발생하시는 분들을 위해서 남겨 놓습니다.

제가 우회한 방법은 다음과 같습니다.

  1. 윗 부분에 적은 대로 socket() 과 connect() 사이의 delay 는 궁극적인 해결이 되지 못했습니다.

  2. connect 시에 return 값으로 SOCKERR_SOCKCLOSED 를 받으며 그 때 Sn_SR 값을 확인하면 0x10 이었지만 일정 시간이 지나면 Sn_SR 값이 0x17 로 변경되는 것을 확인하였습니다.

socket.c 파일의 connect 함수 내부를 보면 setSn_CR(sn, Sn_CR_CONNECT); 로 CONNECT 명령을 전송한 후 Sn_SR 의 상태가 SOCK_ESTABLISHED 가 아닐 때 바로 에러 처리를 하는데 이 부분의 코드를 아래와 같이 재확인을 하도록 수정하였습니다.

int8_t connect(uint8_t sn, uint8_t * addr, uint16_t port)
{
   CHECK_SOCKNUM();
   CHECK_SOCKMODE(Sn_MR_TCP);
   CHECK_SOCKINIT();
   //M20140501 : For avoiding fatal error on memory align mismatched
   //if( *((uint32_t*)addr) == 0xFFFFFFFF || *((uint32_t*)addr) == 0) return SOCKERR_IPINVALID;
   {
      uint32_t taddr;
      taddr = ((uint32_t)addr[0] & 0x000000FF);
      taddr = (taddr << 8) + ((uint32_t)addr[1] & 0x000000FF);
      taddr = (taddr << 8) + ((uint32_t)addr[2] & 0x000000FF);
      taddr = (taddr << 8) + ((uint32_t)addr[3] & 0x000000FF);
      if( taddr == 0xFFFFFFFF || taddr == 0) return SOCKERR_IPINVALID;
   }
   //
	
	if(port == 0) return SOCKERR_PORTZERO;
	setSn_DIPR(sn,addr);
	setSn_DPORT(sn,port);
	setSn_CR(sn,Sn_CR_CONNECT);
   while(getSn_CR(sn));
   if(sock_io_mode & (1<<sn)) return SOCK_BUSY;
   while(getSn_SR(sn) != SOCK_ESTABLISHED)
   {
		if (getSn_IR(sn) & Sn_IR_TIMEOUT)
		{
			setSn_IR(sn, Sn_IR_TIMEOUT);
            return SOCKERR_TIMEOUT;
		}

		//2020.06.09 eziya76, 때때로 Sn_SR 이  SOCK_ESTABLISHED 로 천천히 변하는 현상이 보임
		//따라서 CONNECT 요청 후 getSn_SR 이 SOCK_ESTABLISHED 가 아닌 경우에는 여러번 확인하고 CLOSE 처리
		uint16_t retry = 0;
		if (getSn_SR(sn) == SOCK_CLOSED)
		{
			retry++;
			if(retry >= 100)
			{
				return SOCKERR_SOCKCLOSED;
			}
		}
	}
   
   return SOCK_OK;
}
  1. close 시에도 유사하게 setSn_CR(sn, Sn_CR_CLOSE); 로 CLOSE 명령을 전송한 이후에 Sn_SR 이 SOCK_CLOSED 가 아니면 무한히 대기하도록 되어 있는데 정확히는 판단할 수 없지만 setSn_CR 로 명령을 전송한 이후에 다른 상태 조건에 의해서 Sn_SR 이 SOCK_CLOSED 가 되지 않고 SOCK_ESTABLISHED를 유지하는 경우에 무한루프에 빠지기 때문에 이때는 다시 CLOSE 명령을 전송하도록 아래와 같이 변경하였습니다.

    int8_t close(uint8_t sn)
    {
    CHECK_SOCKNUM();

     setSn_CR(sn,Sn_CR_CLOSE);
    /* wait to process the command... */
     while( getSn_CR(sn) );
     /* clear all interrupt of the socket. */
     setSn_IR(sn, 0xFF);
     //A20150401 : Release the sock_io_mode of socket n.
     sock_io_mode &= ~(1<<sn);
     //
     sock_is_sending &= ~(1<<sn);
     sock_remained_size[sn] = 0;
     sock_pack_info[sn] = 0;
     while(getSn_SR(sn) != SOCK_CLOSED)
     {
     	//2020.06.08 eziya, getSn_SR sometimes returns SOCK_ESTABLISHED
     	setSn_CR(sn,Sn_CR_CLOSE);
     	while( getSn_CR(sn) );
     }
     return SOCK_OK;
    

    }

제 환경에만 적용될지 모르는 우회 방법일지 모르겠지만 유사 문제를 접하시는 분들을 위해서 남겨둡니다.

안녕하세요

답변이 늦어 죄송합니다.
우선 의문인 점이 SOCKERR_SOCKCLOSED 를 Return 받았다고 하셨는데, Sn_SR값은 0x10인 점이 이상합니다.
socket()함수이후 Sn_SR은 SOCK_INIT 상태였다가, CONNECT Command 이후 CONNECT가 될경우 SOCK_ESTABLISHED, 실패할경우 SOCK_CLOSED 상태가 됩니다. 말씀하신 0x10은 Connection 과정중에 있는 Status입니다
말씀하신 상황으로는 Status가
SOCK_INIT → SOCK_CLOSED(Connect에 대한 return값 SOCKERR_SOCKCLOSED 가 반환되기때문에) → 0x10 → SOCK_ESTABLISHED 라는건데 이해되지 않는 상황입니다.
유사한 케이스는 파악되지않았습니다.

1 Like

FREERTOS 사용하셨다고 하셔서 몇가지 질문이 있습니다.

  1. 어떤 모드를 사용하고 계시는지? address mode or SPI mode?
  2. 다른 task에서 address 나 SPI에 접근하시는지?
  3. Critical section 설정을 하셨는지?

결론은 critical section을 반드시 설정하셔야 합니다.
특히 SPI로 사용하실 때 다른 task에서도 해당 spi에 접근하게 되면 W5100 spi 시퀀스가 깨질 확률이 대단히 높습니다. 이 때 원인을 알 수 없는 오류가 발생해 많은 분들이 고생하십니다.
참고하세요.

감사합니다.

1 Like