레이블이 Free Pascal인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Free Pascal인 게시물을 표시합니다. 모든 게시물 표시

2016년 5월 31일 화요일

ESP8266 and STM32F4


Original Article : http://blog.naver.com/simonsayz/220724527073


ESP8266  -  The Wrong Solution or Good...
요즘 많이 사용하는 ESP8266을 고속 통신이 필요해서 테스트를 해보았습니다.

인터넷 상에서, 너무 좋은 정보들이 많이 있기는 한데, 개인적으로 정말 필요했던, ESP8266 (ESP12, WROOM02)의 물리적인 속도는 어디까지 나오는가에 대한 정보를 찾을 수가 없었습니다.


그래서 직접 TCP/IP 성능을 직접 확인하기로 하고, 각종 테스트를 진행해보았습니다.


상황 , 전제조건
 - 개인 프로젝트용으로 초당 1MB/s ​속도로 스트리밍이 되어야 함.
 - STM32F4의 통신 모듈로 사용해야 함.
 - 인터넷에 있는 내용은 전체가 그런것은 아니지만, 주객전도된 내용이 많음.
   (16Mhz Atmega가 80,160Mhz ESP8266을 통신 모듈로 사용해서,  배보다 배꼽이 더 큼. 

     Atmega가 나쁘다는 것이 아니고, ESP8266 성능이 상대적으로 너무 뛰어남. 접근성에서 Atmega가 좋은 것은 인정)

주의
  bps = bit / sec ,   Bps = byte /sec


실험 결과
 - AT Command 방식으로는 해결 않됨. (성능을 향상하기 위해서, Firmware 직접 작성 필수)
 - ESP8266ex는 160Mhz 클럭사용해야 함.
 - 전체 시스템 통합을 위해서, PC를 사용할 경우,
 - STM32F4 (168Mhz) 사용할 경우, UART는 10.5Mbps까지 가능 (USART1,6사용시), 나머지 Uart,Usart는 5.25Mbps까지 가능
 - ESP8266 Uart는 9Mbps까지는 가능
    ( PC로 테스트하기 위해서는 PL2303HXD를 사용해야 함, FT232BM 사용시 장비 최고 속도 미만인 관계로, 테스트 불가)
 -  ESP8266 단독 실행시 1.4MB/s 정도까지 전송속도가 보장됨 (잘 작성했을 경우)
 - STM32F4의 경우, USART를 사용해도 연동시 7Mbps정도만 가능 (보드에 따라서 성능차이 발생함)
     Resampling x8 사용시, x16 사용시 5Mbps까지 가능
 - 연동을 위해서는 SPI를 사용해야함.
     SPI 통신의 문제점을 해결하기 위해서, Uart도 사용해야 함.  (Master : ESP8266 / Slave : STM32F4 )
     잘 작성을 한다면, SPI만으로도 가능하지만, 응답을 받는 ESP8266 CPU 낭비를 없애기 위해서

     기본 구성도는 ...​
     PC(Mobile) -> Wifi -> ESP8266 -> SPI -> STM32 (Receive)
                                                           Uart <- span="" stm32=""> (Send)


 - ESP8266 코드 작성시, 프로토콜의 특성상, TCP<->Uart Transparent Bridge 예제와 같이 작성하면 망함.
    (TCP/IP Nagle때문에, 비대칭 송수신 통신일 경우, 패킷을 모아주어야 함, 특히 무선인터넷에서는 잘못 작성할 경우, 10배정도 차이가
     발생할 수도 있음.  응답은 한개의 패킷으로  / 개인 프로젝트에서 비대칭 (PC->Embedded System (패킷多),
     Embedded System->PC(패킷小) )
   

최종 결론 (/w STM32F4)
 - 1MBps가 요즘 장비(예. 단말기 - 휴대폰)에서는 저속이나, Embedded 장비에서는 매우 고속임 (bit로 환산할 경우, 10Mbps)
    (cf. 115200bps ~= 11520Bps =  11.5KBps)
 - 고속 통신을 하려면, Interrupt 방식은 Cortex M4라도 절대 사용하면 않됨
    (모두 DMA방식을 사용해야함)
 - SDIO는 DMA Stream,Channel이 고정되어 있으므로, DMA 매핑을 할때 0순위로 선정되어야 함.
 - DMA 방식으로 적당히 작성시, 100KBps정도까지 가능
 - Optimized State Machine으로 작성할 경우, 500KB까지 가능
    (손발이 오그라질때까지 최적화를 할 경우, 900KB까지 가능할 것으로 예상)



느낌점
 - 하드웨어가 $2이라도, $1000 소프트웨어가 붙어서, $10000 제품이 될 수도 있음.
   정말 공개 소프트웨어 개발자분들을 존경해야 함.
 - 최근 초등학생을 위한 미국에서 진행하는 S/W 캠프도 있다고 듣고, 엄청 놀랐음.
    (어렸을때 좋은 경험은 될수 있겠지만, 금액이 너무 어마 어마 해서... 손발이 곱계 정도로 짜면서 고민해야 이해할 수 있는 부분을

     금전으로 해결하는 것 같아서, 슬퍼졌음)
    ​     
참고. 하드웨어 통신 속도
 // ---------------------------------------------------------------------------
 //                      Speed    Over16      Over8
 //                               ----------- -----------
 //                               42Mhz 84Mhz 42Mhz 84Mhz
 //                               APB1  APB2  APB1  APB2   FPS    Limit
 //  --------------------------------------------------------------------------
 //  115200 x 8 x 1    =   921600  O     O     O     O      3.75
 //  115200 x 8 x 2    =  1843200  O     O     O     O      7.50
 //  115200 x 8 x 2.8  =  2580480  -     O     O     O     10.50
 //                       2625000  -     O     O     O     10.68
 //  115200 x 8 x 3    =  2764800  -     O     O     O     11.25
 //                       3000000  -     O     O     O     12.20   FT232BM
 //  115200 x 8 x 4    =  3686400  -     O     O     O     15.00
 //  115200 x 8 x 5    =  4608000  -     -           O     18.75   ESP8266 80Mhz(DataSheet p19)
 //                       5250000  -     -           O     21.36
 //  115200 x 8 x 6    =  5529600  -     -           O     22.50
 //  115200 x 8 x 7    =  6451200  -     -           O     26.25
 //  115200 x 8 x 8    =  7372800  -     -           O     30.00
 //  ###################  8000000  -     -                         ESP8266 160Mhz
 //  115200 x 8 x 9    =  8294400  -     -           O     33.75
 //  115200 x 8 x 9.55 =  8801280  -     -           O     35.81   LPix V1.3b
 //  115200 x 8 x 10   =  9216000  -     -           O x   37.50
 //                      10000000  -     -           O x   40.69
 //  115200 x 8 x 11   = 10137600  -     -           O x   41.25
 //                      10500000  -     -           O     42.7    STM32F4
 //  115200 x 8 x 12   = 11059200  -     -
 //  115200 x 8 x 13   = 11980800  -     -
 //                      12000000                                  PL2303HXD



 



2014년 5월 12일 월요일

TCP/IP Stack for Embedded MCU (atmega128)


Original Article : http://blog.naver.com/simonsayz/120212895522

 
코드는 제가...
The time to share with the pascal ... :)
CPU : Atmega128 tested
 
//******************************************************************************
//
//  ** **** PPP Driver
//
//  2011.08.08 Rcv오류 수정 (-20 대신 xor 사용)
//
//
//  -------------------------------------------------------------------------
//
//  API
//  -------------------------------------------------------------------------
//
//  1. Function PPP_Open ('s=2')
//  2. Function PPP_Close;
//  3. Function Svr_Open (TCP,80,'
www.maxpaper.com' )
//  4. Function Svr_Close;
//  5. Function Svr_Snd  (
//  6. Function Svr_Rcv  (
//
//
//
//  To Do.
//
//    1. 접속,해제 관련 State 정리
//
//
//
//  중요한 부분
//   1. IPCP 의 경우, Address 0.0.0.0 의 Request에 대하여, Nak을 주면서,
//                    사용자 PC의 IP를 줌
//                    기본적으로는 서버 IP를 되돌려줌.
//
//  To Do
//  -------------------------------------------------------------------------
//
//
//  Ref.
//   #1. Intenet
//      
http://www.apps.ietf.org/rfc/rfc1331.html
//       http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/TCP_IP/IP_Header
//
//   #2. Book
//         PPP Design Implementation and Debugging 2nd Ed
//           p57       A Request ID 1
//                     B Reject  ID 1
//                     A Request ID 2
//                     B Nak     ID 2
//                     A Request ID 3
//                     B Ack     ID 3
//                     B Request ID 1
//                     A Ack     ID 1
//
//
//******************************************************************************

unit PPP_Drv;
interface
Uses
 StdCtrls,SysUtils,Windows,VCL_Uart,Forms;

