Sample Code

Windows Driver Samples/ DirectMusic UART Driver Sample/ C++/ mpu.cpp/

/*****************************************************************************
 * MPU.cpp - UART miniport implementation
 *****************************************************************************
 * Copyright (c) 1998-2000 Microsoft Corporation.  All rights reserved.
 *
 */

#pragma warning (disable : 4127)

#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
)
{
    UNREFERENCED_PARAMETER(InterruptSync);

    _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)
{
    UNREFERENCED_PARAMETER(InterruptSync);

    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
)
{
    UNREFERENCED_PARAMETER(InterruptSync);

    _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%08p",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;
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS