作者:Walzer
日期:2005.4.16
本文集中讨论StartKitl函数中KITLConnectToDesktop过程。
在OEMKitlInit()初始化KITL硬件介质后,KITLGlobalState状态里被打上KITL_ST_KITLSTARTED标记,KITLConnectToDesktop完成后则会有KITL_ST_DESKTOP_CONNECTED标记。这是继续往下运行完成StartKitl的重要一步。在KitlConnectToDesktop中,将首次调用到OEM层对KITL介质的读写函数。
我们依次看下来。KITLConnectToDesktop可以分为两大部分,
首先是发送过程。进入KITLConnectToDesktop后,马上填写要从target发送到PB的数据包。
PKITL_HDR pHdr = (PKITL_HDR) (PpfsFmtBuf + Kitl.FrmHdrSize);
PKITL_DEV_TRANSCFG pCfg = (PKITL_DEV_TRANSCFG) KITLDATA(pHdr);
USHORT cbData = sizeof (PpfsFmtBuf) - Kitl.FrmHdrSize - sizeof (KITL_HDR) - sizeof (KITL_DEV_TRANSCFG);
if (!Kitl.pfnGetDevCfg ((LPBYTE) (pCfg+1), &cbData))
return FALSE;
memset (pHdr, 0, sizeof (KITL_HDR));
pHdr->Id = KITL_ID;
pHdr->Service = KITL_SVC_ADMIN;
pHdr->Cmd = KITL_CMD_TRAN_CONFIG;
cbData += sizeof (KITL_DEV_TRANSCFG);
pCfg->wLen = cbData;
pCfg->wCpuId = KITL_CPUID;
memcpy (pCfg->szDevName, Kitl.szName, KITL_MAX_DEV_NAMELEN);
cbData += sizeof (KITL_HDR);
注意GetDeviceConfig函数在Ether中获取了source ip, source mac和port, 而在usb serial和traditional serial中都是stub.
填写完KITL数据后,就调用KitlSendFrame了。在KitlSendFrame中又首先调用OEM层的Kitl.pfnEncode, 根据ETHER或SERIAL格式填写packet header,然后才调用KitlSendRawData, 进而用Kitl.pfnSend把数据发往PC端的PB。
下面先看Ethernet Kitl中具体发送的该数据包。
00000000: FF FF FF FF FF FF 00 05 5D 53 1C 37 08 00 45 00
00000010: 00 44 00 00 00 00 40 11 FB FE C0 A8 BE 02 FF FF
00000020: FF FF 03 D5 03 D5 00 30 01 F4 45 44 42 47 FF 00
00000030: 00 09 20 00 41 00 41 4D 4F 49 53 4F 46 54 37 32
00000040: 32 33 00 00 00 00 C0 A8 BE 02 00 05 5D 53 1C 37
00000050: 03 D5
逐位分析如下:
------------------------DLC Header
0~5 FF FF FF FF FF //Destination Mac Address. (BROADCAST)
6~11 00 05 5d 53 1c 37 //Source Mac Address (Station DLink 531C37)
12~13 08 00 // fType = IP. ( IP_Frame 08 00 | ARP_FRAME 08 06 )
-----------------------IPV4 Header
14 45 // Stardard IPV4 Header, Version =4, header length=20 bytes
15 00 // TOS ( type of service,
//bit 0 = 0, CE bit - no congestion;
//bit1 = 0, ECT bit - transport protocol will ignore the CE bit;
//bit 2 =0, normal reliability;
//bit 3 = 0, normal throughtput;
//bit 4 = 0, normal delay;
//bit 5 ~ bit 7 = 000, routine.
16~17 00 44 // Total length = 68 bytes.
18~19 00 00 // id
20~21 00 00 // fragment (may fragmet = 0 , last fragment = 0, fragment oFFset = 0 bytes)
22 40 // TTL (Time to live = 64 seconds/hops)
23 11 // protocol = UDP ( UDP_PROTOCOL=17 | ICMP_PROTOCOL=1 )
24~25 FB FE // header check sum
26~29 C0 A8 BE 02 // Source Ip 192.168.190.2
30~33 FF FF FF FF // Destination Ip (it is a brocast packet)
------------------------UDP Header
34~35 03 D5 // source port = 981
36~37 03 D5 // destination port = 981
38~39 00 30 // length = 48
40~41 01 F4 // header check sum
42~82 UDP 40 bytes of data
------------------------KITL PACKET
42~45 45 44 42 47 // "EDBG"
46 FF // service = KITL_SVC_ADMIN
47 00 // Flags
48 00 // SeqNum
49 09 // Cmd = KITL_CMD_TRAN_CONFIG
50~51 20 00 // wLen
52~53 41 00 // wCPUId = KITL_CPU_ARMV4I
54~69 41 4D 4F 49 53 4F 46 54 37 32 32 33 00 00 00 00 // szDeviceName = AMOISOFT7223
70~73 C0 A8 BE 02 // Source IP = 192.168.190.2
74~79 00 05 5D 53 1C 37 // Source Mac Address (Station DLink 531C37)
80~81 03 D5 // Source Port = 981
// 最后三行是在KITLConnectToDesktop中调用Kitl.pfnGetDevCfg时加进来的. 不太理解为什么IPV4里已经包含该信息了, 这里又重复一次. 而在SerialType中SerialGetDevCfg是个Stub.
接着来看一下Traditional Serial KITL发送的数据包
[COM1, BaudRate=115200, DataBits=8, StopBits=1, Parity=None, FlowControl=None]
发送bytes=cbData + Kitl.FrmHdrSize + Kitl.FrmTlrSize = 0x1C + 0xA + 0x0 = 0x26 = 38 bytes
-----------------------------SERIAL HEADER-----------------------------
0~3 6B 49 54 4C // "kITL"
4 AA // PacketType = OAL_KITL_SERIAL_PACKET
5 00 // reserved
6~7 1C 00 // PayloadSize = cbData
8 3E // crcData
9 58 // crcHeader
-----------------------------KITL PACKET-----------------------------
10~13 45 44 42 47 // "EDBG"
14 FF // service = KITL_SVC_ADMIN
15 00 // flags = 0
16 00 // SeqNum = 0
17 09 // Cmd = KITL_CMD_TRAN_CONFIG
18~19 14 00 // wLen = 20
20~21 41 00 // wCPUId = KITL_CPU_ARMV4I
22~38 53 65 72 69 61 6C 44 65 76 69 63 65 00 00 00 00 00 // "SerialDevice"
下面再看看USB SERIAL KITL在这步发送的数据包, 除了DeviceName以外, 其他都和Traditional Serial的一样.
00000000: 6b 49 54 4c aa 00 1c 00 a9 c3 45 44 42 47 FF 00
00000010: 00 09 14 00 41 00 55 53 42 44 65 76 69 63 65 00
00000020: 00 00 00 00 00 00
逐位分析如下:
-----------------------------SERIAL HEADER-----------------------------
0~3 6B 49 54 4C // "kITL"
4 AA // PacketType = OAL_KITL_SERIAL_PACKET
5 00 // reserved
6~7 1C 00 // PayloadSize = cbData
8 9F // crcData
9 C3 // crcHeader
-----------------------------KITL PACKET-----------------------------
10~13 45 44 42 47 // "EDBG"
14 FF // service = KITL_SVC_ADMIN
15 00 // flags = 0
16 00 // SeqNum = 0
17 09 // Cmd = KITL_CMD_TRAN_CONFIG
18~19 14 00 // wLen = 20
20~21 41 00 // wCPUId = KITL_CPU_ARMV4I
22~38 55 53 42 44 65 76 69 63 65 00 00 00 00 00 00 00 // "USBDevice"
发送完成后就开始轮询等待PB回应。具体到函数就是KITLPollResponse -> KITLPollData -> HandleRecvInterrupt. 前面的箭头表示函数调用关系,而不是指针。在HandleRecvInterrupt中再调用Kitl.pfnRecv收取PACKET, 然后进到ProcessRecvFrame中处理。由于我们现在所处阶段,所以PB回应的PACKET中,必定serivce = KITL_SVC_ADMIN, 进而走到ProcessAdminMsg中处理。
Ethernet KITL收到的第一个包是这样的:
00000000: 00 05 5d 53 1c 37 00 40 b8 51 a8 32 08 00 45 00
00000010: 00 50 fe 49 00 00 80 11 3e f4 c0 a8 be 0b c0 a8
00000020: be 02 06 8e 03 d5 00 3c 4d ea 45 44 42 47 ff 00
00000030: 00 09 2c 00 00 00 0f 00 00 00 56 34 12 00 ff ff
00000040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00000050: ff ff ff ff bf ff ff ff bf ff ff ff ff ff
分析如下:
--------------------------------DLC Header--------------------------------
0~5 00 05 5d 53 1c 37 // Desination Mac Address = Station DLink 531C37
6~11 00 40 b8 51 a8 32 // Source Mac Address = Station IdeaAs51A832
12~13 08 00 // Ethertype = IP
--------------------------------IP Header--------------------------------
14 45 // IpVersion = 4, header length = 20 bytes
15 00 // Type of service = 00
16~17 00 50 // Total length = 80 bytes
18~19 fe 49 // Identification = 65097
20~21 00 // flags = 0, fragment offset = 0 bytes.
22 80 // Time to live = 128 seconds/hops
23 11 // Protocol = 17 (UDP)
24~25 3e f4 // Header checksum (correct)
26~29 c0 a8 be 0b // Source IP address = [192.168.190.11]
30~33 c0 a8 be 02 // Destination address = [192.168.190.2]
--------------------------------UDP Header--------------------------------
34~35 06 8e // Source port = 1678
36~37 03 d5 // Destination port = 981
38~39 00 3c // Length = 60
40~41 4d ea // Checksum
42~93 KITL PACKET // UDP 52 bytes of data
--------------------------------Kitl Packet--------------------------------
42~45 45 44 42 47 // "EDBG"
46 ff // service = KITL_SVC_ADMIN
47 00 // Flags
48 00 // SeqNum
49 09 // cmd = KITL_CMD_TRAN_CONFIG
50~51 2c 00 00 00 // dwLen
52~57 0f 00 00 00 // dwFlags = 0f
58~61 56 34 12 00 // dwKeySig = 00123456 (HOST_TRANSCFG_KEYSIG)
62~65 FF FF FF FF // g_dwKeys[0] = -1, nk , registry settings of desktop zone settings,
66~69 FF FF FF FF // g_dwKeys[1] = -1, filesys, ditto
70~73 FF FF FF FF // g_dwKeys[2] = -1, fsdmgr
74~77 FF FF FF FF // g_dwKeys[3] = -1, relfsd
78~81 FF FF FF FF // g_dwKeys[4] = -1, device
82~85 FF BF FF FF // g_dwKeys[5], CeLogZoneCE
86~89 FF BF FF FF // g_dwKeys[6], CeLogZoneUser
90~93 FF FF FF FF // g_dwKeys[7] = -1, CeLogZoneProcess
上面的关键点在于 cmd = 0x09, 这样就进入ProcessAdminMsg函数中的case KITL_CMD_TRAN_CONFIG,该分支里有KITLGlobalState |= KITL_ST_DESKTOP_CONNECTED, 打上该标记后就很容易往下执行了。
下面看看Traditional Serial在KITLPollingData里收到的第一个包:
-----------------------------SERIAL HEADER-----------------------------
0~3 6B 49 54 4C // "kITL"
4 AA // PacketType = OAL_KITL_SERIAL_PACKET
5 01 // reserced
6~7 34 00 // PayloadSize = cbData
8 51 // crcData
9 84 // crcHeader
--------------------------------Kitl Packet--------------------------------
42~45 45 44 42 47 // "EDBG"
46 FF // service = KITL_SVC_ADMIN
47 00 // Flags
48 00 // SeqNum
49 09 // cmd = KITL_CMD_TRAN_CONFIG
50~51 2C 00 00 00 // dwLen
52~57 0F 00 00 00 // dwFlags = 0F
58~61 56 34 12 00 // dwKeySig = 00123456 (HOST_TRANSCFG_KEYSIG)
62~93 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff bf ff ff ff ff ff // g_dwKeys[0]~[7]
可以看到,除去Kitl类型决定的Header外, 这个包的Kitl Packe的内容是一样的. Cmd=0x9和dwKeySig=00123456决定了能够使KITLGlobalState |= KITL_ST_DESKTOP_CONNECTED. 但目前Traditional Serial Kitl的问题是在打上CONNECTED记号后,会继续运行HandleRecvInterrupt函数, 只有当fFrameRecvd不为零, 即该次循环没有收到packet的时候, 才会跳出这个while循环以跳出该函数, 继续往下执行完成StartKitl. 但从串口监视的数据来看,PB在发cmd=0x9这个PACKET后,还不断地发送cmd=0x6和cmd=0xA的packet, 使得程序卡在这里. 值得一提的时, 这个cmd=0x9的包也不是每次都能收到的, 有时候(满足一定条件或概率, 条件未知)在发送cmd=0x9的包后, 将只收到0x6和0xa的包.
另外, 从串口KITL监听的数据来看, 从点下AttachDevice开始download image开始, PB就发kITL数据包过来了. 如果开启监听的话, 可以看到在PB收到cmd=0x9之前, 全部是往TARGET发送0x6和0xA的包, 如下:
设置 RTS.
写串口 18 bytes: 6b 49 54 4c aa 01 08 00 1b 22 45 44 42 47 ff 00 00 0a
清除 RTS.
设置 RTS.
写串口 42 bytes: 6b 49 54 4c aa 01 20 00 7e 9d 45 44 42 47 ff 00 00 06 01 44 42 47 4d 53 47 00 2c ff 91 08 28 9c 03 00 90 ff 91 08 00 ff 00 00
清除 RTS.
设置 RTS.
写串口 42 bytes: 6b 49 54 4c aa 01 20 00 4c 6b 45 44 42 47 ff 00 00 06 01 50 50 53 48 00 47 00 2c ff 91 08 28 9c 03 00 90 ff 91 08 00 ff 00 00
清除 RTS.
当然如果没有开启串口监听, 即没有从相应的PORT上面读取数据, 那么我觉得应该在TARGET完成UART初始化并接收第一个PACKET之前, PORT上面一直停留在PB发送的第一个包上.
中间遇到个事情。当我失误先在其他WINXP应用程序里打开COM1,然后AttachDevice的时候(PB里设的Serial COM1 KITL), PB里的DebugWindow直接出现了这三行
(CoreCon) 16:54:33 04/18/2005 中国标准时间: Failed to connect debug message service! Please check the transport settings.
(CoreCon) 16:54:33 04/18/2005 中国标准时间: Transport service failed to connect
(CoreCon) 16:54:33 04/18/2005 中国标准时间: Warning: One or more services failed to connect. Make sure that the service is properly configured.
这和用PB for Wince5.0里使用USB KITL然后Attach时的显示是完全一样的。
还有我怀疑DeviceName也不是随便起的. 我使用Serial Kitl时, 给pszDeviceID="FFUART", 而连接后在PC的注册表里
[HKEY_CURRENT_USER \ Software \ Microsoft \ Windows CE Tools \ Platform Manager] 里面和DeviceName=AMOISOFT7223并列的下一个项里键值DeviceName=SerialDevice. 而用USB连接时这个键值为USBDevice
USB SERIAL KITL收到的第一个包是这样的
00000000: 6b 49 54 4c aa 05 20 00 4f 72 45 44 42 47 ff 00
00000010: 00 06 01 44 42 47 4d 53 47 00 2c ff 5e 08 28 d3
00000020: 03 00 90 ff 5e 08 00 ff 00 00
分析如下:
-----------------------------SERIAL HEADER-----------------------------
0~3 6B 49 54 4C // "kITL"
4 AA // PacketType = OAL_KITL_SERIAL_PACKET
5 05 // reserced
6~7 20 00 // PayloadSize = cbData
8 4F // crcData
9 72 // crcHeader
-----------------------------KITL PACKET-----------------------------
10~13 45 44 42 47 // “kITL"
14 FF // service = KITL_SVC_ADMIN
15 00 // flags
16 00 // SeqNum
17 06 // cmd = KITL_CMD_SVC_CONFIG
18 01 // pCfg.ProtocolVersion = 1
19~24 44 42 47 4d 53 47 00 // ServiceName = "DBGMSG" + "\0"
25 2c // ServiceID
26 ff // WindowSize
27 5e // flags
28~ // 未知
如果用前面Traditional Serial串口监听的依据来看, PB只是例常性地循环发送0xA,0x6,0x6三个包而已, 并没有对我们发过去的0x9包作出响应.
关键点在于cmd = 0x6 (KITL_CMD_SVC_CONFIG), 这和上面ETHERNET及FFUART收到的CMD是不同的,这将导致ProcessAdminMsg进入不同的分支。而KITL_CMD_SVC_CONFIG这个分支感觉是Passive Kitl才会应该进来的, 在走到这步之前KITLClients[i]->State应该为KITL_CLIENT_REGISTERING状态,否则就当状态出错处理。而我们的Active Kitl即使是已经调通的Ethernet, 走到这里时也没有REGISTERING或REGISTERED状态。
USB SERIAL KITL就夭折于此地。
(PS,这代表当时的观点,最后还是把SERIAL KITL做出来了,问题出在读DATA REGISTER的之前要先判断一下LCR)