Const
 _cFCS_Init                 = $ffff; // Initial FCS value
 //
 _cPPPFlag                  =   $7E;
 _cEscape                   =   $7D; // 111:1101 -> 뒷 문자 X - $20
 _cMaxBuf                   =  1600;
 _cMaxOpts                  =     7;
 // RFC 1331
 _cPktLCP                   = $C021;
 _cPktCHAP                  = $C223;
 _cPktIPCP                  = $8021;
 _cPktCTCP                  = $002D;
 _cPktUTCP                  = $002F;
 // LCP,IPCP
 _cConf_Req                 =     1;
 _cConf_Ack                 =     2;
 _cConf_Nak                 =     3;
 _cConf_Rej                 =     4;
 _cTerm_Req                 =     5;
 _cTerm_Ack                 =     6;
 _cCode_Rej                 =     7;
 _cProt_Rej                 =     8;
 _cEcho_Req                 =     9;
 _cEcho_Rep                 =    10;
 _cDisc_Req                 =    11;
 _cReserved                 =    12;
 //
 _cLCP_MRU                  =     1; // Maximum_Receive_Unit
 _cLCP_ACCM                 =     2; // Asynchronous Control Character Map
 _cLCP_Auth                 =     3; // Authentication_Protocol
 _cLCP_Quality              =     4; // Quality_Protocol
 _cLCP_Magic                =     5; // Magic_Number
 _cLCP_PFC                  =     7; // Protocol_Field_Compression
 _cLCP_ACFC                 =     8; // Address_Control_Field_Compression
 _cLCP_MLP                  =    12; // Deprecated (Multi-Link-Procedure)
 _cLCP_CallBack             =    13; // $0D CallBack RFC1570
 //
 _cIPCP_IPComp              =     2; // L:6 IP_Compress_Protocol
 _cIPCP_IPAddr              =     3; // L:6 IP_Address
 _cIPCP_1DNSAddr            =   $81; // L:6 Primary_DNS_Addr
 _cIPCP_1NBNSAddr           =   $82; // L:6 Primary_NBNS_Addr
 _cIPCP_2DNSAddr            =   $83; // L:6 Secondary_DNS_Addr
 _cIPCP_2NBNSAddr           =   $84; // L:6 Secondary_NBNS_Addr
 //
 _cIP_TCP                   =   $06;
 _cIP_UDP                   =   $11;
 //
 _cProto_DNS                =   $35;
 _cProto_Web                =   $50;
 //
 _cURG                      =   $20;
 _cACK                      =   $10;
 _cPSH                      =   $08;
 _cRST                      =   $04;
 _cSYN                      =   $02;
 _cFIN                      =   $01;


Type
 //
 TStepState     = (ns0_Init,       // 초기 상태
                   ns1_LCP_Req,    // 서버 -> PC
                   nsLCP_1Rcv,     // 첫번째 LCP 읽고,
                   nsLCP_1Ack,     // Ack 보내고,
                   nsIPCP_1Req);
 //
 TBuf4          = Packed Array[0.. 3] of Byte;
 TBufTcpIp      = Packed Array[0..39] of Byte; // Header
 TStr           = String[60];
 TFCSWork       = (fwStart,
                   fwWork,
                   fwEnd);
 TCodeState     = (csCmd,
                   csLen,
                   csDat);
 TCRCType       = (ctTCP,
                   ctIP,
                   ctUDP);
 TTcpState      = (tsOpen,       // Flag 2 UAPRSF (Sync)
                   tsWork,       // Flag
                   tsClose);
 //
 TPktType       = (ptNone,
                   ptLCP,        // Frame Packet Type
                   ptCHAP,
                   ptIPCP,
                   ptTCP);
 TPktState      = (psStart,      // Frame State
                   psAddr,
                   psControl,
                   psProtocol,
                   psData,
                   psFCS,
                   psStop);
 TPktData       = Packed Record  // Frame Data
                   Len         : Word;
                   Data        : Array[0.._cMaxBuf-1] of Byte;
                  End;
 //
 TIPCP_Cmd      = (icUnknown,    //
                   icIP_Addr,    // IP Addr
                   icIP_Comp,
                   icIP_DNS1,
                   icIP_DNS2);
 TOpt           = Record
                   Cmd : Byte;
                   Len : Byte;
                   Dat : TBuf4;
                  End;
 TOpts          = Record
                   Cnt : Integer;
                   DB  : Array[0.._cMaxOpts-1] of TOpt;
                  End;
 //
 TState_Frame   = (sfCode,
                   sfID,
                   sfLen,
                   sfData);
 //
 TState_TcpIp   = (stVerLen,     // IP  ------------
                   stTOS,
                   stLen,
                   stID,
                   stFlagIP,
                   stTTL,
                   stProtocol,
                   stChkSumIP,
                   stSrcIP,
                   stDstIP,
                   stSrcPort,    // TCP ------------
                   stDstPort,
                   stSeqNum,
                   stAck,
                   stHLen,
                   stFlagTCP,
                   stWin,
                   stChkSumTCP,
                   stUrgent,
                   stData);      // Data -----------
 //
 TPktFrame      = Record
                   State       : TState_Frame;
                   Code        : Byte;
                   ID          : Byte;
                   Len         : Word;     // Code + ID + Len + Data (ex. Data : 16-> 20)
                   Opts        : TOpts;
                  End;
 TPktTcpIp      = Record
                   State       : TState_TCPIP;
                   // IP ------------------------------------------------------
                   VerLen      : Byte;     //  1 $45            : IPV4 / IHL : 5 x 32b = 20 Bytes
                   TOS         : Byte;     //  2 $00            : Type of Service
                   Length      : Word;     //  4 $00,$2C [44]   : Ver ~ Data (TCP+ Data)
                   ID          : Word;     //  6 $73,$49
                   FlagIP      : Word;     //  7 $40,$00        : 0100:0000
                   TTL         : Byte;     //  9 $40,           :
                   Protocol    : Byte;     // 10 $06,           : TCP , $11 : UDP
                   ChkSumIP    : Word;     // 12 $74,$74
                   SrcIP       : TBuf4;    // 16 $0A,$17,$06,$04
                   DstIP       : TBuf4;    // 20 $7C,$D9,$C6,$1A
                   // TCP -----------------------------------------------------
                   SrcPort     : Word;     //  2 $5C,$7A
                   DstPort     : Word;     //  4 $00,$50
                   SegNum      : TBuf4;    //  8 $B1,$14,$19,$B2
                   Ack         : TBuf4;    // 12 $DE,$AC,$94,$D1
                   HLen        : Byte;     // 13 $60 [0110:0000 ->
                   FlagTCP     : Byte;     // 14 $02  UAPRSF ->Sync
                   Win         : Word;     // 16 $01,$FF
                   ChkSumTCP   : Word;     // 18 $AC,$EB,
                   Urgent      : Word;     // 20 $00,$00
                  End;
 TPktDNS        = Record
                   ID          : Word; //
                   Flags       : Word; // 0100  Recursion Desired
                   QuestionRR  : Word; // 0001 Question 1
                   AnswerRR    : Word; // 0000 Query시 0
                   AuthorityRR : Word; // 0000 Query시 0
                   AdditionRR  : Word; // 0000 Query시 0
                   // Addr     : String[50];
                   QueryType   : Word; // 0001 : IP
                   QueryClass  : Word; // 0001 : Internet
                   Name        : Word; // C00C : Name
                   TypeRR      : Word; // 0001 : Type / A
                   //ClassRR   : Word; // 0001 : Class / In
                   TTL         : TBuf4;// 00000000 :
                   Length      : Word; // 0004 : IP경우 4
                   //IP        : TBuf4;//        IP Address
                  End;
{
         //
         $01,$EF, // 02  ID
         $81,$80, // 04  Flag   1000:0001
         $00,$01, // 06  Question Rec
         $00,$01, // 08  Answer Rec
         $00,$05, // 10  Authority Rec
         $00,$05, // 12  Additonal Rec
         $03,$77,$77,$77,                     // Question www
         $08,$6D,$61,$78,$70,$61,$70,$65,$72, //          maxpaper
         $03,$63,$6F,$6D,$00,                 //           com
         $00,$01,                             // Query    Type
         $00,$01,                             // Query    Class
         $C0,$0C,                             // 06 Name --------------------
         $00,$01,                             // 02 Type A
         $00,$01,                             // 02 Clasee / In
         $00,$00,$12,$C1,                     // 04 TTL
         $00,$04,                             // Length
         $7C,$D9,$C6,$1A,                     // Addr
}                                              // ...

 TEnv           = Record
                   ID          : Byte;
                   MagicCode   : TBuf4;
                   IPServer    : TBuf4;    //
                   IPClient    : TBuf4;    // My IP
                   IPHost      : TBuf4;    // Host IP
                   IPComp      : TBuf4;
                   DNS1        : TBuf4;
                   DNS2        : TBuf4;
                   TCPState    : TTCPState;
                  End;
 TPkt           = Record
                   iEscaped    : Boolean;  // 내부 변수
                   iInx        : Word;     // 내부 변수
                   iBuf        : TBuf4;    //
                   iBufTCP     : TBufTcpIp;// TCP/IP Header
                   iFCS        : Word;     // 계산 FCS
                   rFCS        : Word;     // 실제 FCS
                   iStep       : TStepState;

                   //
                   Env         : TEnv;
                   //
                   Type_       : TPktType;
                   State       : TPktState;
                   // PPP Packet
                   Frame       : TPktFrame;
                   TCPIP       : TPktTcpIp;
                   DNS         : TPktDNS;
                   //
                   Data        : TPktData;
                  End;

Var
 _gPkt  : TPkt;       // 패킷 정보
 //
 _gDbg  : TMemo;      // Debug
 _gDStr : String;     // Debug String
 _gUart : TUart;      // Hw


 // Dev Function
 Procedure hwInit        (Uart  : TUart; Dbg : TMemo);
 Procedure hwSndEsc      (sByte : Byte  ); // PPP Escape
 Procedure hwSnd         (sByte : Byte  ); // PPP Direct
 Procedure hwSndBuf      (sData : Pointer; sSize : Integer);

 // Utility Function
 Procedure Dbg            (dsStr : String);
 Function  B2H            (ub  : Byte ) : String;
 Function  B2Buf4         (uA,uB,uC,uD : Byte ) : TBuf4;
 Function  IsSet          (inB : TBuf4) : Boolean;
 Function  B4ToDW         (Buf : TBuf4 ) : DWord;
 Function  DWToB4         (dB : DWord) : TBuf4;

 //
 Procedure IPCP_Data2Opts (Var Pkt : TPkt);
 Procedure IPCP_Opts2Env  (ForceEdit : Boolean; Var Pkt : TPkt);
 Function  IPCP_OptsExist (Var Pkt : TPkt; Cmd : Byte) : Boolean;
 Function  IPCP_OptsComp  (Var Pkt : TPkt; Cmd : Byte; Buf4 : TBuf4) : Boolean;
 Procedure IPCP_Env2Data  (Var Pkt : TPkt);
 Procedure IPCP_Env2DataReject(Var Pkt : TPkt);
 //
 Function  DNS_Query      (Var Pkt : TPkt; Host : TStr) : Boolean;
 Function  DNS_IpPos      (Const Buf : TPktData) : Word;

 //
 Procedure TCP_Send       (Var Pkt : TPkt);
 Procedure TCP_Open       (Var Pkt : TPkt; Ip : TBuf4; Port : Word;Flag : Byte);
 Procedure TCP_Reply      (Var Pkt : TPkt);
 //
 Procedure ppp_Init       (Var Pkt : TPkt);
 Function  ppp_Receive    (Var Pkt : TPkt; InData : Byte) : Boolean;
 //
 Function  ppp_process    (Var Pkt : TPkt; InData : Byte) : Boolean;
 //
 Procedure Send_IPCP_Req;

