//----------------------------------------------------------------------------
//
// KD hard-line communication support.
//
// Copyright (C) Microsoft Corporation, 1999-2001.
//
//----------------------------------------------------------------------------

#include "ntsdp.hpp"

#include <portio.h>

#define THROTTLE_WRITES 0
#define DBG_KD_READ 0
#define DBG_KD_WRITE 0
#define DBG_SYNCH 0

#define KD_FILE_SIGNATURE 'lFdK'

struct KD_FILE
{
    LIST_ENTRY List;
    HANDLE Handle;
    ULONG Signature;
};

struct KD_FILE_ASSOC
{
    LIST_ENTRY List;
    PWSTR From;
    PSTR To;
};

LIST_ENTRY g_KdFiles;
char g_KdFileAssocSource[MAX_PATH];
LIST_ENTRY g_KdFileAssoc;

ULONG g_LastProcessorToPrint = (ULONG) -1;
CHAR g_PrintBuf[PACKET_MAX_SIZE];

PCSTR g_DbgKdTransportNames[] =
{
    "com", "1394"
};

DbgKdTransport* g_DbgKdTransport;
DbgKdComTransport g_DbgKdComTransport;
DbgKd1394Transport g_DbgKd1394Transport;

// This log is for debugging the protocol so leave it
// a simple global for easy examination.
ULONG g_PacketLogIndex;
ULONG64 g_PacketLog[16];

#define PACKET_LOG_SIZE (sizeof(g_PacketLog) / sizeof(g_PacketLog[0]))

UCHAR DbgKdTransport::s_BreakinPacket[1] =
{
    BREAKIN_PACKET_BYTE
};

UCHAR DbgKdTransport::s_PacketTrailingByte[1] =
{
    PACKET_TRAILING_BYTE
};

UCHAR DbgKdTransport::s_PacketLeader[4] =
{
    PACKET_LEADER_BYTE,
    PACKET_LEADER_BYTE,
    PACKET_LEADER_BYTE,
    PACKET_LEADER_BYTE
};

UCHAR DbgKdTransport::s_Packet[PACKET_MAX_MANIP_SIZE];
KD_PACKET DbgKdTransport::s_PacketHeader;

UCHAR DbgKdTransport::s_SavedPacket[PACKET_MAX_MANIP_SIZE];
KD_PACKET DbgKdTransport::s_SavedPacketHeader;

#define COPYSE(p64,p32,f) p64->f = (ULONG64)(LONG64)(LONG)p32->f

__inline
void
DbgkdGetVersion32To64(
    IN PDBGKD_GET_VERSION32 vs32,
    OUT PDBGKD_GET_VERSION64 vs64,
    OUT PKDDEBUGGER_DATA64 dd64
    )
{
    vs64->MajorVersion = vs32->MajorVersion;
    vs64->MinorVersion = vs32->MinorVersion;
    vs64->ProtocolVersion = vs32->ProtocolVersion;
    vs64->Flags = vs32->Flags;
    vs64->MachineType = vs32->MachineType;
    COPYSE(vs64,vs32,PsLoadedModuleList);
    COPYSE(vs64,vs32,DebuggerDataList);
    COPYSE(vs64,vs32,KernBase);

    COPYSE(dd64,vs32,KernBase);
    COPYSE(dd64,vs32,PsLoadedModuleList);
    dd64->ThCallbackStack = vs32->ThCallbackStack;
    dd64->NextCallback = vs32->NextCallback;
    dd64->FramePointer = vs32->FramePointer;
    COPYSE(dd64,vs32,KiCallUserMode);
    COPYSE(dd64,vs32,KeUserCallbackDispatcher);
    COPYSE(dd64,vs32,BreakpointWithStatus);
}

void
OutputIo(PSTR Format, PVOID _Buffer, ULONG Request, ULONG Done)
{
    ULONG i, Chunk;
    PUCHAR Buffer = (PUCHAR)_Buffer;

    dprintf(Format, Done, Request);
    while (Done > 0)
    {
        Chunk = min(Done, 16);
        Done -= Chunk;
        dprintf("   ");
        for (i = 0; i < Chunk; i++)
        {
            dprintf(" %02X", *Buffer++);
        }
        dprintf("\n");
    }
}

KD_FILE_ASSOC*
FindKdFileAssoc(PWSTR From)
{
    PLIST_ENTRY Entry;
    KD_FILE_ASSOC* Assoc;

    for (Entry = g_KdFileAssoc.Flink;
         Entry != &g_KdFileAssoc;
         Entry = Entry->Flink)
    {
        Assoc = CONTAINING_RECORD(Entry, KD_FILE_ASSOC, List);

        if (!_wcsicmp(From, Assoc->From))
        {
            return Assoc;
        }
    }

    return NULL;
}

void
ClearKdFileAssoc(void)
{
    while (!IsListEmpty(&g_KdFileAssoc))
    {
        KD_FILE_ASSOC* Assoc;

        Assoc = CONTAINING_RECORD(g_KdFileAssoc.Flink, KD_FILE_ASSOC, List);
        RemoveEntryList(&Assoc->List);
        free(Assoc);
    }

    g_KdFileAssocSource[0] = 0;
}

HRESULT
LoadKdFileAssoc(PSTR FileName)
{
    HRESULT Status;
    FILE* File;
    char Op[32], From[MAX_PATH], To[MAX_PATH];

    File = fopen(FileName, "r");
    if (File == NULL)
    {
        return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
    }
    
    ClearKdFileAssoc();

    Status = S_OK;
    for (;;)
    {
        if (fgets(Op, sizeof(Op), File) == NULL)
        {
            break;
        }
        // Remove newline.
        Op[strlen(Op) - 1] = 0;

        if (_stricmp(Op, "map") != 0)
        {
            Status = E_INVALIDARG;
            break;
        }
        
        if (fgets(From, sizeof(From), File) == NULL ||
            fgets(To, sizeof(To), File) == NULL)
        {
            Status = E_INVALIDARG;
            break;
        }
        // Remove newlines.
        From[strlen(From) - 1] = 0;
        To[strlen(To) - 1] = 0;

        KD_FILE_ASSOC* Assoc;

        Assoc = (KD_FILE_ASSOC*)malloc(sizeof(KD_FILE_ASSOC) +
                                       (strlen(From) + 1) * sizeof(WCHAR) +
                                       strlen(To) + 1);
        if (Assoc == NULL)
        {
            Status = E_OUTOFMEMORY;
            break;
        }

        Assoc->From = (PWSTR)(Assoc + 1);
        if (MultiByteToWideChar(CP_ACP, 0, From, -1, Assoc->From,
                                sizeof(From) / sizeof(WCHAR)) == 0)
        {
            Status = WIN32_LAST_STATUS();
            break;
        }
        
        Assoc->To = (PSTR)(Assoc->From + strlen(From) + 1);
        strcpy(Assoc->To, To);

        InsertHeadList(&g_KdFileAssoc, &Assoc->List);
    }
    
    fclose(File);

    if (Status == S_OK)
    {
        strncat(g_KdFileAssocSource, FileName,
                sizeof(g_KdFileAssocSource) - 1);
    }
    
    return Status;
}

void
InitKdFileAssoc(void)
{
    PSTR Env;
    
    InitializeListHead(&g_KdFileAssoc);

    Env = getenv("_NT_KD_FILES");
    if (Env != NULL)
    {
        LoadKdFileAssoc(Env);
    }
}

void
ParseKdFileAssoc(void)
{
    if (PeekChar() == ';' || *g_CurCmd == 0)
    {
        if (g_KdFileAssocSource[0])
        {
            dprintf("KD file assocations loaded from '%s'\n",
                    g_KdFileAssocSource);
        }
        else
        {
            dprintf("No KD file associations set\n");
        }
        return;
    }

    while (PeekChar() == '-' || *g_CurCmd == '/')
    {
        g_CurCmd++;
        switch(*g_CurCmd++)
        {
        case 'c':
            ClearKdFileAssoc();
            dprintf("KD file associations cleared\n");
            return;
        default:
            ErrOut("Unknown option '%c'\n", *(g_CurCmd - 1));
            break;
        }
    }
            
    PSTR FileName;
    CHAR Save;

    FileName = StringValue(STRV_TRIM_TRAILING_SPACE, &Save);
    if (LoadKdFileAssoc(FileName) == S_OK)
    {
        dprintf("KD file assocations loaded from '%s'\n", FileName);
    }
    else
    {
        dprintf("Unable to load KD file associations from '%s'\n", FileName);
    }
    *g_CurCmd = Save;
}

NTSTATUS
CreateKdFile(PWSTR FileName,
             ULONG DesiredAccess, ULONG FileAttributes,
             ULONG ShareAccess, ULONG CreateDisposition,
             ULONG CreateOptions,
             KD_FILE** FileEntry, PULONG64 Length)
{
    ULONG Access, Create;
    KD_FILE* File;
    KD_FILE_ASSOC* Assoc;

    Assoc = FindKdFileAssoc(FileName);
    if (Assoc == NULL)
    {
        return STATUS_NO_SUCH_FILE;
    }
    
    File = new KD_FILE;
    if (File == NULL)
    {
        return STATUS_NO_MEMORY;
    }
    
    Access = 0;
    if (DesiredAccess & FILE_GENERIC_READ)
    {
        Access |= GENERIC_READ;
    }
    if (DesiredAccess & FILE_GENERIC_WRITE)
    {
        Access |= GENERIC_WRITE;
    }

    switch(CreateDisposition)
    {
    case FILE_OPEN:
        Create = OPEN_EXISTING;
        break;
    case FILE_CREATE:
        Create = CREATE_NEW;
        break;
    case FILE_OPEN_IF:
        Create = OPEN_ALWAYS;
        break;
    case FILE_OVERWRITE_IF:
        Create = CREATE_ALWAYS;
        break;
    default:
        delete File;
        return STATUS_INVALID_PARAMETER;
    }

    // No interesting CreateOptions at this point.

    File->Handle = CreateFile(Assoc->To, Access, ShareAccess, NULL,
                              Create, FileAttributes, NULL);
    if (File->Handle == NULL || File->Handle == INVALID_HANDLE_VALUE)
    {
        delete File;
        switch(GetLastError())
        {
        case ERROR_FILE_NOT_FOUND:
            return STATUS_NO_SUCH_FILE;
        case ERROR_ACCESS_DENIED:
            return STATUS_ACCESS_DENIED;
        default:
            return STATUS_UNSUCCESSFUL;
        }
    }

    ULONG SizeLow;
    LONG SizeHigh = 0;

    SizeLow = SetFilePointer(File->Handle, 0, &SizeHigh, FILE_END);
    if (SizeLow == INVALID_SET_FILE_POINTER && GetLastError())
    {
        CloseHandle(File->Handle);
        delete File;
        return STATUS_UNSUCCESSFUL;
    }

    *Length = ((ULONG64)SizeHigh << 32) | SizeLow;

    dprintf("KD: Accessing '%s' (%ws)\n  ", Assoc->To, FileName);
    if (*Length > 0)
    {
        dprintf("File size %dK", KBYTES(*Length));
    }
    // Progress dots will be printed for each read/write.
    
    File->Signature = KD_FILE_SIGNATURE;
    InsertHeadList(&g_KdFiles, &File->List);
    *FileEntry = File;
    return STATUS_SUCCESS;
}

