📄 mpu.cpp
字号:
/*****************************************************************************
* MPU.cpp - UART miniport implementation
*****************************************************************************
* Copyright (c) Microsoft Corporation 1998-1999. All rights reserved.
*
* Sept 98 MartinP .
*/
#include "private.h"
#include "ksdebug.h"
#define STR_MODULENAME "UART:MPU: "
//
// MPU401 ports
//
#define MPU401_REG_DATA 0x00 // Data I/O
#define MPU401_REG_COMMAND 0x01 // Command Register (w/o)
#define MPU401_REG_STATUS 0x01 // Status Register (r/o)
#define MPU401_CMD_RESET 0xFF // Reset command
#define MPU401_CMD_UART 0x3F // Switch to UART mode
#define MPU401_DRR 0x40 // Output ready (for command or data)
#define MPU401_DSR 0x80 // Input ready (for data)
#define UartFifoOkForWrite(status) ((status & MPU401_DRR) == 0)
#define UartFifoOkForRead(status) ((status & MPU401_DSR) == 0)
typedef struct
{
CMiniportMidiUart *Miniport;
PUCHAR PortBase;
PVOID BufferAddress;
ULONG Length;
PULONG BytesRead;
}
SYNCWRITECONTEXT, *PSYNCWRITECONTEXT;
typedef struct
{
PVOID BufferAddress;
ULONG Length;
PULONG BytesRead;
PULONG pMPUInputBufferHead;
ULONG MPUInputBufferTail;
PUCHAR MPUInputBuffer;
}
DEFERREDREADCONTEXT, *PDEFERREDREADCONTEXT;
NTSTATUS DeferredLegacyRead(IN PINTERRUPTSYNC InterruptSync,IN PVOID DynamicContext);
BOOLEAN TryLegacyMPU(IN PUCHAR PortBase);
NTSTATUS WriteLegacyMPU(IN PUCHAR PortBase,IN BOOLEAN IsCommand,IN UCHAR Value);
#pragma code_seg("PAGE")
// make sure we're in UART mode
NTSTATUS ResetMPUHardware(PUCHAR portBase)
{
PAGED_CODE();
return (WriteLegacyMPU(portBase,COMMAND,MPU401_CMD_UART));
}
#pragma code_seg("PAGE")
//
// We initialize the UART with interrupts suppressed so we don't
// try to service the chip prematurely.
//
NTSTATUS CMiniportMidiUart::InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase)
{
PAGED_CODE();
NTSTATUS ntStatus;
if (m_UseIRQ)
{
ntStatus = interruptSync->CallSynchronizedRoutine(InitLegacyMPU,PVOID(portBase));
}
else
{
ntStatus = InitLegacyMPU(NULL,PVOID(portBase));
}
if (NT_SUCCESS(ntStatus))
{
//
// Start the UART (this should trigger an interrupt).
//
ntStatus = ResetMPUHardware(portBase);
}
else
{
_DbgPrintF(DEBUGLVL_TERSE,("*** InitLegacyMPU returned with ntStatus 0x%08x ***",ntStatus));
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* InitLegacyMPU()
*****************************************************************************
* Synchronized routine to initialize the MPU401.
*/
NTSTATUS
InitLegacyMPU
(
IN PINTERRUPTSYNC InterruptSync,
IN PVOID DynamicContext
)
{
_DbgPrintF(DEBUGLVL_BLAB, ("InitLegacyMPU"));
if (!DynamicContext)
{
return STATUS_INVALID_PARAMETER_2;
}
PUCHAR portBase = PUCHAR(DynamicContext);
UCHAR status;
ULONGLONG startTime;
BOOLEAN success;
NTSTATUS ntStatus = STATUS_SUCCESS;
//
// Reset the card (puts it into "smart mode")
//
ntStatus = WriteLegacyMPU(portBase,COMMAND,MPU401_CMD_RESET);
// wait for the acknowledgement
// NOTE: When the Ack arrives, it will trigger an interrupt.
// Normally the DPC routine would read in the ack byte and we
// would never see it, however since we have the hardware locked (HwEnter),
// we can read the port before the DPC can and thus we receive the Ack.
startTime = PcGetTimeInterval(0);
success = FALSE;
while(PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
{
status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
if (UartFifoOkForRead(status)) // Is data waiting?
{
READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
success = TRUE; // don't need to do more
break;
}
KeStallExecutionProcessor(25); // microseconds
}
#if (DBG)
if (!success)
{
_DbgPrintF(DEBUGLVL_VERBOSE,("First attempt to reset the MPU didn't get ACKed.\n"));
}
#endif // (DBG)
// NOTE: We cannot check the ACK byte because if the card was already in
// UART mode it will not send an ACK but it will reset.
// reset the card again
(void) WriteLegacyMPU(portBase,COMMAND,MPU401_CMD_RESET);
// wait for ack (again)
startTime = PcGetTimeInterval(0); // This might take a while
BYTE dataByte = 0;
success = FALSE;
while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
{
status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
if (UartFifoOkForRead(status)) // Is data waiting?
{
dataByte = READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
success = TRUE; // don't need to do more
break;
}
KeStallExecutionProcessor(25);
}
if ((0xFE != dataByte) || !success) // Did we succeed? If no second ACK, something is hosed
{
_DbgPrintF(DEBUGLVL_TERSE,("Second attempt to reset the MPU didn't get ACKed.\n"));
_DbgPrintF(DEBUGLVL_TERSE,("Init Reset failure error. Ack = %X", ULONG(dataByte) ) );
ntStatus = STATUS_IO_DEVICE_ERROR;
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* CMiniportMidiStreamUart::Write()
*****************************************************************************
* Writes outgoing MIDI data.
*/
STDMETHODIMP
CMiniportMidiStreamUart::
Write
(
IN PVOID BufferAddress,
IN ULONG Length,
OUT PULONG BytesWritten
)
{
_DbgPrintF(DEBUGLVL_BLAB, ("Write"));
ASSERT(BytesWritten);
if (!BufferAddress)
{
Length = 0;
}
NTSTATUS ntStatus = STATUS_SUCCESS;
if (!m_fCapture)
{
PUCHAR pMidiData;
ULONG count;
count = 0;
pMidiData = PUCHAR(BufferAddress);
if (Length)
{
SYNCWRITECONTEXT context;
context.Miniport = (m_pMiniport);
context.PortBase = m_pPortBase;
context.BufferAddress = pMidiData;
context.Length = Length;
context.BytesRead = &count;
if (m_pMiniport->m_UseIRQ)
{
ntStatus = m_pMiniport->m_pInterruptSync->
CallSynchronizedRoutine(SynchronizedMPUWrite,PVOID(&context));
}
else // !m_UseIRQ
{
ntStatus = SynchronizedMPUWrite(NULL,PVOID(&context));
} // !m_UseIRQ
if (count == 0)
{
m_NumFailedMPUTries++;
if (m_NumFailedMPUTries >= 100)
{
ntStatus = STATUS_IO_DEVICE_ERROR;
m_NumFailedMPUTries = 0;
}
}
else
{
m_NumFailedMPUTries = 0;
}
} // if we have data at all
*BytesWritten = count;
}
else // called write on the read stream
{
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* SynchronizedMPUWrite()
*****************************************************************************
* Writes outgoing MIDI data.
*/
NTSTATUS
SynchronizedMPUWrite
(
IN PINTERRUPTSYNC InterruptSync,
IN PVOID syncWriteContext
)
{
PSYNCWRITECONTEXT context;
context = (PSYNCWRITECONTEXT)syncWriteContext;
ASSERT(context->Miniport);
ASSERT(context->PortBase);
ASSERT(context->BufferAddress);
ASSERT(context->Length);
ASSERT(context->BytesRead);
PUCHAR pChar = PUCHAR(context->BufferAddress);
NTSTATUS ntStatus,readStatus;
ntStatus = STATUS_SUCCESS;
//
// while we're not there yet, and
// while we don't have to wait on an aligned byte (including 0)
// (we never wait on an aligned byte. Better to come back later)
// if (context->Miniport->m_NumCaptureStreams)
{
readStatus = MPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
}
while ( (*(context->BytesRead) < context->Length)
&& (TryLegacyMPU(context->PortBase)
|| (*(context->BytesRead)%4)
) )
{
ntStatus = WriteLegacyMPU(context->PortBase,DATA,*pChar);
if (NT_SUCCESS(ntStatus))
{
pChar++;
*(context->BytesRead) = *(context->BytesRead) + 1;
readStatus = MPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -