LINK LAYER

자~ 이제 Physical Layer 에서 한단계 위로 올라가 보자! 아직 휴가의 후유증이 남아 있긴 하지만…
앞에서 얘기했듯이 POD 의 동작을 설명하자면, 각각의 계층마다 하는 역할들이 나뉘어져 있다. 마치 네트워크의 TCP/IP 를 보는 것처럼 말이다.
이러한 각각의 계층으로 나뉘어져 있으면, 프로그램 하기가 용이하다. 각각의 계층마다 모듈 또는 태스크로 만들면 되니 말이다. 여기서는 편하게 Link Layer 를 '링크 레이어'로 부르겠다.

물리 계층의 가장 마지막 수행이 무엇이었는지 생각나는가? 바로 Data channel 과 extend channel 의 buernegotiation 이었다. 각각의 채널의 주고 받을 버퍼 사이즈를 조율하는 과정이다. 바로 이것이 가장 중요한 부분이다. 이후 부터는 각각의 레이어들을 동작시킨다.

layerLinklayerStart();
layerTransportlayerStart();	
layerSessionlayerStart();		
layerResourcelayerStart();	

여기서 가장 먼저 수행되는 링크 레이어의 수행동작에 대해서 살펴보기로 하자! 우선 들어가기 전에 어떤 일을 하는지 알아볼 필요가 있다.

  1. POD 로 보내려는 또는 받으려는 데이터의 크기가 앞에서 약속한 크기보다 큰지 작은지 판단해서, 분할하는 일을 한다.
  2. 이 때, 만일 분할하면, 분할된 데이터라는 bit 를 설정하여 보낸다.

두 번째를 좀더 자세히 알아보자!

/* =========================================================================
    Required Function Decription
========================================================================= */
void linkTask()
{
	U16		recv_size;
	U8		toggleRW = 0;
	int		msgQNumMsg;
	ERR_CI	rtnValue;
 
	CABLE_Q	 *pPodMsg, podMsg, podMsgBak;//pys_edit
	message_queue_t* nextQueueExist;
 
	while(1)
	{
		while( toggleRW )      // 여기서의 toggleRW 가 의미하는 것은 반드시 한번 write 하면 다음에는 한번 read 한다는 것이다. 설사 write 할 것이 없다고 하더라도 반드시 일정시간 안에는 서로 간의 통신이 이루어져야 한다.
		{
_RETRY_CM_MESSAGE_READ:                                              // 메세지를 읽을 때 POD -> HOST
			rtnValue = checkDAbit(COMMAND_CH);
			if(rtnValue==CI_OK)
			{
				if(linkLayerBufRead(COMMAND_CH,&recv_size)==CI_OK)
				{
					//for 5sec waiting wdCancel(ciUsedWDID);
					parseLPDU(recv_size);
 
					if(*(readCobuf+1)&0x80)
					{
						goto	_RETRY_CM_MESSAGE_READ;
					}
				}
				toggleRW = 0;
			}
			else if( rtnValue==CI_ABORT )
			{
				toggleRW = 0;
			}
 
			if(ciStatusFlag&FlagCiExtendBufferNego)
			{
			//printf("Check EX Ch\n");
				if( checkDAbit(EXTENDED_CH)==CI_OK )
				{
			//	printf("Read EX Ch\n");
					if(linkLayerBufRead(EXTENDED_CH,&recv_size)==CI_OK)
					{
				//		printf("@@@@@linkLayerBufRead EX Ch@@@@\n");
						parseEXLPDU(recv_size);
					}
				}
			}
		}
 
//		task_delay(2*TICKS_10MS);
 
		if(pPodMsg = message_receive_timeout (pLinkQid, TIMEOUT_IMMEDIATE))
		{
			podMsg = *pPodMsg;
			nextQueueExist=pLinkQid->message_queue_next;
			message_release (pLinkQid, pPodMsg);
 
_RETRY_MESSAGE_WRITE:                                                     // 메세지를 쓸 때 HOST -> POD
			switch(podMsg.tag)
			{
				case CI_TRANS_TAG:                           // 트랜스 포트 레이어로 보낼때
						if( (podMsg.pri==T_DATA_LAST) && (podMsg.size==3) )      // 데이터가 마지막인지 여부
						{
							podMsgBak = podMsg;
							if(pPodMsg = message_receive_timeout (pLinkQid, TIMEOUT_IMMEDIATE))
							{
								podMsg = *pPodMsg;
								message_release (pLinkQid, pPodMsg);
 
								goto		_RETRY_MESSAGE_WRITE;
							}
							else
							{
								podMsg = podMsgBak;
 
								task_delay(TICKS_100MS);//pys_edit,transport polling period 300ms
 
								tcObjectSent = (U8)podMsg.pri;
								makeLPDU(&podMsg);
								if(podMsg.ptr!=NULL)			
									free(podMsg.ptr);
								toggleRW = 1;
							}								
						}
						else
						{
							tcObjectSent = (U8)podMsg.pri;
							makeLPDU(&podMsg);
							if(podMsg.ptr!=NULL)			
								free(podMsg.ptr);
							toggleRW = 1;
						}	
				break;
 
				case CI_EXTEND_TAG:                   // extend channel 에 write 
					if(ciStatusFlag&FlagCiExtendBufferNego)
					{
						makeEXLPDU(&podMsg);
					}
					if(podMsg.ptr!=NULL)			
						free(podMsg.ptr);
				break;
 
		   		case CI_LAYER_CLOSE_TAG:                   // 링크 레이어를 종료할 때
				//	printf("\n[POD_CI] LINK TASK SUSPEND\n");
					message_delete_queue (pLinkQid);
					task_exit (0);
				/*	#ifdef ERR_CHECK
					if( task_suspend( ciLinkTID )==ARENA_ERROR )
					{
						ARENA_Print(("[ERR-PODLayer-LinkLayer-Task] taskSuspend Error(LINK)\n" ));
					}
					#endif*/
				break;
 
				default:
					#ifdef ERR_CHECK
						ARENA_Print(("[ERR-PODLayer-LinkLayer-Task] Illegal Message Tag(LINK) = %d\n",podMsg.tag));
					#endif
					break;
			}
		}
 
		//pod로 data를 쓰기전에 DA bit를 check해야하기 때문에 자리를 옮김
		task_delay(2*TICKS_10MS);
 
	}
}