void
CloseKdFile(KD_FILE* File)
{
    RemoveEntryList(&File->List);
    CloseHandle(File->Handle);
    File->Signature = 0;
    delete File;
}

KD_FILE*
TranslateKdFileHandle(ULONG64 Handle)
{
    KD_FILE* File = (KD_FILE*)(ULONG_PTR)Handle;

    if (IsBadWritePtr(File, sizeof(*File)) ||
        File->Signature != KD_FILE_SIGNATURE)
    {
        return NULL;
    }

    return File;
}

//----------------------------------------------------------------------------
//
// DbgKdTransport.
//
//----------------------------------------------------------------------------

void
DbgKdTransport::Restart(void)
{
    //
    // Reinitialize per-connection values.
    //

    while (!IsListEmpty(&g_KdFiles))
    {
        CloseKdFile(CONTAINING_RECORD(g_KdFiles.Flink, KD_FILE, List));
    }
        
    m_PacketsRead = 0;
    m_BytesRead = 0;
    m_PacketsWritten = 0;
    m_BytesWritten = 0;
    
    m_PacketExpected = INITIAL_PACKET_ID;
    m_NextPacketToSend = INITIAL_PACKET_ID;
    
    m_WaitingThread = 0;
    
    m_AllowInitialBreak = TRUE;
    m_Resync = TRUE;
    m_BreakIn = FALSE;
    m_SyncBreakIn = FALSE;
    m_ValidUnaccessedPacket = FALSE;
}

void
DbgKdTransport::OutputInfo(void)
{
    char Params[2 * (MAX_PARAM_NAME + MAX_PARAM_VALUE)];
    
    g_DbgKdTransport->GetParameters(Params, sizeof(Params));
    dprintf("Transport %s\n", Params);
    dprintf("Packets read: %u, bytes read %I64u\n",
            m_PacketsRead, m_BytesRead);
    dprintf("Packets written: %u, bytes written %I64u\n",
            m_PacketsWritten, m_BytesWritten);
}

HRESULT
DbgKdTransport::Initialize(void)
{
    HRESULT Status;

    //
    // Create the events used by the overlapped structures for the
    // read and write.
    //

    if ((Status = CreateOverlappedPair(&m_ReadOverlapped,
                                       &m_WriteOverlapped)) != S_OK)
    {
        ErrOut("Unable to create overlapped info, 0x%X\n", Status);
    }

    InitializeListHead(&g_KdFiles);
    
    return Status;
}

void
DbgKdTransport::Uninitialize(void)
{
    if (m_ReadOverlapped.hEvent != NULL)
    {
        CloseHandle(m_ReadOverlapped.hEvent);
        m_ReadOverlapped.hEvent = NULL;
    }
    if (m_WriteOverlapped.hEvent != NULL)
    {
        CloseHandle(m_WriteOverlapped.hEvent);
        m_WriteOverlapped.hEvent = NULL;
    }
}

void
DbgKdTransport::CycleSpeed(void)
{
    WarnOut("KD transport cannot change speeds\n");
}

HRESULT
DbgKdTransport::ReadTargetPhysicalMemory(
    IN ULONG64 MemoryOffset,
    IN PVOID Buffer,
    IN ULONG SizeofBuffer,
    IN PULONG BytesRead
    )
{
    WarnOut("Not valid KD transport operation\n");
    return E_UNEXPECTED;
}

ULONG
DbgKdTransport::HandleDebugIo(PDBGKD_DEBUG_IO Packet)
{
    ULONG ReadStatus = DBGKD_WAIT_AGAIN;

    switch(Packet->ApiNumber)
    {
    case DbgKdPrintStringApi:
        DbgKdpPrint(Packet->Processor,
                    (PSTR)(Packet + 1),
                    (SHORT)Packet->u.PrintString.LengthOfString,
                    DEBUG_OUTPUT_DEBUGGEE);
        break;
    case DbgKdGetStringApi:
        DbgKdpHandlePromptString(Packet);
        break;
    default:
        KdOut("READ: Received INVALID DEBUG_IO packet type %x.\n",
              Packet->ApiNumber);
        ReadStatus = DBGKD_WAIT_RESEND;
        break;
    }

    return ReadStatus;
}

ULONG
DbgKdTransport::HandleTraceIo(PDBGKD_TRACE_IO Packet)
{
    ULONG ReadStatus = DBGKD_WAIT_AGAIN;
            
    switch(Packet->ApiNumber)
    {
    case DbgKdPrintTraceApi:
        DbgKdpPrintTrace(Packet->Processor,
                         (PUCHAR)(Packet + 1),
                         (USHORT)Packet->u.PrintTrace.LengthOfData,
                         DEBUG_OUTPUT_DEBUGGEE);
        break;
    default:
        KdOut("READ: Received INVALID TRACE_IO packet type %x.\n",
              Packet->ApiNumber);
        ReadStatus = DBGKD_WAIT_RESEND;
        break;
    }

    return ReadStatus;
}

ULONG
DbgKdTransport::HandleControlRequest(PDBGKD_CONTROL_REQUEST Packet)
{
    ULONG ReadStatus = DBGKD_WAIT_AGAIN;
            
    switch(Packet->ApiNumber)
    {
    case DbgKdRequestHardwareBp:
        DbgKdpAcquireHardwareBp(Packet);
        break;
    case DbgKdReleaseHardwareBp:
        DbgKdpReleaseHardwareBp(Packet);
        break;
    default:
        KdOut("READ: Received INVALID CONTROL_REQUEST packet type %x.\n",
              Packet->ApiNumber);
        ReadStatus = DBGKD_WAIT_RESEND;
        break;
    }

    return ReadStatus;
}

ULONG
DbgKdTransport::HandleFileIo(PDBGKD_FILE_IO Packet)
{
    KD_FILE* File;
    PVOID ExtraData = NULL;
    USHORT ExtraDataLength = 0;
    LARGE_INTEGER FilePtr;

    // Reenter the engine lock to protect the file list.
    RESUME_ENGINE();
    
    switch(Packet->ApiNumber)
    {
    case DbgKdCreateFileApi:
        Packet->Status = CreateKdFile((PWSTR)(Packet + 1),
                                      Packet->u.CreateFile.DesiredAccess,
                                      Packet->u.CreateFile.FileAttributes,
                                      Packet->u.CreateFile.ShareAccess,
                                      Packet->u.CreateFile.CreateDisposition,
                                      Packet->u.CreateFile.CreateOptions,
                                      &File,
                                      &Packet->u.CreateFile.Length);
        Packet->u.CreateFile.Handle = (ULONG_PTR)File;
        KdOut("KdFile request for '%ws' returns %08X\n",
              (PWSTR)(Packet + 1), Packet->Status);
        break;
    case DbgKdReadFileApi:
        File = TranslateKdFileHandle(Packet->u.ReadFile.Handle);
        if (File == NULL ||
            Packet->u.ReadFile.Length > PACKET_MAX_SIZE - sizeof(*Packet))
        {
            Packet->Status = STATUS_INVALID_PARAMETER;
            break;
        }
        FilePtr.QuadPart = Packet->u.ReadFile.Offset;
        if (SetFilePointer(File->Handle, FilePtr.LowPart, &FilePtr.HighPart,
                           FILE_BEGIN) == INVALID_SET_FILE_POINTER &&
            GetLastError())
        {
            Packet->Status = STATUS_END_OF_FILE;
            break;
        }
        if (!ReadFile(File->Handle, Packet + 1, Packet->u.ReadFile.Length,
                      &Packet->u.ReadFile.Length, NULL))
        {
            Packet->Status = STATUS_UNSUCCESSFUL;
        }
        else
        {
            dprintf(".");
            Packet->Status = STATUS_SUCCESS;
            ExtraData = Packet + 1;
            ExtraDataLength = (USHORT)Packet->u.ReadFile.Length;
        }
        break;
    case DbgKdWriteFileApi:
        File = TranslateKdFileHandle(Packet->u.WriteFile.Handle);
        if (File == NULL ||
            Packet->u.WriteFile.Length > PACKET_MAX_SIZE - sizeof(*Packet))
        {
            Packet->Status = STATUS_INVALID_PARAMETER;
            break;
        }
        FilePtr.QuadPart = Packet->u.WriteFile.Offset;
        if (SetFilePointer(File->Handle, FilePtr.LowPart, &FilePtr.HighPart,
                           FILE_BEGIN) == INVALID_SET_FILE_POINTER &&
            GetLastError())
        {
            Packet->Status = STATUS_END_OF_FILE;
            break;
        }
        if (!WriteFile(File->Handle, Packet + 1, Packet->u.WriteFile.Length,
                      &Packet->u.WriteFile.Length, NULL))
        {
            Packet->Status = STATUS_UNSUCCESSFUL;
        }
        else
        {
            dprintf(".");
            Packet->Status = STATUS_SUCCESS;
        }
        break;
    case DbgKdCloseFileApi:
        File = TranslateKdFileHandle(Packet->u.CloseFile.Handle);
        if (File != NULL)
        {
            // Finish line of progress dots.
            dprintf("\n");
            CloseKdFile(File);
            Packet->Status = STATUS_SUCCESS;
        }
        else
        {
            Packet->Status = STATUS_INVALID_PARAMETER;
        }
        break;
    default:
        KdOut("READ: Received INVALID FILE_IO packet type %x.\n",
              Packet->ApiNumber);
        SUSPEND_ENGINE();
        return DBGKD_WAIT_RESEND;
    }

    //
    // Send response data.
    //

    g_DbgKdTransport->WritePacket(Packet, sizeof(*Packet),
                                  PACKET_TYPE_KD_FILE_IO,
                                  ExtraData, ExtraDataLength);

    SUSPEND_ENGINE();
    return DBGKD_WAIT_AGAIN;
}