Const
 //
 _cLCP_Req  : Packed Array[0..55] of Byte =
               ($7E,                                            // Start
                $FF,                                            //  Address
                $7D,$23,                                        //  Control
                $C0,$21,                                        //  Protocol : LCP
                $7D,$21,                                        //  Code : Request
                $7D,$21,                                        //  ID   : 1
                $7D,$20,$7D,$38,                                //  Len  : 18
                $7D,$21,$7D,$24,$7D,$25,$AC,                    //   MRU   01 04 05 AC
                $7D,$22,$7D,$26,$7D,$20,$7D,$20,$7D,$20,$7D,$20,//   ACCM  02 06 00 00 00 00
                $7D,$25,$7D,$26,$7D,$26,$7D,$29,$7D,$22,$7D,$20,//   Magic 05 06 06 09 02 00
                $7D,$27,$7D,$22,                                //   PFC   02 02 (C0 21)
                $7D,$28,$7D,$22,                                //   ACFC  08 02 (FF 03)
                $67,$61,                                        // FCS : Field Check Sequence
                $7E);                                           // End

  _cChap_Res : Packed Array [0..47] of Byte =
               ($7e,
                $c2,$23,                         // CHAP
                $02,                             // Response
                $01,                             // ID
                $00,$2a,                         // (02) Len (42)
                $10,                             // (01) Value-Size (16)
                $95,$38,$af,$c3,$e0,$e0,$7a,$8c, // (16) Option
                $8d,$94,$8e,$43,$ed,$bd,$fd,$3a,
                $30,$31,$30,$35,$37,$34,$30,$31, // (21)
01057401886@lgt.co.kr
                $38,$38,$36,$40,$6c,$67,$74,$2e,
                $63,$6f,$2e,$6b,$72,
                $72,$b6,                         // (02) CRC
                $7e);

 _cIPCP_Req : Packed Array[0..39] of Byte =
               ($7E,                                            // Start
                $FF,                                            //  Address
                $7D,$23,                                        //  Control
                $80,$21,                                        //  Protocol : IPCP
                $7D,$21,                                        //  Code     : Req
                $7D,$30,                                        //  ID       : 10
                $7D,$20,$7D,$30,                                //  Len      : 18 ( Code ~ Data )
                $7D,$22,$7D,$26,$7D,$20,$2D,$7D,$2F,$7D,$21,    //  IP Compress Protocol
                $7D,$23,$7D,$26,$7D,$20,$7D,$20,$7D,$20,$7D,$20,//  IP Addr
                $E3,$73,                                        // FCS
                $7E);                                           // End

 _cIPCP_Req2: Packed Array[0..28] of Byte =
               ($7E,                                            // Start
                $FF,                                            //  Address
                $7D,$23,                                        //  Control
                $80,$21,                                        //  Protocol : IPCP
                $7D,$21,                                        //  Code     : Req
                $7D,$30,                                        //  ID       : 10
                $7D,$20,$7D,$2A,                                //  Len      : 10 ( Code ~ Data )
                $7D,$23,$7D,$26,$7D,$20,$7D,$20,$7D,$20,$7D,$20,//  IP Addr
                $7A,$9A,                                        // FCS
                $7E);                                           // End

implementation
Uses
 PPP_Help;

Const
 _cFCSTbl  : Array[0..255] of Word =
                               ($0000,$1189,$2312,$329b,$4624,$57ad,$6536,$74bf,
                                $8c48,$9dc1,$af5a,$bed3,$ca6c,$dbe5,$e97e,$f8f7,
                                $1081,$0108,$3393,$221a,$56a5,$472c,$75b7,$643e,
                                $9cc9,$8d40,$bfdb,$ae52,$daed,$cb64,$f9ff,$e876,
                                $2102,$308b,$0210,$1399,$6726,$76af,$4434,$55bd,
                                $ad4a,$bcc3,$8e58,$9fd1,$eb6e,$fae7,$c87c,$d9f5,
                                $3183,$200a,$1291,$0318,$77a7,$662e,$54b5,$453c,
                                $bdcb,$ac42,$9ed9,$8f50,$fbef,$ea66,$d8fd,$c974,
                                $4204,$538d,$6116,$709f,$0420,$15a9,$2732,$36bb,
                                $ce4c,$dfc5,$ed5e,$fcd7,$8868,$99e1,$ab7a,$baf3,
                                $5285,$430c,$7197,$601e,$14a1,$0528,$37b3,$263a,
                                $decd,$cf44,$fddf,$ec56,$98e9,$8960,$bbfb,$aa72,
                                $6306,$728f,$4014,$519d,$2522,$34ab,$0630,$17b9,
                                $ef4e,$fec7,$cc5c,$ddd5,$a96a,$b8e3,$8a78,$9bf1,
                                $7387,$620e,$5095,$411c,$35a3,$242a,$16b1,$0738,
                                $ffcf,$ee46,$dcdd,$cd54,$b9eb,$a862,$9af9,$8b70,
                                $8408,$9581,$a71a,$b693,$c22c,$d3a5,$e13e,$f0b7,
                                $0840,$19c9,$2b52,$3adb,$4e64,$5fed,$6d76,$7cff,
                                $9489,$8500,$b79b,$a612,$d2ad,$c324,$f1bf,$e036,
                                $18c1,$0948,$3bd3,$2a5a,$5ee5,$4f6c,$7df7,$6c7e,
                                $a50a,$b483,$8618,$9791,$e32e,$f2a7,$c03c,$d1b5,
                                $2942,$38cb,$0a50,$1bd9,$6f66,$7eef,$4c74,$5dfd,
                                $b58b,$a402,$9699,$8710,$f3af,$e226,$d0bd,$c134,
                                $39c3,$284a,$1ad1,$0b58,$7fe7,$6e6e,$5cf5,$4d7c,
                                $c60c,$d785,$e51e,$f497,$8028,$91a1,$a33a,$b2b3,
                                $4a44,$5bcd,$6956,$78df,$0c60,$1de9,$2f72,$3efb,
                                $d68d,$c704,$f59f,$e416,$90a9,$8120,$b3bb,$a232,
                                $5ac5,$4b4c,$79d7,$685e,$1ce1,$0d68,$3ff3,$2e7a,
                                $e70e,$f687,$c41c,$d595,$a12a,$b0a3,$8238,$93b1,
                                $6b46,$7acf,$4854,$59dd,$2d62,$3ceb,$0e70,$1ff9,
                                $f78f,$e606,$d49d,$c514,$b1ab,$a022,$92b9,$8330,
                                $7bc7,$6a4e,$58d5,$495c,$3de3,$2c6a,$1ef1,$0f78);

//*****************************************************************************
//
//  HardWare Driver Function
//  -------------------------------------------------------------------------
//  추후 HW 환경에 맞도록 Layer 분리
//
//
//*****************************************************************************

// Dev Function
Procedure hwInit  (Uart  : TUart; Dbg : TMemo);
 begin
  //
  _gUart := Uart;
  _gDbg  := Dbg;
  _gDStr := '';
 end;

// PPP Escape
Procedure hwSndEsc(sByte : Byte);
 begin
  Case (sByte in [$0..$20,$7d,$7e]) of
   True  : Begin
            _gUart.SendByte($7D          );
            _gUart.SendByte(sByte xor $20);
            _gDStr := _gDStr + B2H($7D)+',$' + B2H(sByte xor $20) + ',$';
           End;
   False : Begin
            _gUart.SendByte(sByte);
            _gDStr := _gDStr + B2H(sByte) + ',$';
           End;
  End;
 end;

// PPP Direct
Procedure hwSnd   (sByte : Byte);
 begin
  _gUart.SendByte(sByte);
  _gDStr := _gDStr + B2H(sByte) + ',$';
 end;

//
Procedure hwSndBuf      (sData : Pointer; sSize : Integer);
 begin
  _gUart.SendData(sData,sSize);
 end;

//*****************************************************************************
//
//  Utility Function
//  -------------------------------------------------------------------------
//
//
//*****************************************************************************

//
Procedure Dbg(dsStr : String);
 begin
  _gDbg.Lines.Add(dsStr);
 end;

//
Function  B2H (ub : Byte) : String;
 begin
  Result := IntToHex(ub,2);
 end;

//
Function  W2H (uW : Word) : String;
 begin
  Result := IntToHex(uW,4);
 end;

//
Function IsSet(inB : TBuf4) : Boolean;
 begin
  Result := (inB[0] + inB[1] + inB[2] + inB[3]) > 0;
 end;

//
Function B4ToDW(Buf : TBuf4 ) : DWord;
 begin
  Result := (Buf[0] shl 24) or
            (Buf[1] shl 16) or
            (Buf[2] shl  8) or
            (Buf[3] shl  0);
 end;

//
Function DWToB4(dB : DWord) : TBuf4;
 begin
  Result[0] := (db shr 24) and $FF;
  Result[1] := (db shr 16) and $FF;
  Result[2] := (db shr  8) and $FF;
  Result[3] := (db shr  0) and $FF;
 end;

//
Function  B2Buf4(uA,uB,uC,uD : Byte ) : TBuf4;
 begin
  Result[0] := uA;
  Result[1] := uB;
  Result[2] := uC;
  Result[3] := uD;
 end;

// FCS [Protocol ~ Data]
Procedure hwSndCRC(fcIn : Byte; Work  : TFCSWork; Var FCS : Word);
 Begin
  //
  Case fcIn of
   _cPPPFlag : begin
                hwSnd($7D);
                hwSnd(fcIn xor $20);
               end;
   else        hwSnd(fcIn);
  End;
  //
  Case Work of
   fwStart,
   fwWork   : begin
               If Work = fwStart then FCS := _cFCS_Init;
               FCS := (FCS shr 8) xor _cFCSTbl[(FCS xor fcIn) and $FF];
              end;
   fwEnd    :  FCS := Swap((FCS xor $FFFF));
  End;
 End;

