Link Layer
자~ 이제 Physical Layer 에서 한단계 위로 올라가 보자! 아직 휴가의 후유증이 남아 있긴 하지만…
앞에서 얘기했듯이 POD 의 동작을 설명하자면, 각각의 계층마다 하는 역할들이 나뉘어져 있다. 마치 네트워크의 TCP/IP 를 보는 것처럼 말이다.
이러한 각각의 계층으로 나뉘어져 있으면, 프로그램 하기가 용이하다. 각각의 계층마다 모듈 또는 태스크로 만들면 되니 말이다. 여기서는 편하게 Link Layer 를 '링크 레이어'로 부르겠다.
역할
물리 계층의 가장 마지막 수행이 무엇이었는지 생각나는가? 바로 Data channel 과 extend channel 의 buernegotiation 이었다. 각각의 채널의 주고 받을 버퍼 사이즈를 조율하는 과정이다. 바로 이것이 가장 중요한 부분이다. 이후 부터는 각각의 레이어들을 동작시킨다.
layerLinklayerStart(); layerTransportlayerStart(); layerSessionlayerStart(); layerResourcelayerStart();
여기서 가장 먼저 수행되는 링크 레이어의 수행동작에 대해서 살펴보기로 하자! 우선 들어가기 전에 어떤 일을 하는지 알아볼 필요가 있다.
- POD 로 보내려는 또는 받으려는 데이터의 크기가 앞에서 약속한 크기보다 큰지 작은지 판단해서, 분할하는 일을 한다.
- 이 때, 만일 분할하면, 분할된 데이터라는 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가지 경우로 구분할 수 있다.
- CI_TRANS_TAG : command channel 로 넘겨줄 때
- CI_EXTEND_TAG : extend channel 로 넘겨 줄때
- CI_LAYER_CLOSE_TAG : 링크 레이어를 종료할 때
여기서는 버퍼 사이즈 보다 보낼 데이터가 클 경우, 특정 비트를 설정하여 LPDU 를 데이터 헤더에 붙여 보낸다.
read 에서는 먼저 DA 비트를 읽어보고, POD 모듈이 보낼 데이터가 있으면, 읽어온다. 받아온 데이터의 LPDU 를 보고, 이것이 연속된 데이터인지 아닌지를 구분한다. 하나의 데이터가 모두 왔다면, 상위 계층인 트랜스포트 계층으로 보낸다.
결론
관련 문서와 프로그램을 따라가 본 결론은 이렇다. 링크 레이어는 물리 계층과 전송 계층 사이에 존재하는 계층으로서, 주 업무는 전송하는 데이터의 크기를 체크, LPDU 를 사용하여, 이것이 마지막 데이터인지, 아니면 처음 데이터인지, 아니면 하나의 데이터로 이루어져 있는 지를 분석한다. 이 것은 command 채널과 extend 채널 모두 이루어 진다.
데이터를 쓸 때는 약속한 데이터보다 클때는 LPDU 를 사용해서 이것이 끝이 아님을 알려주고, 읽을 때는 POD 모듈에서 보낸 데이터의 LPDU 를 읽어서 트랜스 포트 레이어로 전송시킨다.
마지막으로 하나 더 덧붙이겠다. 아무리 여러 계층을 거치더라도, 데이터를 보내고 받는 것은 physical 계층에서 이루어 진다. 요약하자면, 링크 계층에서 하는 일은 한가지다.
- 약속한 버퍼 사이즈보다 데이터 사이즈가 크면, 쪼개서 보낸다. 이때는 쪼개서 보낸다는 뜻으로 LPDU에 처음 비트를 덧붙여서 보낸다.