ULONG
DbgKdTransport::WaitForPacket(
    IN USHORT PacketType,
    OUT PVOID Packet
    )
{
    ULONG InvPacketRetry = 0;

    // Packets can only be read when the kernel transport
    // is not in use.
    if (m_WaitingThread != 0 &&
        m_WaitingThread != GetCurrentThreadId())
    {
        ErrOut("Kernel transport in use, packet read failed\n");
        return DBGKD_WAIT_FAILED;
    }
    
    if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE)
    {
        KdOut("READ: Wait for ACK packet with id = %lx\n",
              m_NextPacketToSend);
    }
    else
    {
        KdOut("READ: Wait for type %x packet exp id = %lx\n",
              PacketType, m_PacketExpected);
    }

    g_PacketLog[g_PacketLogIndex++ & (PACKET_LOG_SIZE - 1)] =
        ((ULONG64)PacketType << 32);

    if (PacketType != PACKET_TYPE_KD_ACKNOWLEDGE)
    {
        if (m_ValidUnaccessedPacket)
        {
            KdOut("READ: Grab packet from buffer.\n");
            goto ReadBuffered;
        }
    }

 ReadContents:
    
    for (;;)
    {
        ULONG ReadStatus = ReadPacketContents(PacketType);

        //
        // If we read an internal packet such as IO or Resend, then
        // handle it and continue waiting.
        //
        if (ReadStatus == DBGKD_WAIT_PACKET)
        {
            m_PacketsRead++;

            switch(s_PacketHeader.PacketType)
            {
            case PACKET_TYPE_KD_DEBUG_IO:
                ReadStatus = HandleDebugIo((PDBGKD_DEBUG_IO)s_Packet);
                break;
            case PACKET_TYPE_KD_TRACE_IO:
                ReadStatus = HandleTraceIo((PDBGKD_TRACE_IO)s_Packet);
                break;
            case PACKET_TYPE_KD_CONTROL_REQUEST:
                ReadStatus =
                    HandleControlRequest((PDBGKD_CONTROL_REQUEST)s_Packet);
                break;
            case PACKET_TYPE_KD_FILE_IO:
                ReadStatus = HandleFileIo((PDBGKD_FILE_IO)s_Packet);
                break;
            }
        }
        else if (ReadStatus == DBGKD_WAIT_ACK)
        {
            m_PacketsRead++;
            
            // If we're waiting for an ack we're done,
            // otherwise the communication is confused
            // so ask for a resend.
            if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE)
            {
                return DBGKD_WAIT_ACK;
            }
            else
            {
                KdOut("READ: Received ACK while waiting for type %d\n",
                      PacketType);
                ReadStatus = DBGKD_WAIT_RESEND;
            }
        }
        
        if (ReadStatus == DBGKD_WAIT_PACKET)
        {
            // If we're waiting for an ack and received
            // a normal packet leave it in the buffer
            // and record the fact that we have one
            // stored.  Consider it an ack and return.
            if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE)
            {
                m_ValidUnaccessedPacket = TRUE;

                KdOut("READ: Packet Read ahead.\n");
                FlushCallbacks();

                return DBGKD_WAIT_ACK;
            }

            // We're waiting for a data packet and we
            // just got one so process it.
            break;
        }
        else if (ReadStatus == DBGKD_WAIT_RESEND)
        {
            // If the other end didn't wait for an
            // ack then we can't ask for a resend.
            if (!m_AckWrites)
            {
                return DBGKD_WAIT_FAILED;
            }
            
            WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
            if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE)
            {
                return DBGKD_WAIT_ACK;
            }

            KdOut("READ: Ask for resend.\n");
        }
        else if (ReadStatus == DBGKD_WAIT_AGAIN)
        {
            // Internal packets count as acknowledgements,
            // so if we processed one while waiting for an
            // ack consider things done.
            if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE)
            {
                return DBGKD_WAIT_ACK;
            }
        }
        else
        {
            return ReadStatus;
        }
    }
    
 ReadBuffered:
    
    //
    // Check PacketType is what we are waiting for.
    //

    if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64)
    {
        if (s_PacketHeader.PacketType == PACKET_TYPE_KD_STATE_CHANGE64)
        {
            DbgKdApi64 = TRUE;
        }
        else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_STATE_CHANGE32)
        {
            PacketType = PACKET_TYPE_KD_STATE_CHANGE32;
            DbgKdApi64 = FALSE;
        }

        KdOut("READ: Packet type = %x, DbgKdApi64 = %x\n",
              s_PacketHeader.PacketType, DbgKdApi64);
    }

    if (PacketType != s_PacketHeader.PacketType)
    {
        KdOut("READ: Unexpected Packet type %x (Acked). "
              "Expecting Packet type %x\n",
              s_PacketHeader.PacketType, PacketType);

        if (m_InvPacketRetryLimit > 0 &&
            ++InvPacketRetry >= m_InvPacketRetryLimit)
        {
            return DBGKD_WAIT_FAILED;
        }
        
        goto ReadContents;
    }

    if (!DbgKdApi64 && PacketType == PACKET_TYPE_KD_STATE_MANIPULATE)
    {
        DBGKD_MANIPULATE_STATE64 Packet64;
        DWORD AdditionalDataSize;
        
        DbgkdManipulateState32To64((PDBGKD_MANIPULATE_STATE32)&s_Packet,
                                   &Packet64, &AdditionalDataSize);
        if (Packet64.ApiNumber == DbgKdGetVersionApi)
        {
            DbgkdGetVersion32To64(&((PDBGKD_MANIPULATE_STATE32)&s_Packet)->
                                  u.GetVersion32,
                                  &Packet64.u.GetVersion64,
                                  &KdDebuggerData);
        }
        else if (AdditionalDataSize)
        {
            //
            // Move the trailing data to make room for the larger packet header
            //
            MoveMemory(s_Packet + sizeof(DBGKD_MANIPULATE_STATE64),
                       s_Packet + sizeof(DBGKD_MANIPULATE_STATE32),
                       AdditionalDataSize);
        }
        *(PDBGKD_MANIPULATE_STATE64)s_Packet = Packet64;
    }
    
    *(PVOID *)Packet = &s_Packet;
    m_ValidUnaccessedPacket = FALSE;
    return DBGKD_WAIT_PACKET;
}

VOID
DbgKdTransport::WriteBreakInPacket(VOID)
{
    DWORD BytesWritten;
    BOOL rc;

    KdOut("Send Break in ...\n");
    FlushCallbacks();

    do
    {
        rc = Write(&s_BreakinPacket[0], sizeof(s_BreakinPacket),
                   &BytesWritten);
    } while ((!rc) || (BytesWritten != sizeof(s_BreakinPacket)));
    
    m_BreakIn = FALSE;
    m_PacketsWritten++;
}

VOID
DbgKdTransport::WriteControlPacket(
    IN USHORT PacketType,
    IN ULONG PacketId OPTIONAL
    )

/*++

Routine Description:

    This function writes a control packet to target machine.

    N.B. a CONTROL Packet header is sent with the following information:
         PacketLeader - indicates it's a control packet
         PacketType - indicates the type of the control packet
         ByteCount - aways zero to indicate no data following the header
         PacketId - Valid ONLY for PACKET_TYPE_KD_ACKNOWLEDGE to indicate
                    which packet is acknowledged.

Arguments:

    PacketType - Supplies the type of the control packet.

    PacketId - Supplies the PacketId.  Used by Acknowledge packet only.

Return Value:

    None.

--*/
{
    DWORD BytesWritten;
    BOOL rc;
    KD_PACKET Packet;

    DBG_ASSERT( (g_KdMaxPacketType == 0 && PacketType < PACKET_TYPE_MAX) ||
                (g_KdMaxPacketType > 0 && PacketType < g_KdMaxPacketType) );

    Packet.PacketLeader = CONTROL_PACKET_LEADER;
    Packet.ByteCount = 0;
    Packet.PacketType = PacketType;
    if ( PacketId )
    {
        Packet.PacketId = PacketId;
    }
    else
    {
        Packet.PacketId = 0;
    }
    Packet.Checksum = 0;

    do
    {
        //
        // Write the control packet header
        //

        rc = Write(&Packet, sizeof(Packet), &BytesWritten);
    } while ( (!rc) || BytesWritten != sizeof(Packet) );

    m_PacketsWritten++;
}

VOID
DbgKdTransport::WriteDataPacket(
    IN PVOID PacketData,
    IN USHORT PacketDataLength,
    IN USHORT PacketType,
    IN PVOID MorePacketData OPTIONAL,
    IN USHORT MorePacketDataLength OPTIONAL,
    IN BOOL NoAck
    )
{
    KD_PACKET Packet;
    USHORT TotalBytesToWrite;
    DBGKD_MANIPULATE_STATE32 m32;
    PVOID ConvertedPacketData = NULL;

    DBG_ASSERT( (g_KdMaxPacketType == 0 && PacketType < PACKET_TYPE_MAX) ||
                (g_KdMaxPacketType > 0 && PacketType < g_KdMaxPacketType) );

    // Packets can only be written when the kernel transport
    // is not in use.
    if (m_WaitingThread != 0 &&
        m_WaitingThread != GetCurrentThreadId())
    {
        ErrOut("Kernel transport in use, packet write failed\n");
        return;
    }
    
    KdOut("WRITE: Write type %x packet id= %lx.\n",
          PacketType, m_NextPacketToSend);

    if (!DbgKdApi64 && PacketType == PACKET_TYPE_KD_STATE_MANIPULATE)
    {
        PacketDataLength = (USHORT)
            DbgkdManipulateState64To32((PDBGKD_MANIPULATE_STATE64)PacketData,
                                       &m32);
        PacketData = (PVOID)&m32;
        if (m32.ApiNumber == DbgKdWriteBreakPointExApi)
        {
            ConvertedPacketData = malloc(MorePacketDataLength / 2);
            if (!ConvertedPacketData)
            {
                ErrOut("Failed to allocate Packet Data\n");
                return;
            }
            ConvertQwordsToDwords((PULONG64)PacketData,
                                  (PULONG)ConvertedPacketData,
                                  MorePacketDataLength / 8);
            MorePacketData = ConvertedPacketData;
            MorePacketDataLength /= 2;
        }
    }

    if ( ARGUMENT_PRESENT(MorePacketData) )
    {
        TotalBytesToWrite = PacketDataLength + MorePacketDataLength;
        Packet.Checksum = ComputeChecksum((PUCHAR)MorePacketData,
                                          MorePacketDataLength);
    }
    else
    {
        TotalBytesToWrite = PacketDataLength;
        Packet.Checksum = 0;
    }
    Packet.Checksum += ComputeChecksum((PUCHAR)PacketData,
                                       PacketDataLength);
    Packet.PacketLeader = PACKET_LEADER;
    Packet.ByteCount = TotalBytesToWrite;
    Packet.PacketType = PacketType;

    g_PacketLog[g_PacketLogIndex++ & (PACKET_LOG_SIZE - 1)] =
        ((ULONG64)0xF << 60) | ((ULONG64)PacketType << 32) | TotalBytesToWrite;

    for (;;)
    {
        Packet.PacketId = m_NextPacketToSend;

        if (WritePacketContents(&Packet, PacketData, PacketDataLength,
                                MorePacketData, MorePacketDataLength,
                                NoAck) == DBGKD_WRITE_PACKET)
        {
            m_PacketsWritten++;
            break;
        }
    }

    if (ConvertedPacketData)
    {
        free(ConvertedPacketData);
    }
}