// FCS [Protocol ~ Data]
Procedure CRC_PPP(Work  : TFCSWork; fcIn : Byte; Var FCS : Word);
 Begin
  //
  Case Work of
   fwStart,
   fwWork   : begin
               If Work = fwStart then FCS := _cFCS_Init;
               FCS := (FCS shr 8) xor _cFCSTbl[(FCS xor fcIn) and $FF];
              end;
   fwEnd    :  FCS := Swap((FCS xor $FFFF));
  End;
 End;


// Data : IP 첫번쨰 위치
// IpCSum := CRC_IP(@_cExIP);
Function CRC_IP  (data:pointer):word;
var
 pw   : Pword;
 x    : word;
 csum : longint;

Begin
 csum := 0;
 //
 pw := data;
 For x := 1 to 10 do
  Begin
   csum := csum + pw^;
   inc(pw);
  end;
 //
 csum   := (csum and $ffff) + (csum shr 16);
 csum   := csum + (csum shr 16);
 csum   := (csum and $00ff) shl 8 + (csum shr 8);
 Result := csum xor $FFFF;
end;

// Pseudo IP Sum
Function CRC_PseudoIP_Sum(pIP : Pointer) : LongInt;
 Var
  pw  : PWord;
  X   : Byte;
 Begin
  // 초기화
  Result := 0;
  // IP Sum
  pw := pIP;
  For x := 1 to 6 do
   Begin
    Result := Result + pw^;
    inc(pw);
   End;
 End;

// TCP :  CRC_TcpUdp(cSum, Pkt, 20);
// UDP :  CRC_TcpUdp(cSum, Pkt,  8);
Function CRC_TcpUdpCalc(cSum    : LongInt;
                        Var Pkt : TPkt; HdrLen : Integer) : Word;
var
 pw    : Pword;
 x     : word;
Begin
 // Step #01. Header (IP+TCP,IP+UDP) ------------------------------------------
 pw := @Pkt.iBufTCP;
 For x := 1 to (HdrLen shr 1) do
  Begin
   csum := csum + pw^;
   inc(pw);
  End;
 // Step #02. Data  -----------------------------------------------------------
 pw := @Pkt.Data.Data;
 For x := 1 to (Pkt.Data.Len shr 1) do
  Begin
   csum := csum + pw^;
   inc(pw);
  End;

 If (Pkt.Data.Len mod 2=1) then
  Begin
   X    := PByte(pw)^;
   csum := csum + X;
  end;

 csum   := (csum and $ffff) + (csum shr 16);
 csum   := csum + (csum shr 16);
 csum   := (csum and $00ff) shl 8 + (csum shr 8);
 Result := csum xor $FFFF;
end;

