W5500 Driver 분석

안녕하세요.

Edward입니다.

채팅프로그램에 이어서 Wiznet 사에서 배포하여 사용되는 Ethernet Driver 분석을 공유하고자 합니다.

이번은 한글버전이고 다음은 영어버전 그리고 일어 버전을 추가로 업로드 하려고 합니다.

궁금하신부분이 있는 분들은 댓글 남겨주시거나 혹은 http://wizwiki.net/forum/ 에 글을 남겨주시면 답해드리겠습니다.


//*****************************************************************************
//
//! \file socket.c
//! \brief SOCKET APIs Implements file.
//! \details SOCKET APIs like as Berkeley Socket APIs. 
//! \version 1.0.3
//! \date 2013/10/21
//! \par  Revision history
//!       <2014/05/01> V1.0.3. Refer to M20140501
//!         1. Implicit type casting -> Explicit type casting.
//!         2. replace 0x01 with PACK_REMAINED in recvfrom()
//!         3. Validation a destination ip in connect() & sendto(): 
//!            It occurs a fatal error on converting unint32 address if uint8* addr parameter is not aligned by 4byte address.
//!            Copy 4 byte addr value into temporary uint32 variable and then compares it.
//!       <2013/12/20> V1.0.2 Refer to M20131220
//!                    Remove Warning.
//!       <2013/11/04> V1.0.1 2nd Release. Refer to "20131104".
//!                    In sendto(), Add to clear timeout interrupt status (Sn_IR_TIMEOUT)
//!       <2013/10/21> 1st Release
//! \author MidnightCow
//! \copyright
//!
//! Copyright (c)  2013, WIZnet Co., LTD.
//! All rights reserved.
//! 
//! Redistribution and use in source and binary forms, with or without 
//! modification, are permitted provided that the following conditions 
//! are met: 
//! 
//!     * Redistributions of source code must retain the above copyright 
//! notice, this list of conditions and the following disclaimer. 
//!     * Redistributions in binary form must reproduce the above copyright
//! notice, this list of conditions and the following disclaimer in the
//! documentation and/or other materials provided with the distribution. 
//!     * Neither the name of the <ORGANIZATION> nor the names of its 
//! contributors may be used to endorse or promote products derived 
//! from this software without specific prior written permission. 
//! 
//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
//! THE POSSIBILITY OF SUCH DAMAGE.
//
//*****************************************************************************
#include "socket.h"

#define SOCK_ANY_PORT_NUM  0xC000;

static uint16_t sock_any_port = SOCK_ANY_PORT_NUM;
static uint16_t sock_io_mode = 0;
static uint16_t sock_is_sending = 0;
static uint16_t sock_remained_size[_WIZCHIP_SOCK_NUM_] = {0,0,};
static uint8_t  sock_pack_info[_WIZCHIP_SOCK_NUM_] = {0,};

#if _WIZCHIP_ == 5200
   static uint16_t sock_next_rd[_WIZCHIP_SOCK_NUM_] ={0,};
#endif

#define CHECK_SOCKNUM()   \
   do{                    \
      if(sn > _WIZCHIP_SOCK_NUM_) return SOCKERR_SOCKNUM;   \
   }while(0);             \

#define CHECK_SOCKMODE(mode)  \
   do{                     \
      if((getSn_MR(sn) & 0x0F) != mode) return SOCKERR_SOCKMODE;  \
   }while(0);              \

#define CHECK_SOCKINIT()   \
   do{                     \
      if((getSn_SR(sn) != SOCK_INIT)) return SOCKERR_SOCKINIT; \
   }while(0);              \

#define CHECK_SOCKDATA()   \
   do{                     \
      if(len == 0) return SOCKERR_DATALEN;   \
   }while(0);              \

----------------------------------------------------------------------------------------------