ULONG
DbgKdTransport::ComputeChecksum(
    IN PUCHAR Buffer,
    IN ULONG Length
    )
{
    ULONG Checksum = 0;

    while (Length > 0)
    {
        Checksum = Checksum + (ULONG)*Buffer++;
        Length--;
    }
    
    return Checksum;
}

//----------------------------------------------------------------------------
//
// DbgKdComTransport.
//
//----------------------------------------------------------------------------

// Environment variable names.
#define COM_PORT_NAME   "_NT_DEBUG_PORT"
#define COM_PORT_BAUD   "_NT_DEBUG_BAUD_RATE"

// Parameter string names.
#define PARAM_COM_PORT    "Port"
#define PARAM_COM_BAUD    "Baud"
#define PARAM_COM_MODEM   "Modem"
#define PARAM_COM_TIMEOUT "Timeout"

DbgKdComTransport::DbgKdComTransport(void)
{
    m_Index = DBGKD_TRANSPORT_COM;
    m_Name = g_DbgKdTransportNames[m_Index];
    m_InvPacketRetryLimit = 0;
    m_AckWrites = TRUE;
}

ULONG
DbgKdComTransport::GetNumberParameters(void)
{
    return 4;
}

void
DbgKdComTransport::GetParameter(ULONG Index, PSTR Name, PSTR Value)
{
    switch(Index)
    {
    case 0:
        strcpy(Name, PARAM_COM_PORT);
        strcpy(Value, m_PortName);
        break;
    case 1:
        strcpy(Name, PARAM_COM_BAUD);
        sprintf(Value, "%d", m_BaudRate);
        break;
    case 2:
        if (m_Modem)
        {
            strcpy(Name, PARAM_COM_MODEM);
        }
        break;
    case 3:
        strcpy(Name, PARAM_COM_TIMEOUT);
        sprintf(Value, "%d", m_Timeout);
        break;
    }
}

void
DbgKdComTransport::ResetParameters(void)
{
    PSTR Env;
    
    if ((Env = getenv(COM_PORT_NAME)) == NULL)
    {
        Env = "com1";
    }
    SetComPortName(Env, m_PortName);
    
    if ((Env = getenv(COM_PORT_BAUD)) != NULL)
    {
        m_BaudRate = atol(Env);
    }
    else
    {
        m_BaudRate = 19200;
    }

    m_Modem = FALSE;
    m_Timeout = 4000;
}

BOOL
DbgKdComTransport::SetParameter(PCSTR Name, PCSTR Value)
{
    if (!_strcmpi(Name, PARAM_COM_PORT))
    {
        SetComPortName(Value, m_PortName);
    }
    else if (!_strcmpi(Name, PARAM_COM_BAUD))
    {
        m_BaudRate = atol(Value);
    }
    else if (!_strcmpi(Name, PARAM_COM_MODEM))
    {
        m_Modem = TRUE;
    }
    else if (!_strcmpi(Name, PARAM_COM_TIMEOUT))
    {
        m_Timeout = atol(Value);
    }
    else
    {
        ErrOut("COM port parameters: %s is not a valid parameter\n", Name);

        return FALSE;
    }
    
    return TRUE;
}

HRESULT
DbgKdComTransport::Initialize(void)
{
    HRESULT Status;

    if ((Status = DbgKdTransport::Initialize()) != S_OK)
    {
        return Status;
    }

    m_DirectPhysicalMemory = FALSE;

    if ((Status = OpenComPort(m_PortName, m_BaudRate, m_Timeout,
                              &m_Handle, &m_BaudRate)) != S_OK)
    {
        ErrOut("Failed to open %s\n", m_PortName);
        return Status;
    }

    dprintf("Opened %s\n", m_PortName);

    m_ComEvent = 0;
    if (m_Modem)
    {
        DWORD Mask;

        //
        //  Debugger is being run over a modem.  Set event to watch
        //  carrier detect.
        //

        GetCommMask (m_Handle, &Mask);
        // set DDCD event
        if (!SetCommMask (m_Handle, Mask | 0xA0))
        {
            ErrOut("Failed to set event for %s.\n", m_PortName);
            return WIN32_LAST_STATUS();
        }

        m_EventOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!m_EventOverlapped.hEvent)
        {
            ErrOut("Failed to create EventOverlapped\n");
            return WIN32_LAST_STATUS();
        }

        m_EventOverlapped.Offset = 0;
        m_EventOverlapped.OffsetHigh = 0;

        // Fake an event, so modem status will be checked
        m_ComEvent = 1;
    }

    return S_OK;
}

void
DbgKdComTransport::Uninitialize(void)
{
    if (m_Handle != NULL)
    {
        CloseHandle(m_Handle);
        m_Handle = NULL;
    }
    if (m_EventOverlapped.hEvent != NULL)
    {
        CloseHandle(m_EventOverlapped.hEvent);
        m_EventOverlapped.hEvent = NULL;
    }

    DbgKdTransport::Uninitialize();
}

