Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

kdcomio.c File Reference

#include "kdp.h"

Go to the source code of this file.

Functions

ULONG KdpComputeChecksum (IN PUCHAR Buffer, IN ULONG Length)
ULONG KdpReceiveString (OUT PCHAR Destination, IN ULONG Length)
VOID KdpSendString (IN PCHAR Source, IN ULONG Length)
VOID KdpSendControlPacket (IN USHORT PacketType, IN ULONG PacketId OPTIONAL)
USHORT KdpReceivePacketLeader (IN ULONG PacketType, OUT PULONG PacketLeader)
ULONG KdpReceivePacket (IN ULONG PacketType, OUT PSTRING MessageHeader, OUT PSTRING MessageData, OUT PULONG DataLength)
VOID KdpSendPacket (IN ULONG PacketType, IN PSTRING MessageHeader, IN PSTRING MessageData OPTIONAL)


Function Documentation

ULONG KdpComputeChecksum IN PUCHAR  Buffer,
IN ULONG  Length
 

Definition at line 60 of file kdcomio.c.

References Buffer.

00067 : 00068 00069 This routine computes the checksum for the string passed in. 00070 00071 Arguments: 00072 00073 Buffer - Supplies a pointer to the string. 00074 00075 Length - Supplies the length of the string. 00076 00077 Return Value: 00078 00079 A ULONG is return as the checksum for the input string. 00080 00081 --*/ 00082 00083 { 00084 00085 ULONG Checksum = 0; 00086 00087 while (Length > 0) { 00088 Checksum = Checksum + (ULONG)*Buffer++; 00089 Length--; 00090 } 00091 return Checksum; 00092 }

ULONG KdpReceivePacket IN ULONG  PacketType,
OUT PSTRING  MessageHeader,
OUT PSTRING  MessageData,
OUT PULONG  DataLength
 

Definition at line 342 of file kdcomio.c.