int8_t socket(uint8_t sn, uint8_t
		protocol, uint16_t port, uint8_t flag)
{
	CHECK_SOCKNUM(); // 소켓번호 체크
	switch(protocol)
	{
      case Sn_MR_TCP : // 0x01('0001')
      case Sn_MR_UDP : // 0x02('0010')
      case Sn_MR_MACRAW : // 0x04('0100')
         break;
   #if ( _WIZCHIP_ < 5200 )
      case Sn_MR_IPRAW :
      case Sn_MR_PPPoE :
         break;
   #endif
      default :
         return SOCKERR_SOCKMODE;
	}

	if((flag & 0x06) != 0) return SOCKERR_SOCKFLAG; // same not to the Flag zero(0) is return Error Flag(Invalid)
#if _WIZCHIP_ == 5200
   if(flag & 0x10) return SOCKERR_SOCKFLAG;
#endif
	//플래그 모드 Flow
	if(flag != 0)
	{
   	switch(protocol)
   	{
   	   case Sn_MR_TCP:
   	      if((flag & (SF_TCP_NODELAY|SF_IO_NONBLOCK))==0) return SOCKERR_SOCKFLAG; ///< Invalid socket flag
   	      break;
   	   case Sn_MR_UDP:
   	      if(flag & SF_IGMP_VER2)
   	      {
   	         if((flag & SF_MULTI_ENABLE)==0) return SOCKERR_SOCKFLAG;
   	      }
   	      #if _WIZCHIP_ == 5500
      	      if(flag & SF_UNI_BLOCK)
      	      {
      	         if((flag & SF_MULTI_ENABLE) == 0) return SOCKERR_SOCKFLAG;
      	      }
   	      #endif
   	      break;
   	   default:
   	      break;
   	}
   }
	close(sn); // 소켓 닫음
	//모드 레지스터 셋팅
	setSn_MR(sn, (protocol | (flag & 0xF0)));
	if(!port)
	{
	   port = sock_any_port++;
	   if(sock_any_port == 0xFFF0) sock_any_port = SOCK_ANY_PORT_NUM;
	}
   setSn_PORT(sn,port);
   setSn_CR(sn,Sn_CR_OPEN); // 커맨드 레지스터에 '소켓 오픈' 명령 
   while(getSn_CR(sn)); // 소켓 오픈 되었는지 확인
	sock_io_mode |= ((flag & SF_IO_NONBLOCK) << sn); // 0
   sock_is_sending &= ~(1<<sn); // 0
   sock_remained_size[sn] = 0; // 0
   sock_pack_info[sn] = 0; // 0
   while(getSn_SR(sn) == SOCK_CLOSED);
   return (int8_t)sn;
}

----------------------------------------------------------------------------------------------

int8_t close(uint8_t sn)
{
	CHECK_SOCKNUM();
	
	setSn_CR(sn,Sn_CR_CLOSE); // 커맨드 명령 (Host에서 W5500으로)
   /* 커맨드 프로세서 처리 기다림... */
	while( getSn_CR(sn) );
	/* 소켓의 모든 인터럽트 초기화 */
	setSn_IR(sn, 0xFF);
	sock_is_sending &= ~(1<<sn);
	sock_remained_size[sn] = 0;
	sock_pack_info[sn] = 0;
	while(getSn_SR(sn) != SOCK_CLOSED);
	return SOCK_OK;
}

----------------------------------------------------------------------------------------------

int8_t listen(uint8_t sn)
{
	CHECK_SOCKNUM(); // Socket number Check
   CHECK_SOCKMODE(Sn_MR_TCP); // 소켓이 TCP 모드인지 체크
	CHECK_SOCKINIT(); // 소켓생성 이후 Sn_SR가 SOCK_INIT상태가 되었는지 체크
	setSn_CR(sn,Sn_CR_LISTEN); // 커맨드레지스터에 '소켓 대기' 명령
	while(getSn_CR(sn)); // 소켓 대기인지 확인
	 // 에러 Flow (Sn_SR이 Listen상태가 아니면 에러)
   while(getSn_SR(sn) != SOCK_LISTEN)
   {
      if(getSn_CR(sn) == SOCK_CLOSED)
      {
         close(sn);
         return SOCKERR_SOCKCLOSED;
      }
   }
   return SOCK_OK; // Sn_IR(0x10) -> 정상동작 및 완료 되었다는 인터럽트
}