BOOL
DbgKdComTransport::Read(
    IN PVOID    Buffer,
    IN ULONG    SizeOfBuffer,
    IN PULONG   BytesRead
    )
{
    if (IS_DUMP_TARGET())
    {
        ErrOut( "Attempted to read KD transport while "
                "debugging a crash dump\n" );
        DebugBreak();
    }

    if (m_ComEvent)
    {
        CheckComStatus ();
    }

    if (ComPortRead(m_Handle, Buffer, SizeOfBuffer, BytesRead,
                    &m_ReadOverlapped))
    {
#if DBG_KD_READ
        OutputIo("CR: Read %d bytes of %d\n",
                 Buffer, SizeOfBuffer, *BytesRead);
#endif

        m_BytesRead += *BytesRead;
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

BOOL
DbgKdComTransport::Write(
    IN PVOID    Buffer,
    IN ULONG    SizeOfBuffer,
    IN PULONG   BytesWritten
    )
{
    if (IS_DUMP_TARGET())
    {
        ErrOut( "Attempted to write KD transport "
                "while debugging a crash dump\n" );
        DebugBreak();
    }

    if (m_ComEvent)
    {
        CheckComStatus ();
    }

    //
    // Break up large writes in smaller chunks
    // to try and avoid sending too much data
    // to the target all at once.  Sleep a bit
    // between chunks to let the target retrieve
    // data.
    //
    
    BOOL Succ = TRUE;
    *BytesWritten = 0;
    while (SizeOfBuffer > 0)
    {
        ULONG Request, Done;

        // By default we want to encourage vendors
        // to create machines with robust serial
        // support so we don't actually limit
        // the write size.
#if THROTTLE_WRITES
        Request = 96;
#else
        Request = 0xffffffff;
#endif
        if (SizeOfBuffer < Request)
        {
            Request = SizeOfBuffer;
        }
        
        if (!ComPortWrite(m_Handle, Buffer, Request, &Done,
                          &m_WriteOverlapped))
        {
            Succ = FALSE;
            break;
        }
        
#if DBG_KD_WRITE
        OutputIo("CW: Write %d bytes of %d\n",
                 Buffer, Request, Done);
#endif

        *BytesWritten += Done;
        if (Done <= Request)
        {
            break;
        }

        Buffer = (PVOID)((PUCHAR)Buffer + Done);
        SizeOfBuffer -= Done;

        Sleep(10);
    }
    
    m_BytesWritten += *BytesWritten;
    return Succ;
}

void
DbgKdComTransport::CycleSpeed(void)
{
    if (SetComPortBaud(m_Handle, 0, &m_BaudRate) != S_OK)
    {
        ErrOut("New Baud rate Could not be set on Com %I64x - remains %d.\n",
               (ULONG64)m_Handle, m_BaudRate);
    }
    else
    {
        dprintf("Baud rate set to %d\n", m_BaudRate);
    }
}

VOID
DbgKdComTransport::Synchronize(VOID)
{
    USHORT Index;
    UCHAR DataByte, PreviousDataByte;
    USHORT PacketType = 0;
    ULONG TimeoutCount = 0;
    COMMTIMEOUTS CommTimeouts;
    COMMTIMEOUTS OldTimeouts;
    DWORD BytesRead;
    BOOL rc;

    //
    // Get the old time out values and hold them.
    // We then set a new total timeout value of
    // a fraction of the base timeout.
    //

    GetCommTimeouts(g_DbgKdTransport->m_Handle, &OldTimeouts);

    CommTimeouts = OldTimeouts;
    CommTimeouts.ReadIntervalTimeout = 0;
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;
    CommTimeouts.ReadTotalTimeoutConstant = m_Timeout / 8;

#define TIMEOUT_ITERATIONS 6
    
    SetCommTimeouts(g_DbgKdTransport->m_Handle, &CommTimeouts);

    FlushCallbacks();
    
    while (TRUE)
    {
        
Timeout:
        WriteControlPacket(PACKET_TYPE_KD_RESET, 0L);

        //
        // Read packet leader
        //

        BOOL First = TRUE;
        
        Index = 0;
        do
        {
            if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT)
            {
                KdOut("Synchronize interrupted by exit request\n");
                goto Exit;
            }
            
            //
            // Check user input for control_c.  If user types control_c,
            // we will send a breakin packet to the target.  Hopefully,
            // target will send us a StateChange packet and
            //

            //
            // if we don't get response from kernel in 3 seconds we
            // will resend the reset packet if user does not type ctrl_c.
            // Otherwise, we send breakin character and wait for data again.
            //

            rc = g_DbgKdTransport->Read(&DataByte, 1, &BytesRead);
            if ((!rc) || (BytesRead != 1))
            {
                if (m_BreakIn || m_SyncBreakIn)
                {
                    m_SyncBreakIn = FALSE;
                    WriteBreakInPacket();
                    TimeoutCount = 0;
                    continue;
                }
                TimeoutCount++;
                
                //
                // if we have been waiting for 3 seconds, resend RESYNC packet
                //

                if (TimeoutCount < TIMEOUT_ITERATIONS)
                {
                    continue;
                }
                TimeoutCount = 0;

                KdOut("SYNCTARGET: Timeout.\n");
                FlushCallbacks();

                goto Timeout;
            }

#if DBG_SYNCH
            if (rc && BytesRead == 1 && First)
            {
                dprintf("First byte %X\n", DataByte);
                First = FALSE;
            }
#endif
            
            if (rc && BytesRead == 1 &&
                ( DataByte == PACKET_LEADER_BYTE ||
                  DataByte == CONTROL_PACKET_LEADER_BYTE)
                )
            {
                if ( Index == 0 )
                {
                    PreviousDataByte = DataByte;
                    Index++;
                }
                else if ( DataByte == PreviousDataByte )
                {
                    Index++;
                }
                else
                {
                    PreviousDataByte = DataByte;
                    Index = 1;
                }
            }
            else
            {
                Index = 0;
                
                if (rc && BytesRead == 1)
                {
                    // The target machine is alive and talking but
                    // the received data is in the middle of
                    // a packet.  Break out of the header byte
                    // loop and consume up to a trailer byte.
                    break;
                }
            }
        }
        while ( Index < 4 );

        if (Index == 4 && DataByte == CONTROL_PACKET_LEADER_BYTE)
        {
            //
            // Read 2 byte Packet type
            //

            rc = g_DbgKdTransport->Read((PUCHAR)&PacketType,
                                        sizeof(PacketType), &BytesRead);

            if (rc && BytesRead == sizeof(PacketType) &&
                PacketType == PACKET_TYPE_KD_RESET)
            {
                KdOut("SYNCTARGET: Received KD_RESET ACK packet.\n");

                m_PacketExpected = INITIAL_PACKET_ID;
                m_NextPacketToSend = INITIAL_PACKET_ID;

                KdOut("SYNCTARGET: Target synchronized successfully...\n");
                FlushCallbacks();

                goto Exit;
            }
        }

        //
        // If we receive Data Packet leader, it means target has not
        // receive our reset packet. So we loop back and send it again.
        // N.B. We need to wait until target finishes sending the packet.
        // Otherwise, we may be sending the reset packet while the target
        // is sending the packet. This might cause target loss the reset
        // packet.
        //

        Index = 0;
        while (DataByte != PACKET_TRAILING_BYTE)
        {
            rc = g_DbgKdTransport->Read(&DataByte, 1, &BytesRead);
            if (!rc || BytesRead != 1)
            {
                break;
            }

            Index++;
        }

#if DBG_SYNCH
        dprintf("  ate %x bytes\n", Index);
        FlushCallbacks();
#endif
    }
    
 Exit:
    SetCommTimeouts(g_DbgKdTransport->m_Handle, &OldTimeouts);
}

ULONG
DbgKdComTransport::ReadPacketContents(IN USHORT PacketType)
{
    DWORD BytesRead;
    BOOL rc;
    UCHAR DataByte;
    ULONG Checksum;
    ULONG SyncBit;
    ULONG WaitStatus;

    //
    // First read a packet leader
    //

WaitForPacketLeader:

    WaitStatus = ReadPacketLeader(PacketType, &s_PacketHeader.PacketLeader);
    if (WaitStatus != DBGKD_WAIT_PACKET)
    {
        return WaitStatus;
    }
    if (m_AllowInitialBreak && (g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK))
    {
        KdOut("Attempting to get initial breakpoint.\n");
        WriteBreakInPacket();
    }

    // We've either sent the initial break or we don't want
    // one.  Either way we don't need to send another one.
    m_AllowInitialBreak = FALSE;

    //
    // Read packetLeader ONLY read two Packet Leader bytes.  This do loop
    // filters out the remaining leader byte.
    //

    do
    {
        rc = Read(&DataByte, 1, &BytesRead);
        if ((rc) && BytesRead == 1)
        {
            if (DataByte == PACKET_LEADER_BYTE ||
                DataByte == CONTROL_PACKET_LEADER_BYTE)
            {
                continue;
            }
            else
            {
                *(PUCHAR)&s_PacketHeader.PacketType = DataByte;
                break;
            }
        }
        else
        {
            goto WaitForPacketLeader;
        }
    } while (TRUE);

    //
    // Now we have valid packet leader. Read rest of the packet type.
    //

    rc = Read(((PUCHAR)&s_PacketHeader.PacketType) + 1,
              sizeof(s_PacketHeader.PacketType) - 1, &BytesRead);
    if ((!rc) || BytesRead != sizeof(s_PacketHeader.PacketType) - 1)
    {
        //
        // If we cannot read the packet type and if the packet leader
        // indicates this is a data packet, we need to ask for resend.
        // Otherwise we simply ignore the incomplete packet.
        //

        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
            KdOut("READ: Data packet header Type error (short read).\n");
        }
        
        goto WaitForPacketLeader;
    }

    //
    // Check the Packet type.
    //

    if ((g_KdMaxPacketType == 0 &&
         s_PacketHeader.PacketType >= PACKET_TYPE_MAX) ||
        (g_KdMaxPacketType > 0 &&
         s_PacketHeader.PacketType >= g_KdMaxPacketType))
    {
        KdOut("READ: Received INVALID packet type.\n");

        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
        }

        goto WaitForPacketLeader;
    }

    KdOut("      PacketType=%x, ", s_PacketHeader.PacketType);

    //
    // Read ByteCount
    //

    rc = Read(&s_PacketHeader.ByteCount, sizeof(s_PacketHeader.ByteCount),
              &BytesRead);
    if ((!rc) || BytesRead != sizeof(s_PacketHeader.ByteCount))
    {
        //
        // If we cannot read the packet type and if the packet leader
        // indicates this is a data packet, we need to ask for resend.
        // Otherwise we simply ignore the incomplete packet.
        //

        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
            KdOut("READ: Data packet header ByteCount error (short read).\n");
        }
        
        goto WaitForPacketLeader;
    }

    //
    // Check ByteCount
    //

    if (s_PacketHeader.ByteCount > PACKET_MAX_SIZE)
    {
        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
            KdOut("READ: Data packet header ByteCount error (short read).\n");
        }
        
        goto WaitForPacketLeader;
    }

    KdOut("ByteCount=%x, ", s_PacketHeader.ByteCount);

    //
    // Read Packet Id
    //

    rc = Read(&s_PacketHeader.PacketId, sizeof(s_PacketHeader.PacketId),
              &BytesRead);
    if ((!rc) || BytesRead != sizeof(s_PacketHeader.PacketId))
    {
        //
        // If we cannot read the packet Id and if the packet leader
        // indicates this is a data packet, we need to ask for resend.
        // Otherwise we simply ignore the incomplete packet.
        //

        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
            KdOut("READ: Data packet header Id error (short read).\n");
        }
        
        goto WaitForPacketLeader;
    }

    KdOut("PacketId=%x,\n", s_PacketHeader.PacketId);

    //
    // Don't read checksum here as in some cases
    // it isn't sent with control packets.
    //

    if (s_PacketHeader.PacketLeader == CONTROL_PACKET_LEADER )
    {
        if (s_PacketHeader.PacketType == PACKET_TYPE_KD_ACKNOWLEDGE )
        {
            //
            // If we received an expected ACK packet and we are not
            // waiting for any new packet, update outgoing packet id
            // and return.  If we are NOT waiting for ACK packet
            // we will keep on waiting.  If the ACK packet
            // is not for the packet we send, ignore it and keep on waiting.
            //

            if (s_PacketHeader.PacketId != m_NextPacketToSend)
            {
                KdOut("READ: Received unmatched packet id = %lx, Type = %x\n",
                      s_PacketHeader.PacketId, s_PacketHeader.PacketType);
                goto WaitForPacketLeader;
            }
            else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE)
            {
                m_NextPacketToSend ^= 1;

                KdOut("READ: Received correct ACK packet.\n");
                FlushCallbacks();

                return DBGKD_WAIT_ACK;
            }
            else
            {
                goto WaitForPacketLeader;
            }
        }
        else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESET)
        {
            //
            // if we received Reset packet, reset the packet control variables
            // and resend earlier packet.
            //

            m_NextPacketToSend = INITIAL_PACKET_ID;
            m_PacketExpected = INITIAL_PACKET_ID;
            WriteControlPacket(PACKET_TYPE_KD_RESET, 0L);

            KdOut("DbgKdpWaitForPacket(): Recieved KD_RESET packet, "
                  "send KD_RESET ACK packet\n");
            FlushCallbacks();

            return DBGKD_WAIT_FAILED;
        }
        else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESEND)
        {
            KdOut("READ: Received RESEND packet\n");
            FlushCallbacks();

            return DBGKD_WAIT_FAILED;
        }
        else
        {
            //
            // Invalid packet header, ignore it.
            //

            KdOut("READ: Received Control packet with UNKNOWN type\n");
            goto WaitForPacketLeader;
        }
    }
    else
    {
        //
        // The packet header is for data packet (not control packet).
        // Read Checksum.
        //
    
        rc = Read(&s_PacketHeader.Checksum, sizeof(s_PacketHeader.Checksum),
                  &BytesRead);
        if ((!rc) || BytesRead != sizeof(s_PacketHeader.Checksum))
        {
            WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
            KdOut("READ: Data packet header "
                  "checksum error (short read).\n");
            goto WaitForPacketLeader;
        }

        if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE)
        {
            //
            // If we are waiting for ACK packet ONLY
            // and we receive a data packet header, check if the packet id
            // is what we expected.  If yes, assume the acknowledge is lost
            // (but sent) and process the packet.
            //

            if (s_PacketHeader.PacketId == m_PacketExpected)
            {
                m_NextPacketToSend ^= 1;
                KdOut("READ: Received VALID data packet "
                      "while waiting for ACK.\n");
            }
            else
            {
                KdOut("READ: Received Data packet with unmatched ID = %lx\n",
                      s_PacketHeader.PacketId);
                WriteControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE,
                                   s_PacketHeader.PacketId);
                goto WaitForPacketLeader;
            }
        }
    }

    //
    // We are waiting for data packet and we received the packet header
    // for data packet. Perform the following checkings to make sure
    // it is the packet we are waiting for.
    //

    if ((s_PacketHeader.PacketId & ~SYNC_PACKET_ID) != INITIAL_PACKET_ID &&
        (s_PacketHeader.PacketId & ~SYNC_PACKET_ID) != (INITIAL_PACKET_ID ^ 1))
    {
        KdOut("READ: Received INVALID packet Id.\n");
        return DBGKD_WAIT_RESEND;
    }

    rc = Read(s_Packet, s_PacketHeader.ByteCount, &BytesRead);
    if ( (!rc) || BytesRead != s_PacketHeader.ByteCount )
    {
        KdOut("READ: Data packet error (short read).\n");
        return DBGKD_WAIT_RESEND;
    }

    //
    // Make sure the next byte is packet trailing byte
    //

    rc = Read(&DataByte, sizeof(DataByte), &BytesRead);
    if ( (!rc) || BytesRead != sizeof(DataByte) ||
         DataByte != PACKET_TRAILING_BYTE )
    {
        KdOut("READ: Packet trailing byte timeout.\n");
        return DBGKD_WAIT_RESEND;
    }

    //
    // Make sure the checksum is valid.
    //

    Checksum = ComputeChecksum(s_Packet, s_PacketHeader.ByteCount);
    if (Checksum != s_PacketHeader.Checksum)
    {
        KdOut("READ: Checksum error.\n");
        return DBGKD_WAIT_RESEND;
    }

    //
    // We have a valid data packet.  If the packetid is bad, we just
    // ack the packet to the sender will step ahead.  If packetid is bad
    // but SYNC_PACKET_ID bit is set, we sync up.  If packetid is good,
    // or SYNC_PACKET_ID is set, we take the packet.
    //

    KdOut("READ: Received Type %x data packet with id = %lx successfully.\n\n",
          s_PacketHeader.PacketType, s_PacketHeader.PacketId);

    SyncBit = s_PacketHeader.PacketId & SYNC_PACKET_ID;
    s_PacketHeader.PacketId = s_PacketHeader.PacketId & ~SYNC_PACKET_ID;

    //
    // Ack the packet.  SYNC_PACKET_ID bit will ALWAYS be OFF.
    //

    WriteControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE,
                       s_PacketHeader.PacketId);

    //
    // Check the incoming packet Id.
    //

    if ((s_PacketHeader.PacketId != m_PacketExpected) &&
        (SyncBit != SYNC_PACKET_ID))
    {
        KdOut("READ: Unexpected Packet Id (Acked).\n");
        goto WaitForPacketLeader;
    }
    else
    {
        if (SyncBit == SYNC_PACKET_ID)
        {
            //
            // We know SyncBit is set, so reset Expected Ids
            //

            KdOut("READ: Got Sync Id, reset PacketId.\n");

            m_PacketExpected = s_PacketHeader.PacketId;
            m_NextPacketToSend = INITIAL_PACKET_ID;
        }
        
        m_PacketExpected ^= 1;
    }

    return DBGKD_WAIT_PACKET;
}

