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에 처음 비트를 덧붙여서 보낸다.