mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
595 lines
18 KiB
595 lines
18 KiB
/*****************************************************************************
|
|
* MPU.cpp - UART miniport implementation
|
|
*****************************************************************************
|
|
* Copyright (c) 1998-2000 Microsoft Corporation. All rights reserved.
|
|
*
|
|
* Sept 98 MartinP .
|
|
*/
|
|
|
|
#include "private.h"
|
|
#include "ksdebug.h"
|
|
|
|
#define STR_MODULENAME "DMusUART:MPU: "
|
|
|
|
#define UartFifoOkForWrite(status) ((status & MPU401_DRR) == 0)
|
|
#define UartFifoOkForRead(status) ((status & MPU401_DSR) == 0)
|
|
|
|
typedef struct
|
|
{
|
|
CMiniportDMusUART *Miniport;
|
|
PUCHAR PortBase;
|
|
PVOID BufferAddress;
|
|
ULONG Length;
|
|
PULONG BytesRead;
|
|
}
|
|
SYNCWRITECONTEXT, *PSYNCWRITECONTEXT;
|
|
|
|
BOOLEAN TryMPU(IN PUCHAR PortBase);
|
|
NTSTATUS WriteMPU(IN PUCHAR PortBase,IN BOOLEAN IsCommand,IN UCHAR Value);
|
|
|
|
#pragma code_seg("PAGE")
|
|
// make sure we're in UART mode
|
|
NTSTATUS ResetHardware(PUCHAR portBase)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return WriteMPU(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 CMiniportDMusUART::InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS ntStatus;
|
|
if (m_UseIRQ)
|
|
{
|
|
ntStatus = interruptSync->CallSynchronizedRoutine(InitMPU,PVOID(portBase));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = InitMPU(NULL,PVOID(portBase));
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
//
|
|
// Start the UART (this should trigger an interrupt).
|
|
//
|
|
ntStatus = ResetHardware(portBase);
|
|
}
|
|
else
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("*** InitMPU returned with ntStatus 0x%08x ***",ntStatus));
|
|
}
|
|
|
|
m_fMPUInitialized = NT_SUCCESS(ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#pragma code_seg()
|
|
/*****************************************************************************
|
|
* InitMPU()
|
|
*****************************************************************************
|
|
* Synchronized routine to initialize the MPU401.
|
|
*/
|
|
NTSTATUS
|
|
InitMPU
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID DynamicContext
|
|
)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_BLAB, ("InitMPU"));
|
|
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 = WriteMPU(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) WriteMPU(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()
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::Write()
|
|
*****************************************************************************
|
|
* Writes outgoing MIDI data.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CMiniportDMusUARTStream::
|
|
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(SynchronizedDMusMPUWrite,PVOID(&context));
|
|
}
|
|
else // !m_UseIRQ
|
|
{
|
|
ntStatus = SynchronizedDMusMPUWrite(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()
|
|
/*****************************************************************************
|
|
* SynchronizedDMusMPUWrite()
|
|
*****************************************************************************
|
|
* Writes outgoing MIDI data.
|
|
*/
|
|
NTSTATUS
|
|
SynchronizedDMusMPUWrite
|
|
(
|
|
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 a byte. Better to come back later)
|
|
readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
|
|
while ( (*(context->BytesRead) < context->Length)
|
|
&& ( TryMPU(context->PortBase)
|
|
|| (*(context->BytesRead)%3)
|
|
) )
|
|
{
|
|
ntStatus = WriteMPU(context->PortBase,DATA,*pChar);
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
pChar++;
|
|
*(context->BytesRead) = *(context->BytesRead) + 1;
|
|
// readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
|
|
}
|
|
else
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("SynchronizedDMusMPUWrite failed (0x%08x)",ntStatus));
|
|
break;
|
|
}
|
|
}
|
|
readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
|
|
return ntStatus;
|
|
}
|
|
|
|
#define kMPUPollTimeout 2
|
|
|
|
#pragma code_seg()
|
|
/*****************************************************************************
|
|
* TryMPU()
|
|
*****************************************************************************
|
|
* See if the MPU401 is free.
|
|
*/
|
|
BOOLEAN
|
|
TryMPU
|
|
(
|
|
IN PUCHAR PortBase
|
|
)
|
|
{
|
|
BOOLEAN success;
|
|
USHORT numPolls;
|
|
UCHAR status;
|
|
|
|
_DbgPrintF(DEBUGLVL_BLAB, ("TryMPU"));
|
|
numPolls = 0;
|
|
|
|
while (numPolls < kMPUPollTimeout)
|
|
{
|
|
status = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
|
|
|
|
if (UartFifoOkForWrite(status)) // Is this a good time to write data?
|
|
{
|
|
break;
|
|
}
|
|
numPolls++;
|
|
}
|
|
if (numPolls >= kMPUPollTimeout)
|
|
{
|
|
success = FALSE;
|
|
_DbgPrintF(DEBUGLVL_BLAB, ("TryMPU failed"));
|
|
}
|
|
else
|
|
{
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
#pragma code_seg()
|
|
/*****************************************************************************
|
|
* WriteMPU()
|
|
*****************************************************************************
|
|
* Write a byte out to the MPU401.
|
|
*/
|
|
NTSTATUS
|
|
WriteMPU
|
|
(
|
|
IN PUCHAR PortBase,
|
|
IN BOOLEAN IsCommand,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_BLAB, ("WriteMPU"));
|
|
NTSTATUS ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
|
|
if (!PortBase)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("O: PortBase is zero\n"));
|
|
return ntStatus;
|
|
}
|
|
PUCHAR deviceAddr = PortBase + MPU401_REG_DATA;
|
|
|
|
if (IsCommand)
|
|
{
|
|
deviceAddr = PortBase + MPU401_REG_COMMAND;
|
|
}
|
|
|
|
ULONGLONG startTime = PcGetTimeInterval(0);
|
|
|
|
while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
|
|
{
|
|
UCHAR status
|
|
= READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
|
|
|
|
if (UartFifoOkForWrite(status)) // Is this a good time to write data?
|
|
{ // yep (Jon comment)
|
|
WRITE_PORT_UCHAR(deviceAddr,Value);
|
|
_DbgPrintF(DEBUGLVL_BLAB, ("WriteMPU emitted 0x%02x",Value));
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
#pragma code_seg()
|
|
/*****************************************************************************
|
|
* SnapTimeStamp()
|
|
*****************************************************************************
|
|
*
|
|
* At synchronized execution to ISR, copy miniport's volatile m_InputTimeStamp
|
|
* to stream's m_SnapshotTimeStamp and zero m_InputTimeStamp.
|
|
*
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream)
|
|
{
|
|
CMiniportDMusUARTStream *pMPStream = (CMiniportDMusUARTStream *)pStream;
|
|
|
|
// cache the timestamp
|
|
pMPStream->m_SnapshotTimeStamp = pMPStream->m_pMiniport->m_InputTimeStamp;
|
|
|
|
// if the window is closed, zero the timestamp
|
|
if (pMPStream->m_pMiniport->m_MPUInputBufferHead ==
|
|
pMPStream->m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
pMPStream->m_pMiniport->m_InputTimeStamp = 0;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::SourceEvtsToPort()
|
|
*****************************************************************************
|
|
*
|
|
* Reads incoming MIDI data, feeds into DMus events.
|
|
* No need to touch the hardware, just read from our SW FIFO.
|
|
*
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CMiniportDMusUARTStream::SourceEvtsToPort()
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
_DbgPrintF(DEBUGLVL_BLAB, ("SourceEvtsToPort"));
|
|
|
|
if (m_fCapture)
|
|
{
|
|
ntStatus = STATUS_SUCCESS;
|
|
if (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
PDMUS_KERNEL_EVENT aDMKEvt,eventTail,eventHead = NULL;
|
|
|
|
while (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
(void) m_AllocatorMXF->GetMessage(&aDMKEvt);
|
|
if (!aDMKEvt)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort can't allocate DMKEvt"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// put this event at the end of the list
|
|
if (!eventHead)
|
|
{
|
|
eventHead = aDMKEvt;
|
|
}
|
|
else
|
|
{
|
|
eventTail = eventHead;
|
|
while (eventTail->pNextEvt)
|
|
{
|
|
eventTail = eventTail->pNextEvt;
|
|
}
|
|
eventTail->pNextEvt = aDMKEvt;
|
|
}
|
|
// read all the bytes out of the buffer, into event(s)
|
|
for (aDMKEvt->cbEvent = 0; aDMKEvt->cbEvent < sizeof(PBYTE); aDMKEvt->cbEvent++)
|
|
{
|
|
if (m_pMiniport->m_MPUInputBufferHead == m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
// _DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort m_MPUInputBufferHead met m_MPUInputBufferTail, overrun"));
|
|
break;
|
|
}
|
|
aDMKEvt->uData.abData[aDMKEvt->cbEvent] = m_pMiniport->m_MPUInputBuffer[m_pMiniport->m_MPUInputBufferHead];
|
|
m_pMiniport->m_MPUInputBufferHead++;
|
|
if (m_pMiniport->m_MPUInputBufferHead >= kMPUInputBufferSize)
|
|
{
|
|
m_pMiniport->m_MPUInputBufferHead = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_pMiniport->m_UseIRQ)
|
|
{
|
|
ntStatus = m_pMiniport->m_pInterruptSync->CallSynchronizedRoutine(SnapTimeStamp,PVOID(this));
|
|
}
|
|
else // !m_UseIRQ
|
|
{
|
|
ntStatus = SnapTimeStamp(NULL,PVOID(this));
|
|
} // !m_UseIRQ
|
|
aDMKEvt = eventHead;
|
|
while (aDMKEvt)
|
|
{
|
|
aDMKEvt->ullPresTime100ns = m_SnapshotTimeStamp;
|
|
aDMKEvt->usChannelGroup = 1;
|
|
aDMKEvt->usFlags = DMUS_KEF_EVENT_INCOMPLETE;
|
|
aDMKEvt = aDMKEvt->pNextEvt;
|
|
}
|
|
(void)m_sinkMXF->PutMessage(eventHead);
|
|
}
|
|
}
|
|
else // render stream
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort called on render stream"));
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
#pragma code_seg()
|
|
/*****************************************************************************
|
|
* DMusMPUInterruptServiceRoutine()
|
|
*****************************************************************************
|
|
* ISR.
|
|
*/
|
|
NTSTATUS
|
|
DMusMPUInterruptServiceRoutine
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID DynamicContext
|
|
)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_BLAB, ("DMusMPUInterruptServiceRoutine"));
|
|
ULONGLONG startTime;
|
|
|
|
ASSERT(DynamicContext);
|
|
|
|
NTSTATUS ntStatus;
|
|
BOOL newBytesAvailable;
|
|
CMiniportDMusUART *that;
|
|
NTSTATUS clockStatus;
|
|
|
|
that = (CMiniportDMusUART *) DynamicContext;
|
|
newBytesAvailable = FALSE;
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
UCHAR portStatus = 0xff;
|
|
|
|
//
|
|
// Read the MPU status byte.
|
|
//
|
|
if (that->m_pPortBase)
|
|
{
|
|
portStatus =
|
|
READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
|
|
|
|
//
|
|
// If there is outstanding work to do and there is a port-driver for
|
|
// the MPU miniport...
|
|
//
|
|
if (UartFifoOkForRead(portStatus) && that->m_pPort)
|
|
{
|
|
startTime = PcGetTimeInterval(0);
|
|
while ( (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
|
|
&& (UartFifoOkForRead(portStatus)) )
|
|
{
|
|
UCHAR uDest = READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_DATA);
|
|
if ( (that->m_KSStateInput == KSSTATE_RUN)
|
|
&& (that->m_NumCaptureStreams)
|
|
)
|
|
{
|
|
LONG buffHead = that->m_MPUInputBufferHead;
|
|
if ( (that->m_MPUInputBufferTail + 1 == buffHead)
|
|
|| (that->m_MPUInputBufferTail + 1 - kMPUInputBufferSize == buffHead))
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("*****MPU Input Buffer Overflow*****"));
|
|
}
|
|
else
|
|
{
|
|
if (!that->m_InputTimeStamp)
|
|
{
|
|
clockStatus = that->m_MasterClock->GetTime(&that->m_InputTimeStamp);
|
|
if (STATUS_SUCCESS != clockStatus)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("GetTime failed for clock 0x%08x",that->m_MasterClock));
|
|
}
|
|
}
|
|
newBytesAvailable = TRUE;
|
|
// ...place the data in our FIFO...
|
|
that->m_MPUInputBuffer[that->m_MPUInputBufferTail] = uDest;
|
|
ASSERT(that->m_MPUInputBufferTail < kMPUInputBufferSize);
|
|
|
|
that->m_MPUInputBufferTail++;
|
|
if (that->m_MPUInputBufferTail >= kMPUInputBufferSize)
|
|
{
|
|
that->m_MPUInputBufferTail = 0;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Look for more MIDI data.
|
|
//
|
|
portStatus =
|
|
READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
|
|
} // either there's no data or we ran too long
|
|
if (newBytesAvailable)
|
|
{
|
|
//
|
|
// ...notify the MPU port driver that we have bytes.
|
|
//
|
|
that->m_pPort->Notify(that->m_pServiceGroup);
|
|
}
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|