----------------------------------------------------------------------------------------------

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;
   
   // 나누어져 있는 IP를 묶어서 IP 체크
   {
   	  //IP 체크 변수
      uint32_t taddr;
      //입력될 IP들을 계산해서 변수에 담아서 체크
      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);
      //담겨있는 IP가 255.255.255.255 or 0.0.0.0 이면 에러
      if( taddr == 0xFFFFFFFF || taddr == 0) return SOCKERR_IPINVALID;
   }
  
	if(port == 0) return SOCKERR_PORTZERO; // 포트가 0이면 에러
	setSn_DIPR(sn,addr); // 접속할 서버 IP 설정
	setSn_DPORT(sn,port); // 접속할 서버 PORT 설정
   #if _WIZCHIP_ == 5200   // for W5200 ARP errata 
      setSUBR(0);
   #endif
	setSn_CR(sn,Sn_CR_CONNECT); // 커맨드레지스터에 '소켓 접속' 명령
   while(getSn_CR(sn)); // 소켓 접속 확인
   // Blocking & Nonblocking 설정 
   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);
         #if _WIZCHIP_ == 5200   // for W5200 ARP errata 
            setSUBR((uint8_t*)"\x00\x00\x00\x00");
         #endif
         return SOCKERR_TIMEOUT;
		}
	}
   #if _WIZCHIP_ == 5200   // for W5200 ARP errata 
      setSUBR((uint8_t*)"\x00\x00\x00\x00");
   #endif
   
   return SOCK_OK;
}

int8_t disconnect(uint8_t sn)
{
   CHECK_SOCKNUM();
   CHECK_SOCKMODE(Sn_MR_TCP);
	setSn_CR(sn,Sn_CR_DISCON);
	/* wait to process the command... */
	while(getSn_CR(sn));
	sock_is_sending &= ~(1<<sn);
   if(sock_io_mode & (1<<sn)) return SOCK_BUSY;
	while(getSn_SR(sn) != SOCK_CLOSED)
	{
	   if(getSn_IR(sn) & Sn_IR_TIMEOUT)
	   {
	      close(sn);
	      return SOCKERR_TIMEOUT;
	   }
	}
	return SOCK_OK;
}

----------------------------------------------------------------------------------------------