// 전제 조건 : TCPIP ,Data.Len 값이 정확해야 함
Function CRC_TcpUdp(Var Pkt : TPkt; CrcType : TCRCType) : Word;
 var
  IpSum : LongInt;
 begin
  //
  Case CrcType of
   ctIP  : begin
            // IP
            Pkt.iBufTCP[ 0] :=     Pkt.TCPIP.VerLen;
            Pkt.iBufTCP[ 1] :=     Pkt.TCPIP.TOS;
            Pkt.iBufTCP[ 2] :=  Hi(Pkt.TCPIP.Length);
            Pkt.iBufTCP[ 3] :=  Lo(Pkt.TCPIP.Length);
            Pkt.iBufTCP[ 4] :=  Hi(Pkt.TCPIP.ID    );
            Pkt.iBufTCP[ 5] :=  Lo(Pkt.TCPIP.ID    );
            Pkt.iBufTCP[ 6] :=  Hi(Pkt.TCPIP.FlagIP);
            Pkt.iBufTCP[ 7] :=  Lo(Pkt.TCPIP.FlagIP);
            Pkt.iBufTCP[ 8] :=     Pkt.TCPIP.TTL;
            Pkt.iBufTCP[ 9] :=     Pkt.TCPIP.Protocol;
            Pkt.iBufTCP[10] :=  0; // ChkSumIP
            Pkt.iBufTCP[11] :=  0; // ChkSumIP
            Pkt.iBufTCP[12] :=     Pkt.TCPIP.SrcIP[0];
            Pkt.iBufTCP[13] :=     Pkt.TCPIP.SrcIP[1];
            Pkt.iBufTCP[14] :=     Pkt.TCPIP.SrcIP[2];
            Pkt.iBufTCP[15] :=     Pkt.TCPIP.SrcIP[3];
            Pkt.iBufTCP[16] :=     Pkt.TCPIP.DstIP[0];
            Pkt.iBufTCP[17] :=     Pkt.TCPIP.DstIP[1];
            Pkt.iBufTCP[18] :=     Pkt.TCPIP.DstIP[2];
            Pkt.iBufTCP[19] :=     Pkt.TCPIP.DstIP[3];
            //
            Result  := CRC_IP  (@Pkt.iBufTCP);
           end;
   ctTCP,
   ctUDP : begin
            // Calc Pseudo Sum
            Pkt.iBufTCP[ 0] :=     Pkt.TCPIP.SrcIP[0];
            Pkt.iBufTCP[ 1] :=     Pkt.TCPIP.SrcIP[1];
            Pkt.iBufTCP[ 2] :=     Pkt.TCPIP.SrcIP[2];
            Pkt.iBufTCP[ 3] :=     Pkt.TCPIP.SrcIP[3];
            Pkt.iBufTCP[ 4] :=     Pkt.TCPIP.DstIP[0];
            Pkt.iBufTCP[ 5] :=     Pkt.TCPIP.DstIP[1];
            Pkt.iBufTCP[ 6] :=     Pkt.TCPIP.DstIP[2];
            Pkt.iBufTCP[ 7] :=     Pkt.TCPIP.DstIP[3];
            Pkt.iBufTCP[ 8] :=     0; // Pad
            Pkt.iBufTCP[ 9] :=     Pkt.TCPIP.Protocol;
            // TCP
            Case CrcType of
             ctTCP : begin
                      Pkt.iBufTCP[10] :=  Hi(20 + Pkt.Data.Len); // TCP + Data Length
                      Pkt.iBufTCP[11] :=  Lo(20 + Pkt.Data.Len); // TCP + Data Length
                      IpSum   := CRC_PseudoIP_Sum(@Pkt.iBufTCP);
                      //
                      Pkt.iBufTCP[ 0] :=  Hi(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 1] :=  Lo(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 2] :=  Hi(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 3] :=  Lo(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 4] :=     Pkt.TCPIP.SegNum[0];
                      Pkt.iBufTCP[ 5] :=     Pkt.TCPIP.SegNum[1];
                      Pkt.iBufTCP[ 6] :=     Pkt.TCPIP.SegNum[2];
                      Pkt.iBufTCP[ 7] :=     Pkt.TCPIP.SegNum[3];
                      Pkt.iBufTCP[ 8] :=     Pkt.TCPIP.Ack   [0];
                      Pkt.iBufTCP[ 9] :=     Pkt.TCPIP.Ack   [1];
                      Pkt.iBufTCP[10] :=     Pkt.TCPIP.Ack   [2];
                      Pkt.iBufTCP[11] :=     Pkt.TCPIP.Ack   [3];
                      Pkt.iBufTCP[12] :=     Pkt.TCPIP.HLen;
                      Pkt.iBufTCP[13] :=     Pkt.TCPIP.FlagTCP;
                      Pkt.iBufTCP[14] :=  Hi(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[15] :=  Lo(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[16] :=  0; // TCP Check Sum
                      Pkt.iBufTCP[17] :=  0;
                      Pkt.iBufTCP[18] :=  Hi(Pkt.TCPIP.Urgent );
                      Pkt.iBufTCP[19] :=  Lo(Pkt.TCPIP.Urgent );
                      Result := CRC_TcpUdpCalc(IpSum,Pkt,20);
                     end;
             ctUDP : begin
                      Pkt.iBufTCP[10] :=  Hi(8 + Pkt.Data.Len); // TCP + Data Length
                      Pkt.iBufTCP[11] :=  Lo(8 + Pkt.Data.Len); // TCP + Data Length
                      IpSum   := CRC_PseudoIP_Sum(@Pkt.iBufTCP);
                      //
                      Pkt.iBufTCP[ 0] :=  Hi(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 1] :=  Lo(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 2] :=  Hi(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 3] :=  Lo(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 4] :=  Hi(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[ 5] :=  Lo(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[ 6] :=  0; // UDP Check Sum
                      Pkt.iBufTCP[ 7] :=  0;
                      Result := CRC_TcpUdpCalc(IpSum,Pkt,8);
                     end;
            End;
            //
           end;
  End;
 end;

// Raw 데이터를 Pkt.Frame.Opts에...
// Pkt.Data -> Pkt.Frame.Opts
Procedure IPCP_Data2Opts(Var Pkt : TPkt);
 Var
  Lp     : Integer;
  CState : TCodeState;
  Dat    : Byte;
  Inx    : Integer;
 begin
  // 초기화
  cState             := csCmd;
  Inx                := 0;
  Pkt.Frame.Opts.Cnt := 0;
  //
  For Lp := 0 to Pkt.Data.Len-1 do
   begin
    Dat := Pkt.Data.Data[Lp];
    //
    Case cState of
     csCmd : Begin
              cState := csLen;
              Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Cmd := Dat;
             End;
     csLen : Begin
              cState := csDat;
              Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Len := Dat;
              Inx    := 0;
              Case Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Len <= 2 of
               True  : Begin
                        cState := csCmd;
                        Inc(Pkt.Frame.Opts.Cnt);
                       End;
               False :  cState := csDat;
              End;
             End;
     csDat : Begin
              //
              Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Dat[Inx] := Dat;
              Inc(Inx);
              //
              If Inx >= Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Len-2 then
               Begin
                cState := csCmd;
                Inc(Pkt.Frame.Opts.Cnt);
               End;
             End;
    End;
   end;
 end;

// Opts값 -> Env변수 Update
Procedure IPCP_Opts2Env (ForceEdit  : Boolean; Var Pkt : TPkt);
 Var
  Lp : Integer;
 begin
  For Lp := 0 to Pkt.Frame.Opts.Cnt-1 do
   Case Pkt.Frame.Opts.DB[Lp].Cmd of
    _cIPCP_IPComp    : If (ForceEdit)or(Not(IsSet(Pkt.Env.IPComp))) then
                        Pkt.Env.IPComp := Pkt.Frame.Opts.DB[Lp].Dat;
    _cIPCP_IPAddr    : Case ForceEdit of
                        True : Pkt.Env.IPClient := Pkt.Frame.Opts.DB[Lp].Dat;
                        False: Pkt.Env.IPServer := Pkt.Frame.Opts.DB[Lp].Dat;
                       End;
    _cIPCP_1DNSAddr  : If (ForceEdit)or(Not(IsSet(Pkt.Env.DNS1  ))) then
                        Pkt.Env.DNS1   := Pkt.Frame.Opts.DB[Lp].Dat;
    _cIPCP_2DNSAddr  : If (ForceEdit)or(Not(IsSet(Pkt.Env.DNS2  ))) then
                        Pkt.Env.DNS2   := Pkt.Frame.Opts.DB[Lp].Dat;
    _cIPCP_1NBNSAddr : ;
    _cIPCP_2NBNSAddr : ;
   end;
 end;

//
Function  IPCP_OptsExist(Var Pkt : TPkt; Cmd : Byte) : Boolean;
 Var
  Lp : Integer;
 begin
  // 초기화
  Result := False;
  //
  For Lp := 0 to Pkt.Frame.Opts.Cnt-1 do
   If Pkt.Frame.Opts.DB[Lp].Cmd = Cmd then
    begin
     Result := True;
     Exit;
    end;
 end;

//
Function  IPCP_OptsComp (Var Pkt : TPkt; Cmd : Byte; Buf4 : TBuf4) : Boolean;
 Var
  Lp : Integer;
  Lp2: Integer;
 begin
  // 초기화
  Result := False;
  //
  For Lp := 0 to Pkt.Frame.Opts.Cnt-1 do
   If Pkt.Frame.Opts.DB[Lp].Cmd = Cmd then
    begin
     For Lp2 := 0 to 3 do
      If Buf4[Lp2] <> Pkt.Frame.Opts.DB[Lp].Dat[Lp2] then
       Exit;
     //
     Result := True;
     Exit;
    end;
 end;

// Env 변수 -> Pkt.Data로 이전
Procedure IPCP_Env2Data (Var Pkt : TPkt);
 Var
  Ofs : Integer;
 begin
  //
  Ofs := 0;
  Pkt.Data.Data[Ofs+0] := _cIPCP_IPAddr;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.IpServer,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Len := Ofs;
 end;

//
Procedure IPCP_Env2DataReject(Var Pkt : TPkt);
 Var
  Ofs : Integer;
 begin
  //
  Ofs := 0;
  //
  Pkt.Data.Data[Ofs+0] := _cIPCP_IPComp;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.IpComp,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Data[Ofs+0] := _cIPCP_1DNSAddr;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.DNS1,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Data[Ofs+0] := _cIPCP_2DNSAddr;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.DNS2,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Len := Ofs;
 end;

//
Procedure Reply_LCP(Var Step : TStepState; Var Pkt : TPkt);
 Var
  FCS : Word;
  Lp  : Integer;
 begin
  _gDStr := '';
  hwSnd   (_cPPPFlag); // 7E
  hwSndEsc($FF   );  CRC_PPP(fwStart,$FF,FCS); // Protocol
  hwSndEsc($03   );  CRC_PPP(fwWork ,$03,FCS);
  hwSndEsc($C0   );  CRC_PPP(fwWork ,$C0,FCS);
  hwSndEsc($21   );  CRC_PPP(fwWork ,$21,FCS);
  Case Pkt.Frame.Code of  // Code
   _cConf_Req : begin
                 Pkt.Frame.Code := _cConf_Ack;
                 hwSndEsc  (Pkt.Frame.Code);
                 CRC_PPP(fwWork,Pkt.Frame.Code,FCS);
                end;
  End;
  hwSndEsc     (Pkt.Frame.ID);      CRC_PPP(fwWork ,Pkt.Frame.ID     ,FCS);
  hwSndEsc     (Hi(Pkt.Frame.Len)); CRC_PPP(fwWork ,Hi(Pkt.Frame.Len),FCS);
  hwSndEsc     (Lo(Pkt.Frame.Len)); CRC_PPP(fwWork ,Lo(Pkt.Frame.Len),FCS);
  // Cmd:Len:Data ...
  For Lp := 0 to Pkt.Data.Len-1 do
   Begin
    hwSndEsc   (Pkt.Data.Data[Lp]);
    CRC_PPP(fwWork ,Pkt.Data.Data[Lp],FCS);
   End;

  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSndEsc(Hi(FCS)  );
  hwSndEsc(Lo(FCS)  );
  hwSnd   (_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_LCP('Snd:',Pkt );
 end;

//
Procedure Reply_IPCP(Var Step : TStepState; Var Pkt : TPkt);
 Var
  FCS : Word;
  Lp  : Integer;
 begin
  // Raw Data 값을 Opts Record로 이동후, 환경 변수에 저장
  IPCP_Data2Opts(Pkt);
  Case Pkt.Frame.Code of
   _cConf_Req  : IPCP_Opts2Env(False,Pkt);
   _cConf_Nak  : IPCP_Opts2Env(True ,Pkt);
   _cConf_Ack  : Exit;
  End;
  // Help_Env (Pkt);

  _gDStr := '';
  hwSnd   (_cPPPFlag); // 7E
  hwSndEsc($FF      );  CRC_PPP(fwStart,$FF,FCS); // Protocol
  hwSndEsc($03      );  CRC_PPP(fwWork ,$03,FCS);
  hwSndEsc($80      );  CRC_PPP(fwWork ,$80,FCS);
  hwSndEsc($21      );  CRC_PPP(fwWork ,$21,FCS);
  Case Pkt.Frame.Code of  // Code
   _cConf_Req : Begin
                 Dbg('IPCP : Req->Req');
                 If IPCP_OptsExist(Pkt,_cIPCP_1DNSAddr) or
                    IPCP_OptsExist(Pkt,_cIPCP_2DNSAddr) then
                  Begin
                   IPCP_Env2DataReject(Pkt);
                   Pkt.Frame.Len  := Pkt.Data.Len + 4;
                   Pkt.Frame.Code := _cConf_Rej;
                  End
                 else
                  Pkt.Frame.Code := _cConf_Ack;
                 //
                 hwSndEsc  (Pkt.Frame.Code);
                 CRC_PPP(fwWork,Pkt.Frame.Code,FCS);
                End;
   _cConf_Nak : Begin
                 Dbg('IPCP : Nak->Req');
                 Pkt.Frame.Code := _cConf_Req;
                 Inc(Pkt.Env.ID);
                 Pkt.Frame.ID := Pkt.Env.ID;
                 hwSndEsc(Pkt.Frame.Code);
                 CRC_PPP (fwWork,Pkt.Frame.Code,FCS);
                End;
   _cConf_Ack : Begin
                 Pkt.Frame.Code := _cConf_Req;
                 Inc(Pkt.Env.ID);
                 Pkt.Frame.ID := Pkt.Env.ID;

                 hwSndEsc(Pkt.Frame.Code);
                 CRC_PPP (fwWork,Pkt.Frame.Code,FCS);
                end;
  End;
  //
  hwSndEsc     (Pkt.Frame.ID);      CRC_PPP(fwWork ,Pkt.Frame.ID     ,FCS);
  hwSndEsc     (Hi(Pkt.Frame.Len)); CRC_PPP(fwWork ,Hi(Pkt.Frame.Len),FCS);
  hwSndEsc     (Lo(Pkt.Frame.Len)); CRC_PPP(fwWork ,Lo(Pkt.Frame.Len),FCS);
  // Cmd:Len:Data ...
  For Lp := 0 to Pkt.Data.Len-1 do
   Begin
    hwSndEsc   (Pkt.Data.Data[Lp]);
    CRC_PPP(fwWork ,Pkt.Data.Data[Lp],FCS);
   End;

  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSndEsc(Hi(FCS)  );
  hwSndEsc(Lo(FCS)  );
  hwSnd   (_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_LCP('Snd:', Pkt );
 end;

// Pkt.iFCS : Loop
// Pkt.iInx : 갯수
// Pkt.iBuf : '.' 위치
// 중요한점은 7E는 반드시, Escape 시켜야 함
Function  DNS_Query      (Var Pkt : TPkt; Host : TStr) : Boolean;
 Var
  Lp  : Integer;
  FCS : Word;
 begin
  //
  _gDStr := '';
  Result := False;
  If Host = '' then Exit;
  // DNS 관련 -------------------- 12 + 4 +
  Pkt.DNS.ID            := $01EF;
  Pkt.DNS.Flags         := $0100;
  Pkt.DNS.QuestionRR    := $0001;
  Pkt.DNS.AnswerRR      := $0000;
  Pkt.DNS.AuthorityRR   := $0000;
  Pkt.DNS.AdditionRR    := $0000;
  Pkt.DNS.QueryType     := $0001;
  Pkt.DNS.QueryClass    := $0001;

  // UDP DNS 데이터 설정
  Pkt.Data.Len := 0;
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.ID         ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.ID         ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.Flags      ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.Flags      ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.QuestionRR ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.QuestionRR ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.AnswerRR   ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.AnswerRR   ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.AuthorityRR); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.AuthorityRR); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.AdditionRR ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.AdditionRR ); Inc(Pkt.Data.Len);
  // DNS Name 설정 (
www.maxpaper.com )
  Pkt.iInx := Pkt.Data.Len;
  Pkt.iFCS := 0;
  For Lp := 1 to Length(Host) do
   Case host[Lp] = '.' of
    False : Begin
             Pkt.Data.Data[Pkt.Data.Len+Lp] := Ord(Host[Lp]);
             Inc(Pkt.iFCS);
            End;
    True  : Begin
             Pkt.Data.Data[Pkt.iInx] := Pkt.iFCS;
             Pkt.iInx := Pkt.Data.Len+Lp;
             Pkt.iFCS :=  0;
            End;
   End;
  //
  Pkt.Data.Data[Pkt.iInx] := Pkt.iFCS;  Inc(Pkt.Data.Len,Length(host)+1);
  Pkt.Data.Data[Pkt.Data.Len] := $00;    ;  Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.QueryType  ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.QueryType  ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.QueryClass ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.QueryClass ); Inc(Pkt.Data.Len);

  // IP 관련
  Pkt.TCPIP.VerLen      := $45;
  Pkt.TCPIP.TOS         := $00;
  Pkt.TCPIP.Length      := 20 + (8 + Pkt.Data.Len); // 62 ( 20 + 8 + 34 )
  Pkt.TCPIP.ID          := $1BA3;
  Pkt.TCPIP.FlagIP      := $0000;
  Pkt.TCPIP.TTL         := $80;
  Pkt.TCPIP.Protocol    := _cIP_UDP;
  Pkt.TCPIP.SrcIp       := Pkt.Env.IPClient; // B2Buf4($0A,$17,$06,$04);
  Pkt.TCPIP.DstIP       := Pkt.Env.DNS1;     // B2Buf4($96,$02,$7E,$66);
  // UDP 관련
  Pkt.TCPIP.SrcPort     := $04EC; // DE6;
  Pkt.TCPIP.DstPort     := $0035; // 53
  Pkt.TCPIP.Win         := 8 + Pkt.Data.Len; // 8 + Data[34]

  // Send ----
  Pkt.TCPIP.ChkSumIP  := CRC_TCPUDP(Pkt,ctIP );
  Pkt.TCPIP.ChkSumTCP := CRC_TCPUDP(Pkt,ctUDP);
  //
  hwSnd (_cPPPFlag); // 7E
  // hwSndEsc(rU,Snd);
  hwSndCRC($FF,fwStart,FCS); // Protocol
  hwSndCRC($03,fwWork ,FCS); // Control
  hwSndCRC($00,fwWork ,FCS); // TCP/IP
  hwSndCRC($21,fwWork ,FCS); //
  // IP -----------------------------------------------------------------------
  hwSndCRC($45                    ,fwWork,FCS); // VerLen
  hwSndCRC($00                    ,fwWork,FCS); // TOS
  hwSndCRC(Hi(Pkt.TCPIP.Length)   ,fwWork,FCS); // Len ( IP + TCP + Data.Len )
  hwSndCRC(Lo(Pkt.TCPIP.Length)   ,fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ID    )   ,fwWork,FCS); // ID
  hwSndCRC(Lo(Pkt.TCPIP.ID    )   ,fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.FlagIP)   ,fwWork,FCS); // FlagIP (3:13)
  hwSndCRC(Lo(Pkt.TCPIP.FlagIP)   ,fwWork,FCS); //
  hwSndCRC(   Pkt.TCPIP.TTL       ,fwWork,FCS); // TTL
  hwSndCRC(   Pkt.TCPIP.Protocol  ,fwWork,FCS); // Protocol
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumIP) ,fwWork,FCS); // IP Check Sum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumIP) ,fwWork,FCS); // IP Check Sum
  hwSndCRC(   Pkt.TCPIP.SrcIP[0]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[1]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[2]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[3]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[0]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[1]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[2]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[3]  ,fwWork,FCS); // SrcIP
  // UDP ----------------------------------------------------------------------
  hwSndCRC(Hi(Pkt.TCPIP.SrcPort  ),fwWork,FCS); // Src Port
  hwSndCRC(Lo(Pkt.TCPIP.SrcPort  ),fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.DstPort  ),fwWork,FCS); // Dst Port
  hwSndCRC(Lo(Pkt.TCPIP.DstPort  ),fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.Win      ),fwWork,FCS); // UDP Len
  hwSndCRC(Lo(Pkt.TCPIP.Win      ),fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumTCP),fwWork,FCS); // ChkSum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumTCP),fwWork,FCS); //
  // TCP Data
  If Pkt.Data.Len > 0 then
   For Lp := 0 to Pkt.Data.Len-1 do
    hwSndCRC(Pkt.Data.Data[Lp],fwWork,FCS);
  //
  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSnd(Hi(FCS)  );
  hwSnd(Lo(FCS)  );
  hwSnd(_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_TCP('Snd:',Pkt);
 end;

//
Function  DNS_IpPos (Const Buf : TPktData) : Word;
 Var
  Lp    : Integer;
  Cnt   : Integer;
  Inx   : integer;
  DType : Word;
  DLen  : Word;

begin
 //
 Result := 0;
 If Buf.Len <= 12 then Exit;
 If Not( ( (Buf.Data[2] and $80) = $80 ) and        // Answer
         ( (Buf.Data[5] and $01) = $01 ) and        // Question
         (  Buf.Data[7]          > $01 ) ) then
  Exit;
 //
 Cnt := Buf.Data[7];
 Inx := 0;
 For Lp := 12 to Buf.Len-1 do
  If Buf.Data[Lp] = 0 then
   begin
    Inx := Lp + 5 ; // Name Position
    Break;
   end;
 //
 For Lp := 1 to Cnt do
  begin
   Case Buf.Data[Inx] = $C0 of
    True : Begin // Compress
            Inc(Inx,2);
            Move(Buf.Data[Inx],DType,2); // Read Type
           End;
    False: For DType := Inx to Buf.Len-1 do
            If Buf.Data[DType] = 0 then
             begin
              Inx := DType+1;
              Move(Buf.Data[Inx],DType,2); // Read Type
              Break;
             end;
   End;
   DType := Swap(DType);
   //
   Case DType <> 1 of
    True : begin
            Inc(Inx,8); // Class,TLL,Data
            Move(Buf.Data[Inx],DLen,2); // Read Data Len
            DLen := Swap(DLen);
            Inc(Inx,2+DLen);            // Len + Data
           end;
    False: begin
            Inc(Inx,8); // Class,TLL,Data
            Move(Buf.Data[Inx],DLen,2);
            DLen := Swap(DLen);
            Case DLen of
             4 : begin
                  Inc(Inx,2);
                  Result := Inx;
                  Exit;
                 end;
             else Inc(Inx,2+DLen);      // Len + Data
            End;
           end;
   End;
  end;
end;

//
Procedure TCP_Send       (Var Pkt : TPkt);
 Var
  FCS : Word;
  Lp  : Word;
 begin
  //
  hwSnd (_cPPPFlag); // 7E
  hwSndCRC($FF                    ,fwStart,FCS); // Protocol
  hwSndCRC($03                    ,fwWork ,FCS); // Control
  hwSndCRC($00                    ,fwWork ,FCS); // TCP/IP
  hwSndCRC($21                    ,fwWork ,FCS); //
  // IP ------------------------------------------------------------
  hwSndCRC($45                    ,fwWork ,FCS); // VerLen
  hwSndCRC($00                    ,fwWork ,FCS); // TOS
  hwSndCRC(Hi(Pkt.TCPIP.Length   ),fwWork ,FCS); // Len ( IP + TCP + Data.Len )
  hwSndCRC(Lo(Pkt.TCPIP.Length   ),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ID       ),fwWork ,FCS); // ID
  hwSndCRC(Lo(Pkt.TCPIP.ID       ),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.FlagIP   ),fwWork ,FCS); // FlagIP (3:13)
  hwSndCRC(Lo(Pkt.TCPIP.FlagIP   ),fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.TTL       ,fwWork ,FCS); // TTL
  hwSndCRC(   Pkt.TCPIP.Protocol  ,fwWork ,FCS); // Protocol
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumIP ),fwWork ,FCS); // IP Check Sum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumIP ),fwWork ,FCS); // IP Check Sum
  hwSndCRC(   Pkt.TCPIP.SrcIP[0]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[1]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[2]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[3]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[0]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[1]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[2]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[3]  ,fwWork ,FCS); // SrcIP
  // TCP --------------------------------------------------------
  hwSndCRC(Hi(Pkt.TCPIP.SrcPort)  ,fwWork ,FCS); // Src Port
  hwSndCRC(Lo(Pkt.TCPIP.SrcPort)  ,fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.DstPort)  ,fwWork ,FCS); // Dst Port
  hwSndCRC(Lo(Pkt.TCPIP.DstPort)  ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.SegNum[0] ,fwWork ,FCS); // Seg Num.
  hwSndCRC(   Pkt.TCPIP.SegNum[1] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.SegNum[2] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.SegNum[3] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.Ack   [0] ,fwWork ,FCS); // Ack
  hwSndCRC(   Pkt.TCPIP.Ack   [1] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.Ack   [2] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.Ack   [3] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.HLen      ,fwWork ,FCS); // Header Len (4*X)
  hwSndCRC(   Pkt.TCPIP.FlagTCP   ,fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.Win      ),fwWork ,FCS); // Win
  hwSndCRC(Lo(Pkt.TCPIP.Win      ),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumTCP),fwWork ,FCS); // TCP Check Sum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumTCP),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.Urgent   ),fwWork ,FCS); // Urgent
  hwSndCRC(Lo(Pkt.TCPIP.Urgent   ),fwWork ,FCS); //
  // TCP Data
  If Pkt.Data.Len > 0 then
   For Lp := 0 to Pkt.Data.Len-1 do
    hwSndCRC(Pkt.Data.Data[Lp],fwWork,FCS);
  //
  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSnd(Hi(FCS)  );
  hwSnd(Lo(FCS)  );
  hwSnd(_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_TCP('Snd:',Pkt);
 end;

//
Procedure TCP_Open (Var Pkt : TPkt; Ip : TBuf4; Port : Word; Flag : Byte);
 Var
  Sav4 : TBuf4;
  Len  : Word;
 begin
  //
  Pkt.Env.TCPState   := tsOpen;
  // IP 설정 ------------------------------------------------------------------
  Pkt.TCPIP.VerLen   := $45;
  Pkt.TCPIP.TOS      := $00;
  Pkt.TCPIP.Length   := $2C;
  Pkt.TCPIP.ID       := $4040; // 3D31;
  Pkt.TCPIP.FlagIP   := $4000;
  Pkt.TCPIP.TTL      := $40;
  Pkt.TCPIP.Protocol := _cIP_TCP;
  Pkt.TCPIP.SrcIp    := Pkt.Env.IPClient; // B2Buf4($0A,$17,$06,$04); //
  Pkt.TCPIP.DstIP    := IP;               // B2Buf4($76,$21,$72,$E8);
  // TCP 설정 -----------------------------------------------------------------
  Pkt.TCPIP.SrcPort  := $764A;
  Pkt.TCPIP.DstPort  := Port;
  Case (Flag and _cSyn) = _cSyn of
   True : begin
           Pkt.TCPIP.SegNum   := B2Buf4($00,$42,$24,$5C);
           Pkt.TCPIP.Ack      := B2Buf4($00,$00,$00,$00);
          end;
   False: begin
           Sav4               := Pkt.TCPIP.SegNum;     // Ack,Seq Swap
           Pkt.TCPIP.SegNum   := Pkt.TCPIP.Ack;
           Pkt.TCPIP.Ack      := Sav4;
           //
           Len := Pkt.TCPIP.HLen shr 4;
            Case Len > 5 of
             True  : Len := (Len-5)*4;
             False : Len := 0;
            End;
           Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.ACk)+
                                  Pkt.Data.Len - Len);
          

          end;
  End;
  Pkt.TCPIP.FlagTCP  := Flag;
  Pkt.TCPIP.Win      := $01FF; // Buffer 사이즈
  Pkt.TCPIP.Urgent   := $0000;
  //         ------------------------------------------------------------------
  Case (Flag and _cSyn) = _cSyn of
   True : begin
           Pkt.TCPIP.HLen     := $60;
           Pkt.Data.Len       := 4;
           Pkt.Data.Data[0]   := $02;
           Pkt.Data.Data[1]   := $04;
           Pkt.Data.Data[2]   := $00;
           Pkt.Data.Data[3]   := $D2;
          end;
   False: begin
           Pkt.TCPIP.HLen     := $50;
           Pkt.Data.Len       := 0;
          end;
  End;
  // Check Sum ----------------------------------------------------------------
  Pkt.TCPIP.ChkSumIP  := CRC_TCPUDP(Pkt,ctIP );
  Pkt.TCPIP.ChkSumTCP := CRC_TCPUDP(Pkt,ctTCP);
  //
  TCP_Send(Pkt);
 end;