00351 : 00352 00353 This routine receives a packet from the host machine that is running 00354 the kernel debugger UI. This routine is ALWAYS called after packet being 00355 sent by caller. It first waits for ACK packet for the packet sent and 00356 then waits for the packet desired. 00357 00358 N.B. If caller is KdPrintString, the parameter PacketType is 00359 PACKET_TYPE_KD_ACKNOWLEDGE. In this case, this routine will return 00360 right after the ack packet is received. 00361 00362 Arguments: 00363 00364 PacketType - Supplies the type of packet that is excepted. 00365 00366 MessageHeader - Supplies a pointer to a string descriptor for the input 00367 message. 00368 00369 MessageData - Supplies a pointer to a string descriptor for the input data. 00370 00371 DataLength - Supplies pointer to ULONG to receive length of recv. data. 00372 00373 Return Value: 00374 00375 KDP_PACKET_RESEND - if resend is required. 00376 KDP_PAKCET_TIMEOUT - if timeout. 00377 KDP_PACKET_RECEIVED - if packet received. 00378 00379 --*/ 00380 00381 { 00382 00383 UCHAR Input; 00384 ULONG MessageLength; 00385 KD_PACKET PacketHeader; 00386 ULONG ReturnCode; 00387 ULONG Checksum; 00388 00389 WaitForPacketLeader: 00390 00391 // 00392 // Read Packet Leader 00393 // 00394 00395 ReturnCode = KdpReceivePacketLeader(PacketType, &PacketHeader.PacketLeader); 00396 00397 // 00398 // If we can successfully read packet leader, it has high possibility that 00399 // kernel debugger is alive. So reset count. 00400 // 00401 00402 if (ReturnCode != KDP_PACKET_TIMEOUT) { 00403 KdpNumberRetries = KdpRetryCount; 00404 } 00405 if (ReturnCode != KDP_PACKET_RECEIVED) { 00406 return ReturnCode; 00407 } 00408 00409 // 00410 // Read packet type. 00411 // 00412 00413 ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.PacketType, 00414 sizeof(PacketHeader.PacketType)); 00415 if (ReturnCode == CP_GET_NODATA) { 00416 return KDP_PACKET_TIMEOUT; 00417 } else if (ReturnCode == CP_GET_ERROR) { 00418 if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { 00419 00420 // 00421 // If read error and it is for a control packet, simply 00422 // preptend that we have not seen this packet. Hopefully 00423 // we will receive the packet we desire which automatically acks 00424 // the packet we just sent. 00425 // 00426 00427 goto WaitForPacketLeader; 00428 } else { 00429 00430 // 00431 // if read error while reading data packet, we have to ask 00432 // kernel debugger to resend us the packet. 00433 // 00434 00435 goto SendResendPacket; 00436 } 00437 } 00438 00439 // 00440 // if the packet we received is a resend request, we return true and 00441 // let caller resend the packet. 00442 // 00443 00444 if ( PacketHeader.PacketLeader == CONTROL_PACKET_LEADER && 00445 PacketHeader.PacketType == PACKET_TYPE_KD_RESEND ) { 00446 return KDP_PACKET_RESEND; 00447 } 00448 00449 // 00450 // Read data length. 00451 // 00452 00453 ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.ByteCount, 00454 sizeof(PacketHeader.ByteCount)); 00455 if (ReturnCode == CP_GET_NODATA) { 00456 return KDP_PACKET_TIMEOUT; 00457 } else if (ReturnCode == CP_GET_ERROR) { 00458 if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { 00459 goto WaitForPacketLeader; 00460 } else { 00461 goto SendResendPacket; 00462 } 00463 } 00464 00465 // 00466 // Read Packet Id. 00467 // 00468 00469 ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.PacketId, 00470 sizeof(PacketHeader.PacketId)); 00471 00472 if (ReturnCode == CP_GET_NODATA) { 00473 return KDP_PACKET_TIMEOUT; 00474 } else if (ReturnCode == CP_GET_ERROR) { 00475 if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { 00476 goto WaitForPacketLeader; 00477 } else { 00478 goto SendResendPacket; 00479 } 00480 } 00481 00482 // 00483 // Read packet checksum. 00484 // 00485 00486 ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.Checksum, 00487 sizeof(PacketHeader.Checksum)); 00488 if (ReturnCode == CP_GET_NODATA) { 00489 return KDP_PACKET_TIMEOUT; 00490 } else if (ReturnCode == CP_GET_ERROR) { 00491 if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { 00492 goto WaitForPacketLeader; 00493 } else { 00494 goto SendResendPacket; 00495 } 00496 } 00497 00498 // 00499 // A complete packet header is received. Check its validity and 00500 // perform appropriate action depending on packet type. 00501 // 00502 00503 if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER ) { 00504 if (PacketHeader.PacketType == PACKET_TYPE_KD_ACKNOWLEDGE ) { 00505 00506 // 00507 // If we received an expected ACK packet and we are not 00508 // waiting for any new packet, update outgoing packet id 00509 // and return. If we are NOT waiting for ACK packet 00510 // we will keep on waiting. If the ACK packet 00511 // is not for the packet we send, ignore it and keep on waiting. 00512 // 00513 00514 if (PacketHeader.PacketId != 00515 (KdpNextPacketIdToSend & ~SYNC_PACKET_ID)) { 00516 goto WaitForPacketLeader; 00517 } else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { 00518 KdpNextPacketIdToSend ^= 1; 00519 return KDP_PACKET_RECEIVED; 00520 } else { 00521 goto WaitForPacketLeader; 00522 } 00523 } else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESET) { 00524 00525 // 00526 // if we received Reset packet, reset the packet control variables 00527 // and resend earlier packet. 00528 // 00529 00530 KdpNextPacketIdToSend = INITIAL_PACKET_ID; 00531 KdpPacketIdExpected = INITIAL_PACKET_ID; 00532 KdpSendControlPacket(PACKET_TYPE_KD_RESET, 0L); 00533 return KDP_PACKET_RESEND; 00534 } else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) { 00535 return KDP_PACKET_RESEND; 00536 } else { 00537 00538 // 00539 // Invalid packet header, ignore it. 00540 // 00541 00542 goto WaitForPacketLeader; 00543 } 00544 00545 // 00546 // The packet header is for data packet (not control packet). 00547 // 00548 00549 } else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { 00550 00551 // 00552 // if we are waiting for ACK packet ONLY 00553 // and we receive a data packet header, check if the packet id 00554 // is what we expected. If yes, assume the acknowledge is lost (but 00555 // sent), ask sender to resend and return with PACKET_RECEIVED. 00556 // 00557 00558 if (PacketHeader.PacketId == KdpPacketIdExpected) { 00559 KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); 00560 KdpNextPacketIdToSend ^= 1; 00561 return KDP_PACKET_RECEIVED; 00562 } else { 00563 KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, 00564 PacketHeader.PacketId 00565 ); 00566 goto WaitForPacketLeader; 00567 } 00568 } 00569 00570 // 00571 // we are waiting for data packet and we received the packet header 00572 // for data packet. Perform the following checkings to make sure 00573 // it is the packet we are waiting for. 00574 // 00575 00576 // 00577 // Check ByteCount received is valid 00578 // 00579 00580 MessageLength = MessageHeader->MaximumLength; 00581 if ((PacketHeader.ByteCount > (USHORT)PACKET_MAX_SIZE) || 00582 (PacketHeader.ByteCount < (USHORT)MessageLength)) { 00583 goto SendResendPacket; 00584 } 00585 *DataLength = PacketHeader.ByteCount - MessageLength; 00586 00587 // 00588 // Read the message header. 00589 // 00590 00591 ReturnCode = KdpReceiveString(MessageHeader->Buffer, MessageLength); 00592 if (ReturnCode != CP_GET_SUCCESS) { 00593 goto SendResendPacket; 00594 } 00595 MessageHeader->Length = (USHORT)MessageLength; 00596 00597 // 00598 // Read the message data. 00599 // 00600 00601 ReturnCode = KdpReceiveString(MessageData->Buffer, *DataLength); 00602 if (ReturnCode != CP_GET_SUCCESS) { 00603 goto SendResendPacket; 00604 } 00605 MessageData->Length = (USHORT)*DataLength; 00606 00607 // 00608 // Read packet trailing byte 00609 // 00610 00611 ReturnCode = KdPortGetByte(&Input); 00612 if (ReturnCode != CP_GET_SUCCESS || Input != PACKET_TRAILING_BYTE) { 00613 goto SendResendPacket; 00614 } 00615 00616 // 00617 // Check PacketType is what we are waiting for. 00618 // 00619 00620 if (PacketType != PacketHeader.PacketType) { 00621 KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, 00622 PacketHeader.PacketId 00623 ); 00624 goto WaitForPacketLeader; 00625 } 00626 00627 // 00628 // Check PacketId is valid. 00629 // 00630 00631 if (PacketHeader.PacketId == INITIAL_PACKET_ID || 00632 PacketHeader.PacketId == (INITIAL_PACKET_ID ^ 1)) { 00633 if (PacketHeader.PacketId != KdpPacketIdExpected) { 00634 KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, 00635 PacketHeader.PacketId 00636 ); 00637 goto WaitForPacketLeader; 00638 } 00639 } else { 00640 goto SendResendPacket; 00641 } 00642 00643 // 00644 // Check checksum is valid. 00645 // 00646 00647 Checksum = KdpComputeChecksum( 00648 MessageHeader->Buffer, 00649 MessageHeader->Length 00650 ); 00651 00652 Checksum += KdpComputeChecksum( 00653 MessageData->Buffer, 00654 MessageData->Length 00655 ); 00656 if (Checksum != PacketHeader.Checksum) { 00657 goto SendResendPacket; 00658 } 00659 00660 // 00661 // Send Acknowledge byte and the Id of the packet received. 00662 // Then, update the ExpectId for next incoming packet. 00663 // 00664 00665 KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, 00666 PacketHeader.PacketId 00667 ); 00668 00669 // 00670 // We have successfully received the packet so update the 00671 // packet control variables and return sucess. 00672 // 00673 00674 KdpPacketIdExpected ^= 1; 00675 return KDP_PACKET_RECEIVED; 00676 00677 SendResendPacket: 00678 KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); 00679 goto WaitForPacketLeader; 00680 }

USHORT KdpReceivePacketLeader IN ULONG  PacketType,
OUT PULONG  PacketLeader
 

Definition at line 95 of file kdcomio.c.

00102 : 00103 00104 This routine waits for a packet header leader. 00105 00106 Arguments: 00107 00108 PacketType - supplies the type of packet we are expecting. 00109 00110 PacketLeader - supplies a pointer to a ulong variable to receive 00111 packet leader bytes. 00112 00113 Return Value: 00114 00115 KDP_PACKET_RESEND - if resend is required. 00116 KDP_PAKCET_TIMEOUT - if timeout. 00117 KDP_PACKET_RECEIVED - if packet received. 00118 00119 --*/ 00120 00121 { 00122 00123 UCHAR Input, PreviousByte = 0; 00124 ULONG PacketId = 0; 00125 ULONG Index; 00126 ULONG ReturnCode; 00127 BOOLEAN BreakinDetected = FALSE; 00128 00129 // 00130 // NOTE - With all the interrupts being off, it is very hard 00131 // to implement the actual timeout code. (Maybe, by reading the CMOS.) 00132 // Here we use a loop count to wait about 3 seconds. The CpGetByte 00133 // will return with error code = CP_GET_NODATA if it cannot find data 00134 // byte within 1 second. Kernel debugger's timeout period is 5 seconds. 00135 // 00136 00137 Index = 0; 00138 do { 00139 ReturnCode = KdPortGetByte(&Input); 00140 if (ReturnCode == CP_GET_NODATA) { 00141 if (BreakinDetected) { 00142 KdpControlCPending = TRUE; 00143 return KDP_PACKET_RESEND; 00144 } else { 00145 return KDP_PACKET_TIMEOUT; 00146 } 00147 } else if (ReturnCode == CP_GET_ERROR) { 00148 Index = 0; 00149 continue; 00150 } else { // if (ReturnCode == CP_GET_SUCCESS) 00151 if ( Input == PACKET_LEADER_BYTE || 00152 Input == CONTROL_PACKET_LEADER_BYTE ) { 00153 if ( Index == 0 ) { 00154 PreviousByte = Input; 00155 Index++; 00156 } else if (Input == PreviousByte ) { 00157 Index++; 00158 } else { 00159 PreviousByte = Input; 00160 Index = 1; 00161 } 00162 } else { 00163 00164 // 00165 // If we detect breakin character, we need to verify it 00166 // validity. (It is possible that we missed a packet leader 00167 // and the breakin character is simply a data byte in the 00168 // packet.) 00169 // Since kernel debugger send out breakin character ONLY 00170 // when it is waiting for State Change packet. The breakin 00171 // character should not be followed by any other character 00172 // except packet leader byte. 00173 // 00174 00175 if ( Input == BREAKIN_PACKET_BYTE ) { 00176 BreakinDetected = TRUE; 00177 } else { 00178 00179 // 00180 // The following statement is ABSOLUTELY necessary. 00181 // 00182 00183 BreakinDetected = FALSE; 00184 } 00185 Index = 0; 00186 } 00187 } 00188 } while ( Index < 4 ); 00189 00190 if (BreakinDetected) { 00191 KdpControlCPending = TRUE; 00192 } 00193 00194 // 00195 // return the packet leader and FALSE to indicate no resend is needed. 00196 // 00197 00198 if ( Input == PACKET_LEADER_BYTE ) { 00199 *PacketLeader = PACKET_LEADER; 00200 } else { 00201 *PacketLeader = CONTROL_PACKET_LEADER; 00202 } 00203 00204 KdDebuggerNotPresent = FALSE; 00205 return KDP_PACKET_RECEIVED; 00206 }

ULONG KdpReceiveString OUT PCHAR  Destination,
IN ULONG  Length
 

Definition at line 209 of file kdcomio.c.

References CP_GET_SUCCESS, and KdPortGetByte().

00216 : 00217 00218 This routine reads a string from the kernel debugger port. 00219 00220 Arguments: 00221 00222 Destination - Supplies a pointer to the input string. 00223 00224 Length - Supplies the length of the string to be read. 00225 00226 Return Value: 00227 00228 CP_GET_SUCCESS is returned if string is successfully read from the 00229 kernel debugger line. 00230 CP_GET_ERROR is returned if error encountered during reading. 00231 CP_GET_NODATA is returned if timeout. 00232 00233 --*/ 00234 00235 { 00236 00237 UCHAR Input; 00238 ULONG ReturnCode; 00239 00240 // 00241 // Read bytes until either a error is encountered or the entire string 00242 // has been read. 00243 // 00244 while (Length > 0) { 00245 ReturnCode = KdPortGetByte(&Input); 00246 if (ReturnCode != CP_GET_SUCCESS) { 00247 return ReturnCode; 00248 } else { 00249 *Destination++ = Input; 00250 Length -= 1; 00251 } 00252 } 00253 return CP_GET_SUCCESS; 00254 }

VOID KdpSendControlPacket IN USHORT  PacketType,
IN ULONG PacketId  OPTIONAL
 

Definition at line 297 of file kdcomio.c.

References KdpSendString().

00304 : 00305 00306 This routine sends a control packet to the host machine that is running the 00307 kernel debugger and waits for an ACK. 00308 00309 Arguments: 00310 00311 PacketType - Supplies the type of packet to send. 00312 00313 PacketId - Supplies packet id, optionally. 00314 00315 Return Value: 00316 00317 None. 00318 00319 --*/ 00320 00321 { 00322 00323 KD_PACKET PacketHeader; 00324 00325 // 00326 // Initialize and send the packet header. 00327 // 00328 00329 PacketHeader.PacketLeader = CONTROL_PACKET_LEADER; 00330 if (ARGUMENT_PRESENT(PacketId)) { 00331 PacketHeader.PacketId = PacketId; 00332 } 00333 PacketHeader.ByteCount = 0; 00334 PacketHeader.Checksum = 0; 00335 PacketHeader.PacketType = PacketType; 00336 KdpSendString((PCHAR)&PacketHeader, sizeof(KD_PACKET)); 00337 00338 return; 00339 }

VOID KdpSendPacket IN ULONG  PacketType,
IN PSTRING  MessageHeader,
IN PSTRING MessageData  OPTIONAL
 

Definition at line 683 of file kdcomio.c.

00691 : 00692 00693 This routine sends a packet to the host machine that is running the 00694 kernel debugger and waits for an ACK. 00695 00696 Arguments: 00697 00698 PacketType - Supplies the type of packet to send. 00699 00700 MessageHeader - Supplies a pointer to a string descriptor that describes 00701 the message information. 00702 00703 MessageData - Supplies a pointer to a string descriptor that describes 00704 the optional message data. 00705 00706 Return Value: 00707 00708 None. 00709 00710 --*/ 00711 00712 { 00713 00714 KD_PACKET PacketHeader; 00715 ULONG MessageDataLength; 00716 ULONG ReturnCode; 00717 PDBGKD_DEBUG_IO DebugIo; 00718 PDBGKD_WAIT_STATE_CHANGE StateChange; 00719 00720 if ( ARGUMENT_PRESENT(MessageData) ) { 00721 MessageDataLength = MessageData->Length; 00722 PacketHeader.Checksum = KdpComputeChecksum( 00723 MessageData->Buffer, 00724 MessageData->Length 00725 ); 00726 } else { 00727 MessageDataLength = 0; 00728 PacketHeader.Checksum = 0; 00729 } 00730 00731 PacketHeader.Checksum += KdpComputeChecksum ( 00732 MessageHeader->Buffer, 00733 MessageHeader->Length 00734 ); 00735 00736 // 00737 // Initialize and send the packet header. 00738 // 00739 00740 PacketHeader.PacketLeader = PACKET_LEADER; 00741 PacketHeader.ByteCount = (USHORT)(MessageHeader->Length + MessageDataLength); 00742 PacketHeader.PacketType = (USHORT)PacketType; 00743 KdpNumberRetries = KdpRetryCount; 00744 do { 00745 if (KdpNumberRetries == 0) { 00746 00747 // 00748 // If the packet is not for reporting exception, we give up 00749 // and declare debugger not present. 00750 // 00751 00752 if (PacketType == PACKET_TYPE_KD_DEBUG_IO) { 00753 DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer; 00754 if (DebugIo->ApiNumber == DbgKdPrintStringApi) { 00755 KdDebuggerNotPresent = TRUE; 00756 KdpNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; 00757 KdpPacketIdExpected = INITIAL_PACKET_ID; 00758 return; 00759 } 00760 } else if (PacketType == PACKET_TYPE_KD_STATE_CHANGE) { 00761 StateChange = (PDBGKD_WAIT_STATE_CHANGE)MessageHeader->Buffer; 00762 if (StateChange->NewState == DbgKdLoadSymbolsStateChange) { 00763 KdDebuggerNotPresent = TRUE; 00764 KdpNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; 00765 KdpPacketIdExpected = INITIAL_PACKET_ID; 00766 return; 00767 } 00768 } 00769 } 00770 00771 // 00772 // Setting PacketId has to be in the do loop in case Packet Id was 00773 // reset. 00774 // 00775 00776 PacketHeader.PacketId = KdpNextPacketIdToSend; 00777 KdpSendString((PCHAR)&PacketHeader, sizeof(KD_PACKET)); 00778 00779 // 00780 // Output message header. 00781 // 00782 00783 KdpSendString(MessageHeader->Buffer, MessageHeader->Length); 00784 00785 // 00786 // Output message data. 00787 // 00788 00789 if ( MessageDataLength ) { 00790 KdpSendString(MessageData->Buffer, MessageData->Length); 00791 } 00792 00793 // 00794 // Output a packet trailing byte 00795 // 00796 00797 KdPortPutByte(PACKET_TRAILING_BYTE); 00798 00799 // 00800 // Wait for the Ack Packet 00801 // 00802 00803 ReturnCode = KdpReceivePacket( 00804 PACKET_TYPE_KD_ACKNOWLEDGE, 00805 NULL, 00806 NULL, 00807 NULL 00808 ); 00809 if (ReturnCode == KDP_PACKET_TIMEOUT) { 00810 KdpNumberRetries--; 00811 } 00812 } while (ReturnCode != KDP_PACKET_RECEIVED); 00813 00814 // 00815 // Reset Sync bit in packet id. The packet we sent may have Sync bit set 00816 // 00817 00818 KdpNextPacketIdToSend &= ~SYNC_PACKET_ID; 00819 00820 // 00821 // Since we are able to talk to debugger, the retrycount is set to 00822 // maximum value. 00823 // 00824 00825 KdpRetryCount = MAXIMUM_RETRIES; 00826 } }

VOID KdpSendString IN PCHAR  Source,
IN ULONG  Length
 

Definition at line 257 of file kdcomio.c.

References KdPortPutByte().

00264 : 00265 00266 This routine writes a string to the kernel debugger port. 00267 00268 Arguments: 00269 00270 Source - Supplies a pointer to the output string. 00271 00272 Length - Supplies the length of the string to be written. 00273 00274 Return Value: 00275 00276 None. 00277 00278 --*/ 00279 00280 { 00281 00282 UCHAR Output; 00283 00284 // 00285 // Write bytes to the kernel debugger port. 00286 // 00287 00288 while (Length > 0) { 00289 Output = *Source++; 00290 KdPortPutByte(Output); 00291 Length -= 1; 00292 } 00293 return; 00294 }


Generated on Sat May 15 19:44:25 2004 for test by doxygen 1.3.7