int32_t send(uint8_t sn, uint8_t * buf, uint16_t len)
{
   uint8_t tmp=0;
   uint16_t freesize=0;
   
   CHECK_SOCKNUM();
   CHECK_SOCKMODE(Sn_MR_TCP);
   CHECK_SOCKDATA(); // 데이터가 있는지 체크
   tmp = getSn_SR(sn); // 소켓 상태를 변수에 저장
	 // 저장한 변수로 2가지의 상태와 비교
   if(tmp != SOCK_ESTABLISHED && tmp != SOCK_CLOSE_WAIT) return SOCKERR_SOCKSTATUS;
   	
   // 직전 데이터 유/무 체크
   if( sock_is_sending & (1<<sn) ) // sock_is_sending 플래그 '1' 이면
   {
      tmp = getSn_IR(sn);
      if(tmp & Sn_IR_SENDOK) // 데이터 전송이 완료되었다면 SEND_OK 플래그를 '1'로 셋팅(0x10)
      {
         setSn_IR(sn, Sn_IR_SENDOK); // 인터럽트 레지스터 초기화
         #if _WIZCHIP_ == 5200
            if(getSn_TX_RD(sn) != sock_next_rd[sn])
            {
               setSn_CR(sn,Sn_CR_SEND);
               while(getSn_CR(sn));
               return SOCKERR_BUSY;
            }
         #endif
         sock_is_sending &= ~(1<<sn); // sock_is_sending 플래그 '0' 셋팅
      }
      else if(tmp & Sn_IR_TIMEOUT) // 0x08
      {
         close(sn);
         return SOCKERR_TIMEOUT;
      }
      // SEND_OK, TIME_OUT 둘 다 아닐경우 SOCK_BUSY
      else return SOCK_BUSY; // 아직 data 처리가 안되었음 (0x00)
   }
   
   // W5500은 초기설정 Default 2Kbyte, MAX 16Kbyte할 수 있다.
   // Sn_TX_FSR(Free Size Register)은 비어있는 공간을 나타낸다.
   freesize = getSn_TxMAX(sn); // TX_buffer_size 읽음
   // 사용자가 보낼 Data가 Tx_buffer보다 크면 Tx_buffer만큼만 전송
   if (len > freesize) len = freesize;
	 // 남아있는 용량 체크
   while(1)
   {
      freesize = getSn_TX_FSR(sn); // 남은 size 체크
      // 에러 Flow
      tmp = getSn_SR(sn);
      if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT)) // compare to socket n state
      {
         close(sn);
         return SOCKERR_SOCKSTATUS;
      }
      // 한번 더 에러 체크 (안전성 요구)
      if( (sock_io_mode & (1<<sn)) && (len > freesize) ) return SOCK_BUSY; // nonblocking
      // 데이터가 Tx_buffer에 들어오면 구문 탈출
      if(len <= freesize) break;
   }
   // SPI 전송
   wiz_send_data(sn, buf, len);
   
   #if _WIZCHIP_ == 5200
      sock_next_rd[sn] = getSn_TX_RD(sn) + len;
   #endif
   
   setSn_CR(sn,Sn_CR_SEND); // 갱신된 길이만큼 Data를 전송
   /* wait to process the command... */
   while(getSn_CR(sn)); // Sn_CR이 '0'으로 셋팅되는지 확인 (정상동작했는지 확인)
   sock_is_sending |= (1 << sn); // sock_is_sending 플래그 '1'로 셋팅
   return len;
}

----------------------------------------------------------------------------------------------

int32_t recv(uint8_t sn, uint8_t * buf, uint16_t len)
{
   uint8_t  tmp = 0;
   uint16_t recvsize = 0;
   CHECK_SOCKNUM();
   CHECK_SOCKMODE(Sn_MR_TCP);
   CHECK_SOCKDATA();
   
   // W5500은 초기설정(Default 2Kbyte, MAX 16Kbyte) 할 수 있다.
   // RX_buffer_size 레지스터 읽음
   recvsize = getSn_RxMAX(sn);
   // Ethernet으로 받은 Data가 초기 설정한 RX_buffer_size보다 크면, RX_buffer_size만큼만 읽는다.
   if(recvsize < len) len = recvsize;
   //Data 유/무 판별
   while(1)
   {
   	  // RX_BUF의 Size 읽음
      recvsize = getSn_RX_RSR(sn); // 'Receive Size Register'
      // 에러 Flow
      tmp = getSn_SR(sn);
      if (tmp != SOCK_ESTABLISHED)
      {
         if(tmp == SOCK_CLOSE_WAIT)
         {
            if(recvsize != 0) break; // 아직 남아있는 Data가 있다면 다시 처리
            else if(getSn_TX_FSR(sn) == getSn_TxMAX(sn))
            {
               close(sn);
               return SOCKERR_SOCKSTATUS;
            }
         }
         else // close socket
         {
            close(sn);
            return SOCKERR_SOCKSTATUS;
         }
      }
      // 다시 한번 체크
      if((sock_io_mode & (1<<sn)) && (recvsize == 0)) return SOCK_BUSY;
      if(recvsize != 0) break; // RSR로 RX buffer 길이를 읽으면 무한루프 탈출
   };
   if(recvsize < len) len = recvsize;
   wiz_recv_data(sn, buf, len); // SPI 전송
   //원래 있던 RX_RD 포인터위치에서 읽은 데이터 길이만큼 갱신된 포인터 증가를 적용
   setSn_CR(sn,Sn_CR_RECV);
   while(getSn_CR(sn)); // Sn_CR이 '0'으로 셋팅되는지 확인 (정상동작했는지 확인)
   return len;
   
}