//
Procedure TCP_Reply(Var Pkt : TPkt);
 Var
  Lp    : Byte;
  Sav4  : TBuf4;
  Sav2  : Word;
  Len   : Integer;
 begin
  _gDStr := '';
  //
  If Pkt.TCPIP.Protocol = _cIP_UDP then
   begin
    Dbg('### Rcv UDP , Pass... ###');
    Exit;
   end;
  // ---------------------------------------------------------------
  Case Pkt.Env.TCPState of
   tsOpen : Begin
             // IP 관련 설정 ---------------------------------------------------
             Pkt.TCPIP.ID       := $3D32;
             Pkt.TCPIP.FlagIP   := $4000;
             Pkt.TCPIP.TTL      := $40;
             Pkt.TCPIP.Protocol := _cIP_TCP;
             Sav4               := Pkt.TCPIP.SrcIP;   // IP Swap
             Pkt.TCPIP.SrcIp    := Pkt.TCPIP.DstIP;
             Pkt.TCPIP.DstIP    := Sav4;
             // TCP 관련 설정 --------------------------------------------------
             Sav2               := Pkt.TCPIP.SrcPort; // Port Swap
             Pkt.TCPIP.SrcPort  := Pkt.TCPIP.DstPort;
             Pkt.TCPIP.DstPort  := Sav2;
             Sav4               := Pkt.TCPIP.SegNum;     // Ack,Seq Swap
             Pkt.TCPIP.SegNum   := Pkt.TCPIP.Ack;
             Pkt.TCPIP.Ack      := Sav4;

             Pkt.TCPIP.HLen     := $50;
             Pkt.TCPIP.Win      := $01FF; // Buffer 사이즈
             //
             Pkt.Data.Len       := 0;
             Pkt.TCPIP.Length   := 20 + 20 + Pkt.Data.Len;
             //
             Case (Pkt.TCPIP.FlagTCP and _cSyn) = _cSyn of
              True : Begin
                      Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.Ack)+1);
                      Pkt.Env.TCPState  := tsWork;
                      Pkt.TcpIp.FlagTCP := _cAck;
                      Dbg('### TCP/IP : Open / ');
                     End;
              False: Begin
                      Dbg('### TCP/IP : Open / Exit #### 문제 발생 #### ');
                      Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.Ack)+1);
                      Pkt.Env.TCPState  := tsWork;
                      Pkt.TcpIp.FlagTCP := _cAck;
                     End;
             End;
            End;
   tsWork : Begin
             Exit;
             Len := Pkt.TCPIP.HLen shr 4;
             Case Len > 5 of
              True  : Len := (Len-5)*4;
              False : Len := 0;
             End;
             Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.ACk)+
                                     Pkt.Data.Len - Len);
             Pkt.TcpIp.FlagTCP := _cAck;
             Dbg('### TCP/IP : Work ' +
                 IntToStr(Pkt.Data.Len));
             //
             Pkt.Data.Len       := 0;
             Pkt.TCPIP.Length   := 20 + 20 + Pkt.Data.Len;
            End;
  End;
  //
  Pkt.TCPIP.ChkSumIP  := CRC_TCPUDP(Pkt,ctIP );
  Pkt.TCPIP.ChkSumTCP := CRC_TCPUDP(Pkt,ctTCP);
  //
  TCP_Send(Pkt);
 end;