ULONG
DbgKdComTransport::WritePacketContents(IN KD_PACKET* Packet,
                                       IN PVOID PacketData,
                                       IN USHORT PacketDataLength,
                                       IN PVOID MorePacketData OPTIONAL,
                                       IN USHORT MorePacketDataLength OPTIONAL,
                                       IN BOOL NoAck)
{
    BOOL rc;
    ULONG BytesWritten;
    
    // Lock to ensure all parts of the data are
    // sequential in the stream.
    RESUME_ENGINE();
        
    //
    // Write the packet header
    //

    rc = Write(Packet, sizeof(*Packet), &BytesWritten);
    if ( (!rc) || BytesWritten != sizeof(*Packet))
    {
        //
        // An error occured writing the header, so write it again
        //

        KdOut("WRITE: Packet header error.\n");
        SUSPEND_ENGINE();
        return DBGKD_WRITE_RESEND;
    }

    //
    // Write the primary packet data
    //

    rc = Write(PacketData, PacketDataLength, &BytesWritten);
    if ( (!rc) || BytesWritten != PacketDataLength )
    {
        //
        // An error occured writing the primary packet data,
        // so write it again
        //

        KdOut("WRITE: Message header error.\n");
        SUSPEND_ENGINE();
        return DBGKD_WRITE_RESEND;
    }

    //
    // If secondary packet data was specified (WriteMemory, SetContext...)
    // then write it as well.
    //

    if ( ARGUMENT_PRESENT(MorePacketData) )
    {
        rc = Write(MorePacketData, MorePacketDataLength, &BytesWritten);
        if ( (!rc) || BytesWritten != MorePacketDataLength )
        {
            //
            // An error occured writing the secondary packet data,
            // so write it again
            //

            KdOut("WRITE: Message data error.\n");
            SUSPEND_ENGINE();
            return DBGKD_WRITE_RESEND;
        }
    }

    //
    // Output a packet trailing byte
    //

    do
    {
        rc = Write(&s_PacketTrailingByte[0],
                   sizeof(s_PacketTrailingByte),
                   &BytesWritten);
    }
    while ((!rc) || (BytesWritten != sizeof(s_PacketTrailingByte)));

    SUSPEND_ENGINE();

    if (!NoAck)
    {
        ULONG Received;
        
        //
        // Wait for ACK
        //

        Received = WaitForPacket(PACKET_TYPE_KD_ACKNOWLEDGE, NULL);
        if (Received != DBGKD_WAIT_ACK)
        {
            KdOut("WRITE: Wait for ACK failed. Resend Packet.\n");
            return DBGKD_WRITE_RESEND;
        }
    }

    return DBGKD_WRITE_PACKET;
}

ULONG
DbgKdComTransport::ReadPacketLeader(
    IN ULONG PacketType,
    OUT PULONG PacketLeader
    )
{
    DWORD BytesRead;
    BOOL rc;
    USHORT Index;
    UCHAR DataByte, PreviousDataByte;

    Index = 0;
    do
    {
        if (m_BreakIn)
        {
            if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64)
            {
                WriteBreakInPacket();
                return DBGKD_WAIT_RESYNC;
            }
        }

        if (m_Resync)
        {
            m_Resync = FALSE;

            KdOut(" Resync packet id ...");

            Synchronize();

            KdOut(" Done.\n");
            FlushCallbacks();

            return DBGKD_WAIT_RESYNC;
        }

        if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT)
        {
            KdOut("Packet read interrupted by exit request\n");
            return DBGKD_WAIT_FAILED;
        }
            
        FlushCallbacks();
        
        rc = Read(&DataByte, 1, &BytesRead);
        if (rc && BytesRead == 1 &&
            ( DataByte == PACKET_LEADER_BYTE ||
              DataByte == CONTROL_PACKET_LEADER_BYTE))
        {
            if ( Index == 0 )
            {
                PreviousDataByte = DataByte;
                Index++;
            }
            else if ( DataByte == PreviousDataByte )
            {
                Index++;
            }
            else
            {
                PreviousDataByte = DataByte;
                Index = 1;
            }
        }
        else
        {
            Index = 0;
            if (BytesRead == 0)
            {
                KdOut("READ: Timeout.\n");
                FlushCallbacks();

                if (m_AllowInitialBreak &&
                    (g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK))
                {
                    KdOut("Attempting to get initial breakpoint.\n");
                    WriteBreakInPacket();
                }
                return DBGKD_WAIT_FAILED;
            }
        }
    } while ( Index < 2 );

    if ( DataByte != CONTROL_PACKET_LEADER_BYTE )
    {
        *PacketLeader = PACKET_LEADER;
    }
    else
    {
        *PacketLeader = CONTROL_PACKET_LEADER;
    }
    return DBGKD_WAIT_PACKET;
}

void
DbgKdComTransport::CheckComStatus(void)
/*++

Routine Description:

    Called when the com port status trigger signals a change.
    This function handles the change in status.

    Note: status is only monitored when being used over the modem.

--*/
{
    DWORD   status;
    BOOL    rc;
    ULONG   br, bw;
    CHAR    buf[128];
    DWORD   CommErr;
    COMSTAT CommStat;
    ULONG   Len;

    if (!m_ComEvent)
    {
        //
        // Not triggered, just return
        //

        return;
    }

    // This should succeed since we were just notified,
    // but check the return value to keep PREfix happy.
    if (!GetCommModemStatus(m_Handle, &status))
    {
        // Leave m_ComEvent set for another try.
        return;
    }
    
    m_ComEvent = 0;
    
    if (!(status & 0x80))
    {
        dprintf ("No carrier detect - in terminal mode\n");

        // This routine can be called during a wait when the
        // engine lock isn't held and can also be called when
        // the lock is held.  RESUME handles both of these
        // cases so that the lock is reacquired or reentered.
        RESUME_ENGINE();
    
        //
        // Loop and read any com input
        //

        while (!(status & 0x80))
        {
            //
            // Get some input to send to the modem.
            //

            Len = GetInput("Term> ", buf, sizeof(buf));
            if (Len > 0)
            {
                Write(buf, Len, &Len);
                buf[0] = '\n';
                buf[1] = '\r';
                Write(buf, 2, &Len);
            }

            GetCommModemStatus (m_Handle, &status);
            rc = Read(buf, sizeof buf, &br);
            if (rc != TRUE || br == 0)
            {
                continue;
            }

            //
            // print the string.
            //

            dprintf("%s", buf);
            FlushCallbacks();

            //
            // if logging is on, log the output
            //

            if (g_LogFile != -1)
            {
                _write(g_LogFile, buf, br);
            }
        }

        dprintf ("Carrier detect - returning to debugger\n");
        FlushCallbacks();

        ClearCommError (
            m_Handle,
            &CommErr,
            &CommStat
            );

        SUSPEND_ENGINE();
    }
    else
    {
        CommErr = 0;
        ClearCommError (
            m_Handle,
            &CommErr,
            &CommStat
            );

        if (CommErr & CE_FRAME)
        {
            dprintf (" [FRAME ERR] ");
        }

        if (CommErr & CE_OVERRUN)
        {
            dprintf (" [OVERRUN ERR] ");
        }

        if (CommErr & CE_RXPARITY)
        {
            dprintf (" [PARITY ERR] ");
        }
    }

    //
    // Reset trigger
    //

    WaitCommEvent (m_Handle, &m_ComEvent, &m_EventOverlapped);
}

//----------------------------------------------------------------------------
//
// DbgKd1394Transport.
//
//----------------------------------------------------------------------------

#define PARAM_1394_CHANNEL "Channel"

#define ENV_1394_CHANNEL "_NT_DEBUG_1394_CHANNEL"

DbgKd1394Transport::DbgKd1394Transport(void)
{
    m_Index = DBGKD_TRANSPORT_1394;
    m_Name = g_DbgKdTransportNames[m_Index];
    m_InvPacketRetryLimit = 3;
    m_AckWrites = FALSE;
}

ULONG
DbgKd1394Transport::GetNumberParameters(void)
{
    return 1;
}

void
DbgKd1394Transport::GetParameter(ULONG Index, PSTR Name, PSTR Value)
{
    switch(Index)
    {
    case 0:
        strcpy(Name, PARAM_1394_CHANNEL);
        sprintf(Value, "%d", m_Channel);
        break;
    }
}

void
DbgKd1394Transport::ResetParameters(void)
{
    PSTR Env;
    
    if ((Env = getenv(ENV_1394_CHANNEL)) == NULL)
    {
        m_Channel = 0;
    }
    else
    {
        m_Channel = atol(Env);
    }
}

BOOL
DbgKd1394Transport::SetParameter(PCSTR Name, PCSTR Value)
{
    if (!_strcmpi(Name, PARAM_1394_CHANNEL))
    {
        m_Channel = atol(Value);
    }
    else
    {
        ErrOut("1394 port parameters: %s is not a valid parameter\n", Name);

        return FALSE;
    }
    
    return TRUE;
}