----------------------------------------------------------------------------------------------

int32_t sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port)
{
   uint8_t tmp = 0;
   uint16_t freesize = 0;
   CHECK_SOCKNUM();
   switch(getSn_MR(sn) & 0x0F) // Mode Register 체크
   {
      case Sn_MR_UDP: // P[3:0] -> '0010'
      case Sn_MR_MACRAW: // P[3:0] -> '0100'
         break;
      default:
         return SOCKERR_SOCKMODE;
   }
   CHECK_SOCKDATA();
   //M20140501 : For avoiding fatal error on memory align mismatched
   //if(*((uint32_t*)addr) == 0) return SOCKERR_IPINVALID;
   // 나누어져 있는 IP를 묶어서 IP 체크 (Connect 참고)
   {
      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);
   }
   // IP가 0.0.0.0인지 체크 (UDP(Broadcast)가 있어 255.255.255.255는 체크 안함)
   if(*((uint32_t*)taddr) == 0) return SOCKERR_IPINVALID;  
   if(port == 0)               return SOCKERR_PORTZERO;
   //에러 흐름
   tmp = getSn_SR(sn);
   if(tmp != SOCK_MACRAW && tmp != SOCK_UDP) return SOCKERR_SOCKSTATUS;
      
   setSn_DIPR(sn,addr); // Packet을 보낼 곳의 IP 설정
   setSn_DPORT(sn,port); // Packet을 보낼 곳의 PORT 설정
   //TX_buffer_size 읽음
   freesize = getSn_TxMAX(sn); 
   if (len > freesize) len = freesize;
   //비어있는 Tx_buffer 사이즈 체크
   while(1)
   {
      freesize = getSn_TX_FSR(sn);
      // 에러 체크
      if(getSn_SR(sn) == SOCK_CLOSED) return SOCKERR_SOCKCLOSED;
      if( (sock_io_mode & (1<<sn)) && (len > freesize) ) return SOCK_BUSY;
      // Host로부터 Data가 들어오면 구문 탈출
      if(len <= freesize) break;
   };
	wiz_send_data(sn, buf, len); // SPI 전송

   #if _WIZCHIP_ == 5200   // for W5200 ARP errata 
      setSUBR(0);
   #endif
   
  // 커맨드 명령이 들어오면 TX_WR이 증가한만큼 FSR 공간이 줄어든다.
  // 즉, TX_WR과 TX_RD간의 차이로 자동 계산됨
	setSn_CR(sn,Sn_CR_SEND); 
	/* wait to process the command... */
	while(getSn_CR(sn)); // 커맨드 정상동작되었는지 확인.
   #if _WIZCHIP_ == 5200   // for W5200 ARP errata 
      setSUBR((uint8_t*)"\x00\x00\x00\x00");
   #endif
   
   // Data 전송 판별 체크
   while(1)
   {
      tmp = getSn_IR(sn);
      // UDP 이기때문에 상대방으로 부터 ACK 안옴.
      // 그래서 Data 전송 완료되었다면 Sn_IR_SENDOK 자동 Set
      // 그리고 줄어들었던 FSR의 공간이 RD가 갱신되면서 늘어남.
      if(tmp & Sn_IR_SENDOK) 
      {
         setSn_IR(sn, Sn_IR_SENDOK); // clear Sn_IR
         break;
      }
      //M:20131104
      //else if(tmp & Sn_IR_TIMEOUT) return SOCKERR_TIMEOUT;
      else if(tmp & Sn_IR_TIMEOUT)
      {
         setSn_IR(sn, Sn_IR_TIMEOUT);
         return SOCKERR_TIMEOUT;
      }
      ////////////
   }
	return len;
}