//
Procedure ppp_Init   (Var Pkt    : TPkt);
 begin
  Pkt.iEscaped    := False;
  Pkt.iInx        := 0;
  //FillChar(Pkt.Env,SizeOf(Pkt.Env),#0);
  Pkt.State       := psStart;
  Pkt.Frame.State := sfCode;
  Pkt.TcpIp.State := stVerLen;
  //
  Pkt.Type_       := ptLCP;
 end;

//
Procedure Frame_Receive (Var Pkt : TPkt;InData : Byte);
 begin
  //
  Case Pkt.Frame.State of
   sfCode : Begin
             Pkt.Frame.Code  := InData;
             Pkt.Frame.State := sfID;
            End;
   sfID   : Begin
             Pkt.Frame.ID    := InData;
             Pkt.Frame.State := sfLen;
             Pkt.iInx        := 0;
            End;
   sfLen  : Begin
             Case Pkt.iInx of
              0 : Pkt.Frame.Len := InData;
              1 : Pkt.Frame.Len := (Pkt.Frame.Len shl 8) or InData;
             End;
             Inc(Pkt.iInx);
             If Pkt.iInx >= 2 then
              begin
               Pkt.Frame.State := sfData;
               Pkt.Data.Len    := 0;
              end;
            End;
   sfData : Begin
             Pkt.Data.Data[Pkt.Data.Len] := InData;
             Inc(Pkt.Data.Len);
             // 4 = Code[1] + ID[1] + Len[2]
             If Pkt.Data.Len >= (Pkt.Frame.Len - 4)  then
              begin
               Pkt.State := psFCS;
               Pkt.iInx  := 0;
              end;
            End;
  End;
 end;

Function Hdr_Len(Protocol : Byte ) : Byte;
 begin
  //
  Result := 40;
  If Protocol = _cIP_UDP then
   Result := 28;
 end;