HRESULT
DbgKd1394Transport::Initialize(void)
{
    char Name[64];
    HRESULT Status;

    dprintf("Using 1394 for debugging\n");
    
    if ((Status = DbgKdTransport::Initialize()) != S_OK)
    {
        return Status;
    }
    
    m_DirectPhysicalMemory = TRUE;

    Status = Create1394Channel(m_Channel, Name, &m_Handle);
    if (Status != S_OK)
    {
        ErrOut("Failed to open 1394 channel %d\n", m_Channel);
        ErrOut("If this is the first time KD was run, this is"
               " why this failed.\nVirtual 1394 "
               "Debugger Driver Installation will now be attempted\n");
        return Status;
    }
    else
    {
        dprintf("Opened %s\n", Name);
    }

    //
    // put the virtual driver in the right operating mode..
    //

    if (!SwitchVirtualDebuggerDriverMode(V1394DBG_API_CONFIGURATION_MODE_DEBUG)) {

        return FALSE;

    }


    return S_OK;
}

void
DbgKd1394Transport::Uninitialize(void)
{
    if (m_Handle != NULL)
    {
        CloseHandle(m_Handle);
        m_Handle = NULL;
    }

    DbgKdTransport::Uninitialize();
}

BOOL
DbgKd1394Transport::Read(
    IN PVOID    Buffer,
    IN ULONG    SizeOfBuffer,
    IN PULONG   BytesRead
    )
{
    BOOL    rc;
    DWORD   TrashErr;
    COMSTAT TrashStat;

    if (IS_DUMP_TARGET())
    {
        ErrOut( "Attempted to read KD transport while "
                "debugging a crash dump\n" );
        DebugBreak();
    }

    if (!SwitchVirtualDebuggerDriverMode(V1394DBG_API_CONFIGURATION_MODE_DEBUG)) {

        return FALSE;

    }

    rc = ReadFile(
             m_Handle,
             Buffer,
             SizeOfBuffer,
             BytesRead,
             &m_ReadOverlapped
             );
    if (!rc)
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            rc = GetOverlappedResult(m_Handle,
                                     &m_ReadOverlapped,
                                     BytesRead,
                                     TRUE);
        }
        else
        {
            // Prevent looping on read errors from
            // burning 100% of the CPU.
            Sleep(50);
        }
    }

    if (rc)
    {
        m_BytesRead += *BytesRead;
    }
    
    return rc;
}

BOOL
DbgKd1394Transport::Write(
    IN PVOID    Buffer,
    IN ULONG    SizeOfBuffer,
    IN PULONG   BytesWritten
    )
{
    BOOL    rc;
    DWORD   TrashErr;
    COMSTAT TrashStat;

    if (IS_DUMP_TARGET())
    {
        ErrOut( "Attempted to write KD transport "
                "while debugging a crash dump\n" );
        DebugBreak();
    }

    if (!SwitchVirtualDebuggerDriverMode(V1394DBG_API_CONFIGURATION_MODE_DEBUG)) {

        return FALSE;

    }

    rc = WriteFile(
             m_Handle,
             Buffer,
             SizeOfBuffer,
             BytesWritten,
             &m_WriteOverlapped
             );
    if (!rc)
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            rc = GetOverlappedResult(m_Handle,
                                     &m_WriteOverlapped,
                                     BytesWritten,
                                     TRUE);
        }
    }

    if (rc)
    {
        m_BytesWritten += *BytesWritten;
    }
    
    return rc;
}

HRESULT
DbgKd1394Transport::ReadTargetPhysicalMemory(
    IN ULONG64 MemoryOffset,
    IN PVOID Buffer,
    IN ULONG SizeofBuffer,
    IN PULONG BytesRead
    )
{
    DWORD   dwRet, dwBytesRet;
    PV1394DBG_API_REQUEST pApiReq;

    if (IS_DUMP_TARGET())
    {
        ErrOut( "Attempted to access KD transport while "
                "debugging a crash dump\n" );
        DebugBreak();
    }

    //
    // first setup the read i/o parameters in the virtual driver
    //

    pApiReq = (PV1394DBG_API_REQUEST)
        LocalAlloc(LPTR, sizeof(V1394DBG_API_REQUEST));
    if (pApiReq == NULL)
    {
        return E_OUTOFMEMORY;
    }

    //
    // if the virtual driver is not set in raw access mode, we need to 
    // tell it to change modes..
    //

    if (!SwitchVirtualDebuggerDriverMode(V1394DBG_API_CONFIGURATION_MODE_RAW_MEMORY_ACCESS)) {

        LocalFree(pApiReq);
        return E_UNEXPECTED;

    }

    pApiReq->RequestNumber = V1394DBG_API_SET_IO_PARAMETERS;
    pApiReq->Flags = V1394DBG_API_FLAG_READ_IO;

    pApiReq->u.SetIoParameters.fulFlags = 0;
    pApiReq->u.SetIoParameters.StartingMemoryOffset.QuadPart = MemoryOffset;

    dwRet = DeviceIoControl( m_Handle,
                             IOCTL_V1394DBG_API_REQUEST,
                             pApiReq,
                             sizeof(V1394DBG_API_REQUEST),
                             NULL,
                             0,
                             &dwBytesRet,
                             NULL
                             );
    if (!dwRet)
    {
        dwRet = GetLastError();
        ErrOut("Failed to send SetIoParameters 1394 "
               "Virtual Driver Request, error %x\n",dwRet);

        LocalFree(pApiReq);
        return E_UNEXPECTED;
    }

    LocalFree(pApiReq);

    //
    // now do anormal read. The virtual driver will read SizeofBuffer bytes
    // starting at the remote PCs physical address we specified above
    //

    dwRet = ReadFile(
             m_Handle,
             Buffer,
             SizeofBuffer,
             BytesRead,
             &m_ReadOverlapped
             );
    if (!dwRet)
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            dwRet = GetOverlappedResult(m_Handle,
                                     &m_ReadOverlapped,
                                     BytesRead,
                                     TRUE);
        }
    }

    return (dwRet != 0) ? S_OK : E_UNEXPECTED;
}

BOOL
DbgKd1394Transport::SwitchVirtualDebuggerDriverMode(
    IN ULONG    DesiredOperationMode
    )
{
    DWORD   dwRet, dwBytesRet;
    PV1394DBG_API_REQUEST pApiReq;

    //
    // if the virtual driver is not set in raw access mode, we need to 
    // tell it to change modes..
    //

    if (m_OperationMode != DesiredOperationMode) {

        //
        // first setup the read i/o parameters in the virtual driver
        //
    
        pApiReq = (PV1394DBG_API_REQUEST)
            LocalAlloc(LPTR, sizeof(V1394DBG_API_REQUEST));
        if (pApiReq == NULL)
        {
            return FALSE;
        }

        pApiReq->RequestNumber = V1394DBG_API_SET_CONFIGURATION;
        pApiReq->Flags = 0;
    
        pApiReq->u.SetConfiguration.OperationMode = DesiredOperationMode;
    
        dwRet = DeviceIoControl( m_Handle,
                                 IOCTL_V1394DBG_API_REQUEST,
                                 pApiReq,
                                 sizeof(V1394DBG_API_REQUEST),
                                 NULL,
                                 0,
                                 &dwBytesRet,
                                 NULL
                                 );
    
        if (!dwRet)
        {
            dwRet = GetLastError();
            ErrOut("Failed to send SetConfiguration 1394 "
                   "Virtual Driver Request, error %x\n", dwRet);
    
            LocalFree(pApiReq);
            return FALSE;
        }

        m_OperationMode = DesiredOperationMode;

        LocalFree(pApiReq);

    }

    return TRUE;
}

VOID
DbgKd1394Transport::Synchronize(VOID)
{
    ULONG Index;
    ULONG BytesRead;
    BOOL rc;
    
    // XXX drewb - Why is this code disabled?
    return;
        
    Index = 3;
    while (TRUE)
    {
        if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT)
        {
            KdOut("Synchronize interrupted by exit request\n");
            return;
        }
            
        WriteControlPacket(PACKET_TYPE_KD_RESET, 0L);

        FlushCallbacks();
            
        rc = Read(s_Packet, sizeof(s_Packet), &BytesRead);

        CopyMemory(&s_PacketHeader, &s_Packet[0], sizeof(KD_PACKET));
        
        if (rc && (BytesRead >= sizeof(s_PacketHeader)))
        {
            if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESET)
            {
                break;
            }
        }

        if (!Index--)
        {
            break;
        }
    }
}