----------------------------------------------------------------------------------------------

int32_t recvfrom(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port)
{
   uint8_t  mr;

   uint8_t  head[8];
	uint16_t pack_len=0;

   CHECK_SOCKNUM();
   //CHECK_SOCKMODE(Sn_MR_UDP);
   switch((mr=getSn_MR(sn)) & 0x0F)
   {
      case Sn_MR_UDP: // '0010', 0x02
      case Sn_MR_MACRAW: // '0100', 0x04
         break;
   #if ( _WIZCHIP_ < 5200 )         
      case Sn_MR_IPRAW:
      case Sn_MR_PPPoE:
         break;
   #endif
      default:
         return SOCKERR_SOCKMODE;
   }
   CHECK_SOCKDATA();
   
   if(sock_remained_size[sn] == 0)
   {
   	  // Data 유/무 판별
      while(1)
      {
      	 // RX buffer size 읽기
         pack_len = getSn_RX_RSR(sn);
         // 에러 체크
         if(getSn_SR(sn) == SOCK_CLOSED) return SOCKERR_SOCKCLOSED;
         if( (sock_io_mode & (1<<sn)) && (pack_len == 0) ) return SOCK_BUSY;
         // Data 읽으면 구문 탈출
         if(pack_len != 0) break;
      };
   }
   sock_pack_info[sn] = PACK_COMPLETED; // Data 읽기 완료
   
	switch (mr & 0x07)
	{
	   case Sn_MR_UDP :
	      if(sock_remained_size[sn] == 0)
	      {
   			wiz_recv_data(sn, head, 8); // UDP로 받은 패킷 중 8Byte만 읽음
   			setSn_CR(sn,Sn_CR_RECV);
   			while(getSn_CR(sn));
   			// read peer's IP address, port number & packet length
    		// IP 주소
    		addr[0] = head[0];
   			addr[1] = head[1];
   			addr[2] = head[2];
   			addr[3] = head[3];
   			// 포트 번호
   			*port = head[4];
   			*port = (*port << 8) + head[5];
   			// 전송될 Data 패킷 길이
   			sock_remained_size[sn] = head[6];
   			sock_remained_size[sn] = (sock_remained_size[sn] << 8) + head[7];
   			// UDP로 전체 패킷(Data)를 받아온 뒤 정해진 Header(8byte)를 따로 가져온다.
   			// 이 상태일때 info Flag는 아래의 변수로 선언
   			sock_pack_info[sn] = PACK_FIRST; // 0x80
   	   }
   	  // UDP로 받은 패킷길이가 사용자가 설정한 버퍼 길이보다 크면 Rx buffer 사이즈 만큼만 받음
			if(len < sock_remained_size[sn]) pack_len = len;
			// 아니라면, 받은 패킷 길이만큼 Data 받음
			else pack_len = sock_remained_size[sn];
			//
			// Need to packet length check (default 1472)
			//
			//pack_len이 실제 data
   		wiz_recv_data(sn, buf, pack_len); // data copy.
			break;
			
	   case Sn_MR_MACRAW :
	      if(sock_remained_size[sn] == 0)
	      {
   			wiz_recv_data(sn, head, 2);
   			setSn_CR(sn,Sn_CR_RECV);
   			while(getSn_CR(sn));
   			// read peer's IP address, port number & packet length
    			sock_remained_size[sn] = head[0];
   			sock_remained_size[sn] = (sock_remained_size[sn] <<8) + head[1];
   			if(sock_remained_size[sn] > 1514) 
   			{
   			   close(sn);
   			   return SOCKFATAL_PACKLEN;
   			}
   			sock_pack_info[sn] = PACK_FIRST;
   	   }
			if(len < sock_remained_size[sn]) pack_len = len;
			else pack_len = sock_remained_size[sn];
			wiz_recv_data(sn,buf,pack_len);
		   break;
   #if ( _WIZCHIP_ < 5200 )
		case Sn_MR_IPRAW:
		   if(sock_remained_size[sn] == 0)
		   {
   			wiz_recv_data(sn, head, 6);
   			setSn_CR(sn,Sn_CR_RECV);
   			while(getSn_CR(sn));
   			addr[0] = head[0];
   			addr[1] = head[1];
   			addr[2] = head[2];
   			addr[3] = head[3];
   			sock_remained_size[sn] = head[4];
   			sock_remaiend_size[sn] = (sock_remained_size[sn] << 8) + head[5];
   			sock_pack_info[sn] = PACK_FIRST;
         }
			//
			// Need to packet length check
			//
			if(len < sock_remained_size[sn]) pack_len = len;
			else pack_len = sock_remained_size[sn];
   		wiz_recv_data(sn, buf, pack_len); // data copy.
			break;
   #endif
      default:
      	 // 패킷 받음 (RX_buffer 포인터 증가)
         wiz_recv_ignore(sn, pack_len); // data copy.
         // 전체 패킷을 remained 변수에 넣어 다시 한번 루프 돌 때 읽음
         sock_remained_size[sn] = pack_len;
         break;
   }
	setSn_CR(sn,Sn_CR_RECV);
	/* wait to process the command... */
	while(getSn_CR(sn)) ;
	// 만약 받은 패킷이 사용자가 설정한 버퍼사이즈보다 크면 그 차만큼 뺀다.
	// 그리고 sock_remained_size에 남은 패킷사이즈를 저장한다.
	sock_remained_size[sn] -= pack_len;
	//M20140501 : replace 0x01 with PACK_REMAINED
	//if(sock_remained_size[sn] != 0) sock_pack_info[sn] |= 0x01;
	// 패킷이 아직 W5500 버퍼에 남아있으면 info 변수를 REMAINED로 설정
	// 한번 더 반복해서 나머지 패킷데이터도 Host로 보낸다.
	if(sock_remained_size[sn] != 0) sock_pack_info[sn] |= PACK_REMAINED; //0x01
   //
 	return pack_len; // 실제 data 패킷만 Host로 보낸다.
}