//
Procedure TCP_Receive(Var Pkt : TPkt;InData : Byte);
 Var
  ChkSum : Word;
 begin
  //
  Case Pkt.TcpIp.State of
                // IP -----------------------------------------------------
   stVerLen   : Begin
                 Pkt.TcpIp.VerLen := InData;
                 Pkt.TcpIp.State  := stTOS;
                End;
   stTOS      : Begin
                 Pkt.TcpIp.TOS    := InData;
                 Pkt.TcpIp.State  := stLen;
                 Pkt.iInx      := 0;
                End;
   stLen      : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.Length := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.Length := (Pkt.TcpIp.Length shl 8) or InData;
                      Pkt.TcpIp.State  := stID;
                      Pkt.iInx      := 0;
                     End;
                End;
   stID       : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.ID      := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.ID     := (Pkt.TcpIp.ID shl 8) or InData;
                      Pkt.TcpIp.State  := stFlagIP;
                      Pkt.iInx      := 0;
                     End;
                End;
   stFlagIP   : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.FlagIP := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.FlagIP := (Pkt.TcpIp.FlagIP shl 8) or InData;
                      Pkt.TcpIp.State  := stTTL;
                      Pkt.iInx      := 0;
                     End;
                End;
   stTTL      : Begin
                 Pkt.TcpIp.TTL      := InData;
                 Pkt.TcpIp.State    := stProtocol;
                End;
   stProtocol : Begin
                 Pkt.TcpIp.Protocol := InData;
                 Pkt.TcpIp.State    := stChkSumIP;
                 Pkt.iInx        := 0;
                End;
   stChkSumIP : Case Pkt.iInx of
                 0 : begin
                      Pkt.TcpIp.ChkSumIP := InData;
                      Inc(Pkt.iInx);
                     end;
                 1 : begin
                      Pkt.TcpIp.ChkSumIP := (Pkt.TcpIp.ChkSumIP shl 8) or InData;
                      Pkt.TcpIp.State    := stSrcIP;
                      Pkt.iInx           := 0;
                     end;
                End;
   stSrcIP    : Begin
                 Pkt.TcpIp.SrcIP[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stDstIP;
                   Pkt.iInx     := 0;
                  end;
                End;
   stDstIP    : Begin
                 Pkt.TcpIp.DstIP[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stSrcPort;
                   Pkt.iInx     := 0;
                  end;
                End;
                // TCP ---------------------------------------------------------
   stSrcPort  : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.SrcPort := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.SrcPort := (Pkt.TcpIp.SrcPort shl 8) or InData;
                      Pkt.TcpIp.State   := stDstPort;
                      Pkt.iInx          := 0;
                     End;
                End;
   stDstPort  : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.DstPort := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.DstPort := (Pkt.TcpIp.DstPort shl 8) or InData;
                      Pkt.iInx          := 0;
                      //
                      Case Pkt.TcpIp.Protocol of
                       _cIP_TCP : Pkt.TcpIp.State := stSeqNum;
                       _cIP_UDP : Pkt.TcpIp.State := stWin;
                      End;
                     End;
                End;
   stSeqNum   : Begin
                 Pkt.TcpIp.SegNum[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stAck;
                   Pkt.iInx     := 0;
                  end;
                End;
   stAck      : Begin
                 Pkt.TcpIp.Ack[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stHLen;
                   Pkt.iInx     := 0;
                  end;
                End;
   stHLen     : Begin
                 Pkt.TcpIp.HLen    := InData;
                 Pkt.TcpIp.State   := stFlagTCP;
                End;
   stFlagTCP  : Begin
                 Pkt.TcpIp.FlagTCP := InData;
                 Pkt.TcpIp.State   := stWin;
                 Pkt.iInx          := 0;
                End;
   stWin      : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.Win := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.Win := (Pkt.TcpIp.Win shl 8) or InData;
                      Pkt.TcpIp.State   := stChkSumTCP;
                      Pkt.iInx          := 0;
                     End;
                End;
   stChkSumTCP: Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.ChkSumTCP := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.ChkSumTCP := (Pkt.TcpIp.ChkSumTCP shl 8) or InData;
                      Pkt.iInx          := 0;
                      Pkt.Data.Len      := 0;
                      //
                      Case Pkt.TcpIp.Protocol of
                       _cIP_TCP : Pkt.TcpIp.State := stUrgent;
                       _cIP_UDP : Pkt.TcpIp.State := stData;
                      End;
                     End;
                End;
   stUrgent   : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.Urgent  := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.Urgent  := (Pkt.TcpIp.Urgent shl 8) or InData;
                      Pkt.TcpIp.State   := stData;
                      Pkt.iInx          := 0;
                      Pkt.Data.Len      := 0;
                      If Pkt.TcpIp.Length = Hdr_Len(Pkt.TcpIp.Protocol) then
                       begin
                        Pkt.State := psFCS;
                        Pkt.iInx  := 0;
                       end;
                     End;
                End;
   stData     : Begin
                 Pkt.Data.Data[Pkt.Data.Len] := InData;
                 Inc(Pkt.Data.Len);
                 If Pkt.Data.Len >= (Pkt.TCPIP.Length - Hdr_Len(Pkt.TcpIp.Protocol)) then
                  begin
                   Pkt.State := psFCS;
                   Pkt.iInx  := 0;
                   //
                   ChkSum := CRC_TcpUDP(Pkt,ctIP);
                   If Pkt.TCPIP.ChkSumIP <> ChkSum then
                    Dbg('#### Err IP CRC : Pkt:' + W2H(Pkt.TCPIP.ChkSumIP) +
                                        '/Calc:' + W2H(ChkSum));
                   //
                   Case Pkt.TCPIP.Protocol of
                    _cIP_TCP : ChkSum := CRC_TcpUDP(Pkt,ctTCP);
                    _cIP_UDP : ChkSum := CRC_TcpUDP(Pkt,ctUDP);
                   End;
                   If Pkt.TCPIP.ChkSumTCP <> ChkSum then
                    Dbg('#### Err TCP/UDP CRC : Pkt:' + W2H(Pkt.TCPIP.ChkSumTCP) +
                                             '/Calc:' + W2H(ChkSum));
                  end;
                End;
  End;
 end;

//
Function  ppp_Receive(Var Pkt : TPkt; InData : Byte) : Boolean;
 begin
  //
  Result := False;
  // Process Escape Sequence --------------------------------------------------
  If InData = _cEscape then begin
                             Pkt.iEscaped := True;
                             Exit;
                            end;
  If Pkt.iEscaped      then begin
                             InData := InData xor $20;
                             Pkt.iEscaped := False;
                            end;
  // State --------------------------------------------------------------------
  Case Pkt.State of
   psStart   : If InData = _cPPPFlag then
                begin
                 ppp_Init(_gPkt);
                 Pkt.State := psAddr;
                end;
   psAddr    : // $7E $FF $03 $C0 $21 : LCP
               // $7E         $80 $21 : IPCP
               // $7E             $21 : TCP,IP
               // $7E $FF $03 $00 $2F : Uncomp TCP/IP
               // $7E             $2F : Uncomp TCP/IP
               // $7E $FF $03 $00 $2D : Comp   TCP/IP
               // $7E             $2D : Comp   TCP/IP
               Case InData of
                $21,  // UnCompressed
                $2F,  // UnCompressed
                $2D : // Compressed
                      begin
                       Pkt.Type_ := ptTCP;
                       Pkt.State := psData;
                       CRC_PPP(fwStart,InData,Pkt.iFCS);
                      end;
                $7E : ;
                $80 : begin
                       Pkt.iInx           := 0;
                       Pkt.iBuf[Pkt.iInx] := InData;
                       Inc(Pkt.iInx);
                       Pkt.State          := psProtocol;
                       CRC_PPP(fwStart,InData,Pkt.iFCS);
                      end;
                $FF : begin
                       Pkt.Type_ := ptNone;
                       Pkt.State := psControl;
                       CRC_PPP(fwStart,InData,Pkt.iFCS);
                      end
                else  Pkt.State := psStart;
               End;
   psControl : Begin
                Pkt.State := psProtocol;
                Pkt.iInx  := 0;
                CRC_PPP(fwWork,InData,Pkt.iFCS);
               End;
   psProtocol: Begin
                Pkt.iBuf[Pkt.iInx] := InData;
                //
                CRC_PPP(fwWork ,InData,Pkt.iFCS);
                //
                Inc(Pkt.iInx);
                If Pkt.iInx >= 2 then
                 Begin
                  //
                  Case (Pkt.iBuf[0] shl 8) or
                       (Pkt.iBuf[1]      ) of
                   _cPktLCP  : Pkt.Type_ := ptLCP;   // LCP
                   _cPktCHAP : Pkt.Type_ := ptChap;  // Chap
                   _cPktIPCP : Pkt.Type_ := ptIPCP;  // IPCP
                   $0021,      // UnComp
                   $002F,      // UnComp
                   _cPktCTCP : // Comp
                               Pkt.Type_ := ptTCP;   // TCP,UDP
                   else        Pkt.State := psStart;
                  end;
                  //
                  If Pkt.State <> psStart then
                   Pkt.State := psData;
                 End;
               End;
   psData    : // 타입별로 Parsing
               Begin
                Case Pkt.Type_ of
                 ptLCP,
                 ptChap,
                 ptIPCP : Frame_Receive(Pkt,InData);
                 ptTCP  : TCP_Receive  (Pkt,InData);
                 else     Pkt.State := psStart;
                End;
                CRC_PPP(fwWork,InData,Pkt.iFCS);
               End;
   psFCS     : Begin
                Pkt.iBuf[Pkt.iInx] := InData;
                Inc(Pkt.iInx);
                If Pkt.iInx >= 2 then
                 begin
                  CRC_PPP(fwEnd,InData,Pkt.iFCS);
                  Pkt.rFCS := (Pkt.iBuf[0] shl 8) or
                              (Pkt.iBuf[1]      );
                  Pkt.State := psStop;
                  If Pkt.iFCS <> Pkt.rFCS then
                   Dbg('#### Err FCS : Pkt:'+W2H(Pkt.iFCS)+
                                    '/Calc:'+W2H(Pkt.rFCS));
                 end;
               End;
   psStop    : //
               Begin
                Pkt.State       := psStart;
                If InData = _cPPPFlag then
                 Result := True;
               End;
  End;
 end;

Function ppp_process    (Var Pkt : TPkt; InData : Byte) : Boolean;
 Var
  Lp : Integer;
 begin
  Result := PPP_Receive(Pkt,InData);
  If Result then
   Begin
    // 받은 패킷 보여주고...
    Help_Pkt('Rcv:',_gPkt);
    //
    Case _gPkt.Type_ of
     ptLCP  : Case _gPkt.Frame.Code of
               _cConf_Req : begin
                             Case Pkt.iStep of
                              ns0_Init : begin
                                          Dbg('Snd  LCP Request / 신규 채널 ##');
                                          hwSndBuf(@_cLCP_Req,SizeOf(_cLCP_Req));
                                         end;
                             End;
                             // 처음 패킷 응답
                             Reply_LCP(Pkt.iStep,_gPkt);
                             Pkt.iStep := ns1_LCP_Req;
                            end;
               _cConf_Ack : Begin
                             Dbg('####### Chap Send XXXXXXXXX');
                             hwSndBuf(@_cChap_Res,SizeOf(_cChap_Res));
                             //For Lp := 0 to 10 do
                             // begin
                             //  Sleep(20);
                             //  Application.ProcessMessages;
                             // end;
                             //Dbg('####### LCP 종료/ IPCP Request 보냄 #1  ###');
                             //hwSndBuf(@_cIPCP_Req,SizeOf(_cIPCP_Req));
                            End;
              End;
     ptChap : Case _gPkt.Frame.Code of
               _cConf_Req : Dbg('Rcv Chap Req ');
               _cConf_Ack : Dbg('Rcv Chap Ack ');
              End;
     ptIPCP : Reply_IPCP(Pkt.iStep,_gPkt);
     ptTCP  : TCP_Reply (_gPkt);
    End;
   End;
 end;

Procedure Send_IPCP_Req;
 begin
  Dbg('####### 수동  IPCP Request 보냄 #1  ###');
  hwSndBuf(      @_cIPCP_Req2,
           SizeOf(_cIPCP_Req2));
 end;

Initialization
  FillChar(_gPkt.Env,SizeOf(_gPkt.Env),#0);
  ppp_Init(_gPkt);
end.