ULONG
DbgKd1394Transport::ReadPacketContents(IN USHORT PacketType)
{
    DWORD BytesRead;
    BOOL rc;
    UCHAR DataByte;
    ULONG Checksum;

WaitForPacket1394:

    if (m_AllowInitialBreak && (g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK))
    {
        KdOut("Attempting to get initial breakpoint.\n");
        WriteBreakInPacket();
        m_AllowInitialBreak = FALSE;
    }

    if (m_Resync)
    {
        m_Resync = FALSE;

        KdOut(" Resync packet id ...");

        Synchronize();

        KdOut(" Done.\n");
        FlushCallbacks();

        return DBGKD_WAIT_RESYNC;
    }

    if (m_BreakIn)
    {
        WriteBreakInPacket();
        return DBGKD_WAIT_RESYNC;
    }

    if (g_EngStatus & ENG_STATUS_EXIT_CURRENT_WAIT)
    {
        KdOut("Packet read interrupted by exit request\n");
        return DBGKD_WAIT_FAILED;
    }
            
    FlushCallbacks();
    
    //
    // read the whole packet at once.
    // we try to read MAX_PACKET worth of data and then check how much 
    // we really read. Also since the packet header (KD_PACKET) is part of what
    // we read, we later have to move the data packet back sizeof(KD_PACKET)
    //

    rc = Read(s_Packet, sizeof(s_Packet), &BytesRead);
    CopyMemory(&s_PacketHeader, &s_Packet[0], sizeof(KD_PACKET));

    if (!rc || (BytesRead < sizeof(s_PacketHeader)))
    {
        if (!rc)
        {
            KdOut("READ: Error %x.\n",GetLastError());
        }
        else
        {
            KdOut("READ: Data ByteCount error (short read) %x, %x.\n",
                  BytesRead, sizeof(s_PacketHeader));
        }

        if (rc && (BytesRead >= sizeof(s_PacketHeader)) )
        {
            if (s_PacketHeader.PacketLeader == PACKET_LEADER)
            {
                WriteControlPacket(PACKET_TYPE_KD_RESEND, 0L);
                KdOut("READ: Data packet header "
                      "ByteCount error (short read).\n");
            }
        }

        goto WaitForPacket1394;
    }

    //
    // move data portion to start of packet.
    //

    MoveMemory(s_Packet, ((PUCHAR)s_Packet + sizeof(KD_PACKET)),
               BytesRead - sizeof(KD_PACKET));

    //
    // Check the Packet type.
    //

    if ((g_KdMaxPacketType == 0 &&
         s_PacketHeader.PacketType >= PACKET_TYPE_MAX) ||
        (g_KdMaxPacketType > 0 &&
         s_PacketHeader.PacketType >= g_KdMaxPacketType))
    {
        KdOut("READ: Received INVALID packet type.\n");

        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            return DBGKD_WAIT_RESEND;
        }

        return DBGKD_WAIT_FAILED;
    }

    KdOut("      PacketType=%x, ", s_PacketHeader.PacketType);

    //
    // Check ByteCount
    //

    if (s_PacketHeader.ByteCount > PACKET_MAX_SIZE )
    {
        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            KdOut("READ: Data packet header ByteCount error (short read).\n");
            return DBGKD_WAIT_RESEND;
        }

        return DBGKD_WAIT_FAILED;
    }

    KdOut("ByteCount=%x, PacketId=%x,\n",
          s_PacketHeader.ByteCount,
          s_PacketHeader.PacketId);

    if (s_PacketHeader.ByteCount != (BytesRead - sizeof(s_PacketHeader)))
    {
        if (s_PacketHeader.PacketLeader == PACKET_LEADER)
        {
            KdOut("READ: Data packet header ByteCount error (short read).\n");
            return DBGKD_WAIT_RESEND;
        }
        
        return DBGKD_WAIT_FAILED;
    }

    //
    // Make sure the checksum is valid.
    //

    Checksum = ComputeChecksum(s_Packet, s_PacketHeader.ByteCount);
    if (Checksum != s_PacketHeader.Checksum)
    {
        KdOut("READ: Checksum error.\n");
        return DBGKD_WAIT_RESEND;
    }

    if (s_PacketHeader.PacketLeader == CONTROL_PACKET_LEADER)
    {
        if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESET)
        {
            //
            // if we received Reset packet, reset the packet control variables
            // and resend earlier packet.
            //

            m_NextPacketToSend = INITIAL_PACKET_ID;
            m_PacketExpected = INITIAL_PACKET_ID;
            WriteControlPacket(PACKET_TYPE_KD_RESET, 0L);

            KdOut("DbgKdpWaitForPacket(): "
                  "Recieved KD_RESET packet, send KD_RESET ACK packet\n");
            FlushCallbacks();

            return DBGKD_WAIT_FAILED;
        }
        else if (s_PacketHeader.PacketType == PACKET_TYPE_KD_RESEND)
        {
            KdOut("READ: Received RESEND packet\n");
            FlushCallbacks();

            return DBGKD_WAIT_FAILED;
        }
        else
        {
            //
            // Invalid packet header, ignore it.
            //

            KdOut("READ: Received Control packet with UNKNOWN type\n");
            FlushCallbacks();

            return DBGKD_WAIT_FAILED;
        }
    }

    //
    // we are waiting for data packet and we received the packet header
    // for data packet. Perform the following checkings to make sure
    // it is the packet we are waiting for.
    //

    KdOut("READ: Received Type %x data packet with id = %lx successfully.\n\n",
          s_PacketHeader.PacketType, s_PacketHeader.PacketId);

    return DBGKD_WAIT_PACKET;
}

ULONG
DbgKd1394Transport::WritePacketContents(IN KD_PACKET* Packet,
                                        IN PVOID PacketData,
                                        IN USHORT PacketDataLength,
                                        IN PVOID MorePacketData OPTIONAL,
                                        IN USHORT MorePacketDataLength OPTIONAL,
                                        IN BOOL NoAck)
{
    BOOL rc;
    ULONG BytesWritten;
    PUCHAR Tx;
    
    // Lock to ensure only one thread is using
    // the transmit buffer.
    RESUME_ENGINE();
        
    //
    // On 1394 we double buffer all packet segments into one contigious
    // buffer and write it all at once
    //

    Tx = m_TxPacket;

    memcpy(Tx, Packet, sizeof(*Packet));
    Tx += sizeof(*Packet);

    memcpy(Tx, PacketData, PacketDataLength);
    Tx += PacketDataLength;

    if ( ARGUMENT_PRESENT(MorePacketData) )
    {
        memcpy(Tx, MorePacketData, MorePacketDataLength);
        Tx += MorePacketDataLength;
    }

    //
    // The 1394 Debug protocol does not use trailer bytes
    //

    //
    // Write the whole packet out to the bus
    //

    do
    {
        rc = Write(&m_TxPacket[0], (ULONG)(Tx - m_TxPacket), &BytesWritten);
    }
    while ((!rc) || (BytesWritten != (ULONG)(Tx - m_TxPacket)));

    SUSPEND_ENGINE();

    return DBGKD_WRITE_PACKET;
}

//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------

#define BUS_TYPE         "_NT_DEBUG_BUS"
#define DBG_BUS1394_NAME "1394"

HRESULT
DbgKdConnectAndInitialize(PCSTR Options)
{
    DbgKdTransport* Trans = NULL;
    ULONG Index;
        
    // Try and find the transport by name.
    Index = ParameterStringParser::
        GetParser(Options, DBGKD_TRANSPORT_COUNT, g_DbgKdTransportNames);
    if (Index < DBGKD_TRANSPORT_COUNT)
    {
        switch(Index)
        {
        case DBGKD_TRANSPORT_COM:
            Trans = &g_DbgKdComTransport;
            break;
        case DBGKD_TRANSPORT_1394:
            Trans = &g_DbgKd1394Transport;
            break;
        }
    }

    if (Trans == NULL)
    {
        PCHAR BusType;

        // Couldn't identify the transport from options so check
        // the environment.

        // Default to com port.
        Trans = &g_DbgKdComTransport;
        
        if (BusType = getenv(BUS_TYPE))
        {
            if (strstr(BusType, DBG_BUS1394_NAME))
            {
                Trans = &g_DbgKd1394Transport;
            }
        }
    }

    HRESULT Status;

    // Clear parameter state.
    Trans->ResetParameters();
    
    if (!Trans->ParseParameters(Options))
    {
        Status = E_INVALIDARG;
    }
    else
    {
        Status = Trans->Initialize();
        if (Status != S_OK)
        {
            ErrOut("Kernel Debugger failed initialization, 0x%X\n", Status);
        }
    }
    
    if (Status == S_OK)
    {
        g_DbgKdTransport = Trans;
        Trans->Restart();
    }
    
    return Status;
}

VOID
DbgKdpPrint(
    IN ULONG Processor,
    IN PCSTR String,
    IN USHORT StringLength,
    IN ULONG Mask
    )
{
    DWORD i;
    DWORD j;
    CHAR c;
    PSTR d;

    DBG_ASSERT(StringLength < PACKET_MAX_SIZE - 2);

    // This routine can be called during a wait when the
    // engine lock isn't held and can also be called when
    // the lock is held.  RESUME handles both of these
    // cases so that the lock is reacquired or reentered.
    RESUME_ENGINE();
    
    if (g_TargetNumberProcessors > 1 && Processor != g_LastProcessorToPrint)
    {
        g_LastProcessorToPrint = Processor;
        MaskOut(Mask, "%d:", Processor);
    }

    StartOutLine(Mask, OUT_LINE_NO_PREFIX);

    //
    // Add the original data to the print buffer.
    //

    d = g_PrintBuf;

    for (i = 0; i < StringLength ; i++)
    {
        c = *(String + i);
        if ( c == '\n' )
        {
            g_LastProcessorToPrint = -1;
            *d++ = '\n';
            *d++ = '\r';
        }
        else
        {
            if ( c )
            {
                *d++ = c;
            }
        }
    }

    j = (DWORD)(d - g_PrintBuf);

    //
    // print the string.
    //

    MaskOut(Mask, "%*.*s", j, j, g_PrintBuf);

    SUSPEND_ENGINE();
}

VOID
DbgKdpHandlePromptString(
    IN PDBGKD_DEBUG_IO IoMessage
    )
{
    PSTR IoData;
    DWORD j;

    // This routine can be called during a wait when the
    // engine lock isn't held and can also be called when
    // the lock is held.  RESUME handles both of these
    // cases so that the lock is reacquired or reentered.
    RESUME_ENGINE();
    
    IoData = (PSTR)(IoMessage + 1);

    DbgKdpPrint(IoMessage->Processor,
                IoData,
                (USHORT)IoMessage->u.GetString.LengthOfPromptString,
                DEBUG_OUTPUT_DEBUGGEE_PROMPT
                );

    //
    // read the prompt data
    //

    j = GetInput(NULL, IoData,
                 IoMessage->u.GetString.LengthOfStringRead);
    if (j == 0)
    {
        j = IoMessage->u.GetString.LengthOfStringRead;
        memset(IoData, 0, j);
    }

    g_LastProcessorToPrint = -1;
    if ( j < (USHORT)IoMessage->u.GetString.LengthOfStringRead )
    {
        IoMessage->u.GetString.LengthOfStringRead = j;
    }

    //
    // Log the user's input
    //

    if (g_LogFile != -1)
    {
        _write(g_LogFile, IoData, j);
        _write(g_LogFile, "\n", 1);
    }

    SUSPEND_ENGINE();
    
    //
    // Send data to the debugger-target
    //

    g_DbgKdTransport->WritePacket(IoMessage, sizeof(*IoMessage),
                                  PACKET_TYPE_KD_DEBUG_IO,
                                  IoData,
                                  (USHORT)IoMessage->
                                  u.GetString.LengthOfStringRead);
}

VOID
DbgKdpPrintTrace(
    IN ULONG Processor,
    IN PUCHAR Data,
    IN USHORT DataLength,
    IN ULONG Mask
    )
{
    // This routine can be called during a wait when the
    // engine lock isn't held and can also be called when
    // the lock is held.  RESUME handles both of these
    // cases so that the lock is reacquired or reentered.
    RESUME_ENGINE();

    DebugClient* Client;
    
    // Find a client with output callbacks to use for output.
    for (Client = g_Clients; Client != NULL; Client = Client->m_Next)
    {
        if (Client->m_OutputCb != NULL)
        {
            break;
        }
    }
    if (Client == NULL)
    {
        // No clients have output callbacks so nobody
        // cares about output and we can just quit.
        goto Exit;
    }
    
    // Prefix the entire output block with the processor
    // number as we can't (and don't want to) get involved
    // in the individual messages.
    if (g_TargetNumberProcessors > 1 && Processor != g_LastProcessorToPrint)
    {
        g_LastProcessorToPrint = Processor;
        MaskOut(Mask, "%d", Processor);
    }

    if (g_WmiFormatTraceData == NULL)
    {
        AddExtensionDll("wmikd.dll", FALSE, NULL);
    }

    if (g_WmiFormatTraceData == NULL)
    {
        ErrOut("Missing or incorrect wmikd.dll - "
               "0x%X byte trace data buffer ignored\n",
               DataLength);
    }
    else
    {
        g_WmiFormatTraceData((PDEBUG_CONTROL)(IDebugControlN*)Client,
                             Mask, DataLength, Data);
    }

 Exit:
    SUSPEND_ENGINE();
}