----------------------------------------------------------------------------------------------

// SPI ADDRESS + BSB + R/W , OP(VDM, FDM) 할당
void wiz_send_data(uint8_t sn, uint8_t *wizdata, uint16_t len)
{
   uint16_t ptr = 0;
   uint32_t addrsel = 0;
   if(len == 0)  return;
   // Host는 전송할 Data를 저장할 시작 Address인 이 값을 읽는다.
   ptr = getSn_TX_WR(sn); // Tx buffer의 쓰기 포인터
   //M20140501 : implict type casting -> explict type casting
   //addrsel = ((ptr << 8) + (WIZCHIP_RXBUF_BLOCK(sn) << 3);
   // ADDRESS[15:8] + BSB[7:3] 할당
   // 0b 0000:0000:0000:0XXX
   addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_TXBUF_BLOCK(sn) << 3);
   // Host는 이 값을 시작 Address하여 data를 Socket n TX Buffer에 저장한다.
   WIZCHIP_WRITE_BUF(addrsel,wizdata, len);
   
   //원래있던 TX_WR포인터위치에서 버퍼에 저장한 data의 사이즈만큼 합하여 TX_WR의 위치값을 갱신한다.
   ptr += len;
   setSn_TX_WR(sn,ptr); // 포인터 갱신
}

----------------------------------------------------------------------------------------------