위의 루틴을 보면 다음과 같다. 크게 두 부분으로 나눌 수 있는 데, read 와 write 이다. read 는 pod 에서 host 로 가는 신호이고, write 는 host 에서 pod 로 가는 신호이다.
가장 먼저, 호스트에서 POD 쪽으로 약속된 크기의 버퍼를 보낸다. 이때, 링크 레이어에서는 LPDU 라는 것을 버퍼헤더에 붙인다. 이 LPDU 의 역할은 이것이 분할된 데이터인지, 아닌지를 구분할 수 있게 한다.

00 middle datagram PDU
40 first datagram PDU + one more datagram fragment follow
80 last datagram PDU
C0 only one datagram PDU

위의 표에 따라서 구분한다. write 에서는 또한 3가지 경우로 구분할 수 있다.

  1. CI_TRANS_TAG : command channel 로 넘겨줄 때
  2. CI_EXTEND_TAG : extend channel 로 넘겨 줄때
  3. CI_LAYER_CLOSE_TAG : 링크 레이어를 종료할 때

여기서는 버퍼 사이즈 보다 보낼 데이터가 클 경우, 특정 비트를 설정하여 LPDU 를 데이터 헤더에 붙여 보낸다.
read 에서는 먼저 DA 비트를 읽어보고, POD 모듈이 보낼 데이터가 있으면, 읽어온다. 받아온 데이터의 LPDU 를 보고, 이것이 연속된 데이터인지 아닌지를 구분한다. 하나의 데이터가 모두 왔다면, 상위 계층인 트랜스포트 계층으로 보낸다.

결론

관련 문서와 프로그램을 따라가 본 결론은 이렇다. 링크 레이어는 물리 계층과 전송 계층 사이에 존재하는 계층으로서, 주 업무는 전송하는 데이터의 크기를 체크, LPDU 를 사용하여, 이것이 마지막 데이터인지, 아니면 처음 데이터인지, 아니면 하나의 데이터로 이루어져 있는 지를 분석한다. 이 것은 command 채널과 extend 채널 모두 이루어 진다.
데이터를 쓸 때는 약속한 데이터보다 클때는 LPDU 를 사용해서 이것이 끝이 아님을 알려주고, 읽을 때는 POD 모듈에서 보낸 데이터의 LPDU 를 읽어서 트랜스 포트 레이어로 전송시킨다.

마지막으로 하나 더 덧붙이겠다. 아무리 여러 계층을 거치더라도, 데이터를 보내고 받는 것은 physical 계층에서 이루어 진다. 요약하자면, 링크 계층에서 하는 일은 한가지다.

  1. 약속한 버퍼 사이즈보다 데이터 사이즈가 크면, 쪼개서 보낸다. 이때는 쪼개서 보낸다는 뜻으로 LPDU에 처음 비트를 덧붙여서 보낸다.
  • computer/digitalarena/link_layer.txt
  • Last modified: 3 years ago
  • by likewind