혹시 유사한 케이스에 대한 답변이 있을 것 같았는데 없는 상황이어서 제가 수정한 내용만 혹시 동일한 문제가 발생하시는 분들을 위해서 남겨 놓습니다.
제가 우회한 방법은 다음과 같습니다.
윗 부분에 적은 대로 socket() 과 connect() 사이의 delay 는 궁극적인 해결이 되지 못했습니다.
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;
}
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 라는건데 이해되지 않는 상황입니다.
유사한 케이스는 파악되지않았습니다.
결론은 critical section을 반드시 설정하셔야 합니다.
특히 SPI로 사용하실 때 다른 task에서도 해당 spi에 접근하게 되면 W5100 spi 시퀀스가 깨질 확률이 대단히 높습니다. 이 때 원인을 알 수 없는 오류가 발생해 많은 분들이 고생하십니다.
참고하세요.