// SPI ADDRESS + BSB + R/W , OP(VDM, FDM) 할당
void wiz_recv_data(uint8_t sn, uint8_t *wizdata, uint16_t len)
{
   uint16_t ptr = 0;
   uint32_t addrsel = 0;
   
   if(len == 0) return;
   //Host는 수신한 Data의 시작 Address인 이 값을 읽는다.
   ptr = getSn_RX_RD(sn); // RX buffer의 읽기 포인터
   //M20140501 : implict type casting -> explict type casting
   //addrsel = ((ptr << 8) + (WIZCHIP_RXBUF_BLOCK(sn) << 3);
	 // ADDRESS[15:8] + BSB[7:3] 할당
   // 0b 0000:0000:0000:0XXX
   addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_RXBUF_BLOCK(sn) << 3);
   //Host는 이 시작 Address부터 data를 읽는다.
   WIZCHIP_READ_BUF(addrsel, wizdata, len);
   
   //원래있던 RX_RD 포인터위치에서 읽은 데이터 길이만큼 포인터 증가
   ptr += len;
   setSn_RX_RD(sn,ptr); // 증가 후 포인터 갱신
}


void wiz_recv_ignore(uint8_t sn, uint16_t len)
{
   uint16_t ptr = 0;
   ptr = getSn_RX_RD(sn);
   ptr += len;
   setSn_RX_RD(sn,ptr);
}


----------------------------------------------------------------------------------------------


void     WIZCHIP_WRITE_BUF(uint32_t AddrSel, uint8_t* pBuf, uint16_t len)
{
   uint16_t i = 0;
   uint16_t j = 0;
   WIZCHIP_CRITICAL_ENTER();
   WIZCHIP.CS._select(); // CS가 Low일 때 -> SPI 전송 시작

#if( (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SPI_)) // SPI 인터페이스 모드 체크

   #if  ( _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_ ) // VDM 모드 체크
      // R/W, OP(VDM,FDM) 할당
      // XXXX:XXXX:XXXX:X000
      AddrSel |= (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_); // 1 write mode(MOSI)
      WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16); // Address 상위 1byte 전송
      WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >>  8); // Address 하위 1byte 전송
      WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >>  0); // Control phase 1byte 전송(BSB + R/W + OP)
      // 사용자가 설정한 버퍼에 사용자가 입력한 길이 만큼 버퍼에 쓰기
      for(i = 0; i < len; i++,j) 
         WIZCHIP.IF.SPI._write_byte(pBuf[i]);


----------------------------------------------------------------------------------------------


void     WIZCHIP_READ_BUF (uint32_t AddrSel, uint8_t* pBuf, uint16_t len)
{
   uint16_t i = 0;
   uint16_t j = 0;
   WIZCHIP_CRITICAL_ENTER();
   WIZCHIP.CS._select(); // CS가 Low일 때 -> SPI 전송 시작

#if( (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SPI_)) // SPI 인터페이스 모드 체크

   #if  ( _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_ ) // VDM 모드 체크
      // R/W, OP(VDM,FDM) 할당
      // XXXX:XXXX:XXXX:X000
      AddrSel |= (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_); // 0 read mode(MISO)
      WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16); // Address 상위 1byte 전송
      WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >>  8); // Address 하위 1byte 전송
      WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >>  0); // Control phase 1byte 전송(BSB + R/W + OP)
      // 사용자가 설정한 버퍼에 Ethernet으로 부터 받은 길이 만큼 버퍼에서 읽음
      for(i = 0; i < len; i++,j)
        pBuf[i] = WIZCHIP.IF.SPI._read_byte(); // 1byte read

Advertisements

W5500 Driver 분석”에 대한 1개의 생각

  1. 이정민

    recvfrom() 함수 내에 wiz_recv_data(sn, head, 8); // UDP로 받은 패킷 중 8Byte만 읽음 <— 이 부분 관련해서 질문드립니다.
    W5500 해당 소켓의 Rx Buf 내 데이터를 가져 오는데… 어떻게 IP addr, Port addr, length 정보를 얻을 수 있죠?
    UDP protocol 내의 Data 부분만 Rx Buf 에 저장되는 것 아니였나요?
    TCP 같은 경우는 TCP protocol 내의 Data 부분만 저장되는 것 같은데…
    제가 뭔가 잘못 이해하고 있는것인지… 여쭤봅니다.

    좋아요

    응답

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중