Author: janderwald Date: Mon Nov 1 00:26:59 2010 New Revision: 49389
URL: http://svn.reactos.org/svn/reactos?rev=49389&view=rev Log: [PORTCLS] - Add MPU-401 Uart miniport driver from DDK 2003 sample
Modified: trunk/reactos/drivers/wdm/audio/backpln/portcls/guids.cpp trunk/reactos/drivers/wdm/audio/backpln/portcls/miniport_dmus.cpp trunk/reactos/drivers/wdm/audio/backpln/portcls/private.hpp
Modified: trunk/reactos/drivers/wdm/audio/backpln/portcls/guids.cpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/wdm/audio/backpln/p... ============================================================================== --- trunk/reactos/drivers/wdm/audio/backpln/portcls/guids.cpp [iso-8859-1] (original) +++ trunk/reactos/drivers/wdm/audio/backpln/portcls/guids.cpp [iso-8859-1] Mon Nov 1 00:26:59 2010 @@ -35,7 +35,6 @@ const GUID CLSID_MiniportDriverFmSynth = {0xb4c90ae0L, 0x5791, 0x11d0, {0x86, 0xf9, 0x00, 0xa0, 0xc9, 0x11, 0xb5, 0x44}}; const GUID CLSID_MiniportDriverFmSynthWithVol = {0xe5a3c139L, 0xf0f2, 0x11d1, {0x81, 0xaf, 0x00, 0x60, 0x08, 0x33, 0x16, 0xc1}};
- const GUID IID_IPort = {0xb4c90a25L, 0x5791, 0x11d0, {0x86, 0xf9, 0x00, 0xa0, 0xc9, 0x11, 0xb5, 0x44}}; const GUID IID_IDrmPort = {0x286D3DF8L, 0xCA22, 0x4E2E, {0xB9, 0xBC, 0x20, 0xB4, 0xF0, 0xE2, 0x01, 0xCE}}; const GUID IID_IDrmPort2 = {0x1ACCE59CL, 0x7311, 0x4B6B, {0x9F, 0xBA, 0xCC, 0x3B, 0xA5, 0x9A, 0xCD, 0xCE}}; @@ -46,6 +45,7 @@ const GUID IID_IPortDMus = {0xc096df9cL, 0xfb09, 0x11d1, {0x81, 0xb0, 0x00, 0x60, 0x08, 0x33, 0x16, 0xc1}}; const GUID IID_IAdapterPowerManagement = {0x793417D0L, 0x35FE, 0x11D1, {0xAD, 0x08, 0x00, 0xA0, 0xC9, 0x0A, 0xB1, 0xB0}};
+const GUID IID_IMiniport = {0xb4c90a24, 0x5791, 0x11d0, {0x86, 0xf9, 0x00, 0xa0, 0xc9, 0x11, 0xb5, 0x44}}; const GUID IID_IMiniportWaveCyclic = {0xb4c90a27L, 0x5791, 0x11d0, {0x86, 0xf9, 0x00, 0xa0, 0xc9, 0x11, 0xb5, 0x44}}; const GUID IID_IPortWaveCyclic = {0xb4c90a26L, 0x5791, 0x11d0, {0x86, 0xf9, 0x00, 0xa0, 0xc9, 0x11, 0xb5, 0x44}}; const GUID IID_IResourceList = {0x22C6AC60L, 0x851B, 0x11D0, {0x9A, 0x7F, 0x00, 0xAA, 0x00, 0x38, 0xAC, 0xFE}}; @@ -59,6 +59,7 @@ const GUID IID_IPortClsVersion = {0x7D89A7BBL, 0x869B, 0x4567, {0x8D, 0xBE, 0x1E, 0x16, 0x8C, 0xC8, 0x53, 0xDE}}; const GUID IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x46}}; const GUID IID_IPortEvents = {0xA80F29C4L, 0x5498, 0x11D2, {0x95, 0xD9, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}}; +const GUID IID_IMusicTechnology = {0x80396C3C, 0xCBCB, 0x409B, {0x9F, 0x65, 0x4F, 0x1E, 0x74, 0x67, 0xCD, 0xAF}};
const GUID KSNAME_PIN = {0x146F1A80, 0x4791, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID IID_IDrmAudioStream = {0x1915c967, 0x3299, 0x48cb, {0xa3, 0xe4, 0x69, 0xfd, 0x1d, 0x1b, 0x30, 0x6e}}; @@ -69,7 +70,13 @@ const GUID KS_CATEGORY_RENDER = {0x65E8773E, 0x8F56, 0x11D0, {0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KS_CATEGORY_CAPTURE = {0x65E8773D, 0x8F56, 0x11D0, {0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; const GUID KS_CATEGORY_TOPOLOGY = {0xDDA54A40, 0x1E4C, 0x11D1, {0xA0, 0x50, 0x40, 0x57, 0x05, 0xC1, 0x00, 0x00}}; +const GUID KSCATEGORY_AUDIO = {0x6994AD04L, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+ +const GUID KSNODETYPE_SYNTHESIZER = {0xDFF220F3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; +const GUID KSAUDFNAME_DMUSIC_MPU_IN = {0xB2EC0A7D, 0xBAC9, 0x11d2, {0xB7, 0xA8, 0x00, 0x60, 0x08, 0x33, 0x16, 0xC1}}; +const GUID KSAUDFNAME_MIDI = {0x185FEDF8L, 0x9905, 0x11D1, {0x95, 0xA9, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}}; +const GUID KSAUDFNAME_DMUSIC_MPU_OUT = {0xA4DF0EB5, 0xBAC9, 0x11d2, {0xB7, 0xA8, 0x00, 0x60, 0x08, 0x33, 0x16, 0xC1}};
const GUID KSDATAFORMAT_TYPE_AUDIO = {0x73647561L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; @@ -80,13 +87,16 @@ const GUID KSPROPSETID_Connection = {0x1D58C920L, 0xAC9B, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID KSPROPTYPESETID_General = {0x97E99BA0L, 0xBDEA, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID KSPROPSETID_DrmAudioStream = {0x2f2c8ddd, 0x4198, 0x4fac, {0xba, 0x29, 0x61, 0xbb, 0x05, 0xb7, 0xde, 0x06}}; +const GUID KSPROPSETID_Synth = {0xfedfae25L, 0xe46e, 0x11d1, {0xaa, 0xce, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12}}; +const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
const GUID KSEVENTSETID_LoopedStreaming = {0x4682B940L, 0xC6EF, 0x11D0, {0x96, 0xD8, 0x00, 0xAA, 0x00, 0x51, 0xE5, 0x1D}}; const GUID KSEVENTSETID_Connection = {0x7f4bcbe0L, 0x9ea5, 0x11cf, {0xa5, 0xd6, 0x28, 0xdb, 0x04, 0xc1, 0x00, 0x00}};
const GUID IID_IAllocatorMXF = {0xa5f0d62cL, 0xb30f, 0x11d2, {0xb7, 0xa3, 0x00, 0x60, 0x08, 0x33, 0x16, 0xc1}}; +const GUID IID_IMXF = {0xc096df9e, 0xfb09, 0x11d1, {0x81, 0xb0, 0x00, 0x60, 0x08, 0x33, 0x16, 0xc1}}; +const GUID KSMUSIC_TECHNOLOGY_PORT = {0x86C92E60, 0x62E8, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
-const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}; /// /// undocumented guids
Modified: trunk/reactos/drivers/wdm/audio/backpln/portcls/miniport_dmus.cpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/wdm/audio/backpln/p... ============================================================================== --- trunk/reactos/drivers/wdm/audio/backpln/portcls/miniport_dmus.cpp [iso-8859-1] (original) +++ trunk/reactos/drivers/wdm/audio/backpln/portcls/miniport_dmus.cpp [iso-8859-1] Mon Nov 1 00:26:59 2010 @@ -1,15 +1,92 @@ -/* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Kernel Streaming - * FILE: drivers/wdm/audio/backpln/portcls/miniport_dmus.cpp - * PURPOSE: DirectMusic miniport - * PROGRAMMER: Johannes Anderwald - */ - #include "private.hpp"
-class CMiniportDMus : public IMiniportDMus -{ +// + for absolute / - for relative +#define kOneMillisec (10 * 1000) + +// +// MPU401 ports +// +#define MPU401_REG_STATUS 0x01 // Status register +#define MPU401_DRR 0x40 // Output ready (for command or data) + // if this bit is set, the output FIFO is FULL +#define MPU401_DSR 0x80 // Input ready (for data) + // if this bit is set, the input FIFO is empty + +#define MPU401_REG_DATA 0x00 // Data in +#define MPU401_REG_COMMAND 0x01 // Commands +#define MPU401_CMD_RESET 0xFF // Reset command +#define MPU401_CMD_UART 0x3F // Switch to UART mod + + +/***************************************************************************** + * Prototypes + */ + +NTSTATUS NTAPI InitMPU(IN PINTERRUPTSYNC InterruptSync,IN PVOID DynamicContext); +NTSTATUS ResetHardware(PUCHAR portBase); +NTSTATUS ValidatePropertyRequest(IN PPCPROPERTY_REQUEST pRequest, IN ULONG ulValueSize, IN BOOLEAN fValueRequired); +NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST PropertyRequest); +NTSTATUS NTAPI DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext); +VOID NTAPI DMusUARTTimerDPC(PKDPC Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2); +NTSTATUS NTAPI SynchronizedDMusMPUWrite(PINTERRUPTSYNC InterruptSync,PVOID syncWriteContext); +/***************************************************************************** + * Constants + */ + +const BOOLEAN COMMAND = TRUE; +const BOOLEAN DATA = FALSE; + +const ULONG kMPUInputBufferSize = 128; + + +/***************************************************************************** + * Classes + */ + +/***************************************************************************** + * CMiniportDMusUART + ***************************************************************************** + * MPU-401 miniport. This object is associated with the device and is + * created when the device is started. The class inherits IMiniportDMus + * so it can expose this interface and CUnknown so it automatically gets + * reference counting and aggregation support. + */ +class CMiniportDMusUART +: public IMiniportDMus, + public IMusicTechnology, + public IPowerNotify +{ +private: + LONG m_Ref; // Reference count + KSSTATE m_KSStateInput; // Miniport state (RUN/PAUSE/ACQUIRE/STOP) + PPORTDMUS m_pPort; // Callback interface. + PUCHAR m_pPortBase; // Base port address. + PINTERRUPTSYNC m_pInterruptSync; // Interrupt synchronization object. + PSERVICEGROUP m_pServiceGroup; // Service group for capture. + PMASTERCLOCK m_MasterClock; // for input data + REFERENCE_TIME m_InputTimeStamp; // capture data timestamp + USHORT m_NumRenderStreams; // Num active render streams. + USHORT m_NumCaptureStreams; // Num active capture streams. + ULONG m_MPUInputBufferHead; // Index of the newest byte in the FIFO. + ULONG m_MPUInputBufferTail; // Index of the oldest empty space in the FIFO. + GUID m_MusicFormatTechnology; + POWER_STATE m_PowerState; // Saved power state (D0 = full power, D3 = off) + BOOLEAN m_fMPUInitialized; // Is the MPU HW initialized. + BOOLEAN m_UseIRQ; // FALSE if no IRQ is used for MIDI. + UCHAR m_MPUInputBuffer[kMPUInputBufferSize]; // Internal SW FIFO. + + /************************************************************************* + * CMiniportDMusUART methods + * + * These are private member functions used internally by the object. + * See MINIPORT.CPP for specific descriptions. + */ + NTSTATUS ProcessResources + ( + IN PRESOURCELIST ResourceList + ); + NTSTATUS InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase); + public: STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
@@ -29,107 +106,2584 @@ } return m_Ref; } - IMP_IMiniportDMus; - CMiniportDMus(IUnknown *OuterUnknown){} - virtual ~CMiniportDMus(){} - -protected: - LONG m_Ref; - + + CMiniportDMusUART(IUnknown * Unknown){} + ~CMiniportDMusUART(); + + /************************************************************************* + * IMiniport methods + */ + STDMETHODIMP_(NTSTATUS) + GetDescription + ( OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor + ); + STDMETHODIMP_(NTSTATUS) + DataRangeIntersection + ( IN ULONG PinId + , IN PKSDATARANGE DataRange + , IN PKSDATARANGE MatchingDataRange + , IN ULONG OutputBufferLength + , OUT PVOID ResultantFormat + , OUT PULONG ResultantFormatLength + ) + { + return STATUS_NOT_IMPLEMENTED; + } + + /************************************************************************* + * IMiniportDMus methods + */ + STDMETHODIMP_(NTSTATUS) Init + ( + IN PUNKNOWN UnknownAdapter, + IN PRESOURCELIST ResourceList, + IN PPORTDMUS Port, + OUT PSERVICEGROUP * ServiceGroup + ); + STDMETHODIMP_(NTSTATUS) NewStream + ( + OUT PMXF * Stream, + IN PUNKNOWN OuterUnknown OPTIONAL, + IN POOL_TYPE PoolType, + IN ULONG PinID, + IN DMUS_STREAM_TYPE StreamType, + IN PKSDATAFORMAT DataFormat, + OUT PSERVICEGROUP * ServiceGroup, + IN PAllocatorMXF AllocatorMXF, + IN PMASTERCLOCK MasterClock, + OUT PULONGLONG SchedulePreFetch + ); + STDMETHODIMP_(void) Service + ( void + ); + + /************************************************************************* + * IMusicTechnology methods + */ + IMP_IMusicTechnology; + + /************************************************************************* + * IPowerNotify methods + */ + IMP_IPowerNotify; + + /************************************************************************* + * Friends + */ + friend class CMiniportDMusUARTStream; + friend NTSTATUS + DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext); + friend NTSTATUS + SynchronizedDMusMPUWrite(PINTERRUPTSYNC InterruptSync,PVOID syncWriteContext); + friend VOID NTAPI + DMusUARTTimerDPC(PKDPC Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2); + friend NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST PropertyRequest); + friend STDMETHODIMP_(NTSTATUS) SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream); };
-// IUnknown methods - +/***************************************************************************** + * CMiniportDMusUARTStream + ***************************************************************************** + * MPU-401 miniport stream. This object is associated with the pin and is + * created when the pin is instantiated. It inherits IMXF + * so it can expose this interface and CUnknown so it automatically gets + * reference counting and aggregation support. + */ +class CMiniportDMusUARTStream : public IMXF +{ +private: + LONG m_Ref; // Reference Count + CMiniportDMusUART * m_pMiniport; // Parent. + REFERENCE_TIME m_SnapshotTimeStamp; // Current snapshot of miniport's input timestamp. + PUCHAR m_pPortBase; // Base port address. + BOOLEAN m_fCapture; // Whether this is capture. + long m_NumFailedMPUTries; // Deadman timeout for MPU hardware. + PAllocatorMXF m_AllocatorMXF; // source/sink for DMus structs + PMXF m_sinkMXF; // sink for DMus capture + PDMUS_KERNEL_EVENT m_DMKEvtQueue; // queue of waiting events + ULONG m_NumberOfRetries; // Number of consecutive times the h/w was busy/full + ULONG m_DMKEvtOffset; // offset into the event + KDPC m_Dpc; // DPC for timer + KTIMER m_TimerEvent; // timer + BOOL m_TimerQueued; // whether a timer has been set + KSPIN_LOCK m_DpcSpinLock; // protects the ConsumeEvents DPC + + STDMETHODIMP_(NTSTATUS) SourceEvtsToPort(); + STDMETHODIMP_(NTSTATUS) ConsumeEvents(); + STDMETHODIMP_(NTSTATUS) PutMessageLocked(PDMUS_KERNEL_EVENT pDMKEvt); + +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + ~CMiniportDMusUARTStream(); + + STDMETHODIMP_(NTSTATUS) Init + ( + IN CMiniportDMusUART * pMiniport, + IN PUCHAR pPortBase, + IN BOOLEAN fCapture, + IN PAllocatorMXF allocatorMXF, + IN PMASTERCLOCK masterClock + ); + + NTSTATUS HandlePortParams + ( + IN PPCPROPERTY_REQUEST Request + ); + + /************************************************************************* + * IMiniportStreamDMusUART methods + */ + IMP_IMXF; + + STDMETHODIMP_(NTSTATUS) Write + ( + IN PVOID BufferAddress, + IN ULONG BytesToWrite, + OUT PULONG BytesWritten + ); + + friend VOID NTAPI + DMusUARTTimerDPC + ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + friend NTSTATUS PropertyHandler_Synth(IN PPCPROPERTY_REQUEST); + friend STDMETHODIMP_(NTSTATUS) SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream); +}; + +/***************************************************************************** + * miniport.cpp - UART miniport implementation + ***************************************************************************** + * Copyright (c) 1997-2000 Microsoft Corporation. All Rights Reserved. + * + * Feb 98 MartinP -- based on UART, began deltas for DirectMusic. + */ + +#define STR_MODULENAME "DMusUART:Miniport: " + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +#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; + +/***************************************************************************** + * PinDataRangesStreamLegacy + * PinDataRangesStreamDMusic + ***************************************************************************** + * Structures indicating range of valid format values for live pins. + */ +static +KSDATARANGE_MUSIC PinDataRangesStreamLegacy = +{ + { + { + sizeof(KSDATARANGE_MUSIC), + 0, + 0, + 0, + {STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC)}, + {STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI)}, + {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)} + } + }, + {STATICGUIDOF(KSMUSIC_TECHNOLOGY_PORT)}, + 0, + 0, + 0xFFFF +}; +static +KSDATARANGE_MUSIC PinDataRangesStreamDMusic = +{ + { + { + sizeof(KSDATARANGE_MUSIC), + 0, + 0, + 0, + {STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC)}, + {STATICGUIDOF(KSDATAFORMAT_SUBTYPE_DIRECTMUSIC)}, + {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)} + } + }, + {STATICGUIDOF(KSMUSIC_TECHNOLOGY_PORT)}, + 0, + 0, + 0xFFFF +}; + +/***************************************************************************** + * PinDataRangePointersStreamLegacy + * PinDataRangePointersStreamDMusic + * PinDataRangePointersStreamCombined + ***************************************************************************** + * List of pointers to structures indicating range of valid format values + * for live pins. + */ +static +PKSDATARANGE PinDataRangePointersStreamLegacy[] = +{ + PKSDATARANGE(&PinDataRangesStreamLegacy) +}; +static +PKSDATARANGE PinDataRangePointersStreamDMusic[] = +{ + PKSDATARANGE(&PinDataRangesStreamDMusic) +}; +static +PKSDATARANGE PinDataRangePointersStreamCombined[] = +{ + PKSDATARANGE(&PinDataRangesStreamLegacy) + ,PKSDATARANGE(&PinDataRangesStreamDMusic) +}; + +/***************************************************************************** + * PinDataRangesBridge + ***************************************************************************** + * Structures indicating range of valid format values for bridge pins. + */ +static +KSDATARANGE PinDataRangesBridge[] = +{ + { + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + {STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC)}, + {STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI_BUS)}, + {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)} + } + } +}; + +/***************************************************************************** + * PinDataRangePointersBridge + ***************************************************************************** + * List of pointers to structures indicating range of valid format values + * for bridge pins. + */ +static +PKSDATARANGE PinDataRangePointersBridge[] = +{ + &PinDataRangesBridge[0] +}; + +/***************************************************************************** + * SynthProperties + ***************************************************************************** + * List of properties in the Synth set. + */ +static +PCPROPERTY_ITEM +SynthProperties[] = +{ + // Global: S/Get synthesizer caps + { + &KSPROPSETID_Synth, + KSPROPERTY_SYNTH_CAPS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Synth + }, + // Global: S/Get port parameters + { + &KSPROPSETID_Synth, + KSPROPERTY_SYNTH_PORTPARAMETERS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Synth + }, + // Per stream: S/Get channel groups + { + &KSPROPSETID_Synth, + KSPROPERTY_SYNTH_CHANNELGROUPS, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Synth + }, + // Per stream: Get current latency time + { + &KSPROPSETID_Synth, + KSPROPERTY_SYNTH_LATENCYCLOCK, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Synth + } +}; +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth, SynthProperties); +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth2, SynthProperties); + +#define kMaxNumCaptureStreams 1 +#define kMaxNumLegacyRenderStreams 1 +#define kMaxNumDMusicRenderStreams 1 + +/***************************************************************************** + * MiniportPins + ***************************************************************************** + * List of pins. + */ +static +PCPIN_DESCRIPTOR MiniportPins[] = +{ + { + kMaxNumLegacyRenderStreams,kMaxNumLegacyRenderStreams,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersStreamLegacy), // DataRangesCount + PinDataRangePointersStreamLegacy, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_SINK, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + &KSAUDFNAME_MIDI, // Name + {0} // Reserved + } + }, + { + kMaxNumDMusicRenderStreams,kMaxNumDMusicRenderStreams,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersStreamDMusic), // DataRangesCount + PinDataRangePointersStreamDMusic, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_SINK, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + &KSAUDFNAME_DMUSIC_MPU_OUT, // Name + {0} // Reserved + } + }, + { + 0,0,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersBridge), // DataRangesCount + PinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + NULL, // Name + {0} // Reserved + } + }, + { + 0,0,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersBridge), // DataRangesCount + PinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + NULL, // Name + {0} // Reserved + } + }, + { + kMaxNumCaptureStreams,kMaxNumCaptureStreams,0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(PinDataRangePointersStreamCombined), // DataRangesCount + PinDataRangePointersStreamCombined, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_SINK, // Communication + (GUID *) &KSCATEGORY_AUDIO, // Category + &KSAUDFNAME_DMUSIC_MPU_IN, // Name + {0} // Reserved + } + } +}; + +/***************************************************************************** + * MiniportNodes + ***************************************************************************** + * List of nodes. + */ +#define CONST_PCNODE_DESCRIPTOR(n) { 0, NULL, &n, NULL } +#define CONST_PCNODE_DESCRIPTOR_AUTO(n,a) { 0, &a, &n, NULL } +static +PCNODE_DESCRIPTOR MiniportNodes[] = +{ + CONST_PCNODE_DESCRIPTOR_AUTO(KSNODETYPE_SYNTHESIZER, AutomationSynth) + , CONST_PCNODE_DESCRIPTOR_AUTO(KSNODETYPE_SYNTHESIZER, AutomationSynth2) +}; + +/***************************************************************************** + * MiniportConnections + ***************************************************************************** + * List of connections. + */ +enum { + eSynthNode = 0 + , eInputNode +}; + +enum { + eFilterInputPinLeg = 0, + eFilterInputPinDM, + eBridgeOutputPin, + eBridgeInputPin, + eFilterOutputPin +}; + +static +PCCONNECTION_DESCRIPTOR MiniportConnections[] = +{ // From To + // Node pin Node pin + { PCFILTER_NODE, eFilterInputPinLeg, PCFILTER_NODE, eBridgeOutputPin } // Legacy Stream in to synth. + , { PCFILTER_NODE, eFilterInputPinDM, eSynthNode, KSNODEPIN_STANDARD_IN } // DM Stream in to synth. + , { eSynthNode, KSNODEPIN_STANDARD_OUT, PCFILTER_NODE, eBridgeOutputPin } // Synth to bridge out. + , { PCFILTER_NODE, eBridgeInputPin, eInputNode, KSNODEPIN_STANDARD_IN } // Bridge in to input. + , { eInputNode, KSNODEPIN_STANDARD_OUT, PCFILTER_NODE, eFilterOutputPin } // Input to DM/Legacy Stream out. +}; + +/***************************************************************************** + * MiniportCategories + ***************************************************************************** + * List of categories. + */ +static +GUID MiniportCategories[] = +{ + {STATICGUIDOF(KSCATEGORY_AUDIO)}, + {STATICGUIDOF(KSCATEGORY_RENDER)}, + {STATICGUIDOF(KSCATEGORY_CAPTURE)} +}; + +/***************************************************************************** + * MiniportFilterDescriptor + ***************************************************************************** + * Complete miniport filter description. + */ +static +PCFILTER_DESCRIPTOR MiniportFilterDescriptor = +{ + 0, // Version + NULL, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MiniportPins), // PinCount + MiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MiniportNodes), // NodeCount + MiniportNodes, // Nodes + SIZEOF_ARRAY(MiniportConnections), // ConnectionCount + MiniportConnections, // Connections + SIZEOF_ARRAY(MiniportCategories), // CategoryCount + MiniportCategories // Categories +}; + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +BOOLEAN TryMPU(IN PUCHAR PortBase); +NTSTATUS WriteMPU(IN PUCHAR PortBase,IN BOOLEAN IsCommand,IN UCHAR Value); + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +// make sure we're in UART mode +NTSTATUS ResetHardware(PUCHAR portBase) +{ + PAGED_CODE(); + + return WriteMPU(portBase,COMMAND,MPU401_CMD_UART); +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +// +// 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 + { + DPRINT("*** InitMPU returned with ntStatus 0x%08x ***", ntStatus); + } + + m_fMPUInitialized = NT_SUCCESS(ntStatus); + + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * InitMPU() + ***************************************************************************** + * Synchronized routine to initialize the MPU401. + */ NTSTATUS -NTAPI -CMiniportDMus::QueryInterface( - IN REFIID refiid, - OUT PVOID* Output) -{ - if (IsEqualGUIDAligned(refiid, IID_IMiniportDMus)) - { - *Output = PVOID(PUNKNOWN(this)); - PUNKNOWN(*Output)->AddRef(); - return STATUS_SUCCESS; - } - return STATUS_UNSUCCESSFUL; -} - -// IMiniport methods - +InitMPU +( + IN PINTERRUPTSYNC InterruptSync, + IN PVOID DynamicContext +) +{ + DPRINT("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) + { + DPRINT("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 + { + DPRINT("Second attempt to reset the MPU didn't get ACKed.\n"); + DPRINT("Init Reset failure error. Ack = %X", ULONG(dataByte)); + + ntStatus = STATUS_IO_DEVICE_ERROR; + } + + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * CMiniportDMusUARTStream::Write() + ***************************************************************************** + * Writes outgoing MIDI data. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUARTStream:: +Write +( + IN PVOID BufferAddress, + IN ULONG Length, + OUT PULONG BytesWritten +) +{ + DPRINT("Write\n"); + 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; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * SynchronizedDMusMPUWrite() + ***************************************************************************** + * Writes outgoing MIDI data. + */ NTSTATUS -NTAPI -CMiniportDMus::DataRangeIntersection( - IN ULONG PinId, - IN PKSDATARANGE DataRange, - IN PKSDATARANGE MatchingDataRange, - IN ULONG OutputBufferLength, - OUT PVOID ResultantFormat OPTIONAL, - OUT PULONG ResultantFormatLength) -{ - return STATUS_UNSUCCESSFUL; -} - +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 + { + DPRINT("SynchronizedDMusMPUWrite failed (0x%08x)",ntStatus); + break; + } + } + readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport)); + return ntStatus; +} + +#define kMPUPollTimeout 2 + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * TryMPU() + ***************************************************************************** + * See if the MPU401 is free. + */ +BOOLEAN +TryMPU +( + IN PUCHAR PortBase +) +{ + BOOLEAN success; + USHORT numPolls; + UCHAR status; + + DPRINT("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; + DPRINT("TryMPU failed"); + } + else + { + success = TRUE; + } + + return success; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * WriteMPU() + ***************************************************************************** + * Write a byte out to the MPU401. + */ NTSTATUS -NTAPI -CMiniportDMus::GetDescription( - OUT PPCFILTER_DESCRIPTOR *Description) -{ - return STATUS_UNSUCCESSFUL; -} - -HRESULT -NTAPI -CMiniportDMus::Init( - IN PUNKNOWN pUnknownAdapter, - IN PRESOURCELIST pResourceList, - IN PPORTDMUS pPort, - OUT PSERVICEGROUP *ppServiceGroup - ) -{ - return STATUS_UNSUCCESSFUL; -} - -HRESULT -NTAPI -CMiniportDMus::NewStream( - OUT PMXF *ppMXF, - IN PUNKNOWN pOuterUnknown OPTIONAL, - IN POOL_TYPE PoolType, - IN ULONG uPinId, - IN DMUS_STREAM_TYPE StreamType, - IN PKSDATAFORMAT pDataFormat, - OUT PSERVICEGROUP *ppServiceGroup, - IN PAllocatorMXF pAllocatorMXF, - IN PMASTERCLOCK pMasterClock, - OUT PULONGLONG puuSchedulePreFetch - ) -{ - return STATUS_UNSUCCESSFUL; -} - -VOID -NTAPI -CMiniportDMus::Service() -{ - -} +WriteMPU +( + IN PUCHAR PortBase, + IN BOOLEAN IsCommand, + IN UCHAR Value +) +{ + DPRINT("WriteMPU"); + NTSTATUS ntStatus = STATUS_IO_DEVICE_ERROR; + + if (!PortBase) + { + DPRINT("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); + DPRINT("WriteMPU emitted 0x%02x",Value); + ntStatus = STATUS_SUCCESS; + break; + } + } + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * 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); + DPRINT("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) + { + DPRINT("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 + { + DPRINT("SourceEvtsToPort called on render stream"); + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * DMusMPUInterruptServiceRoutine() + ***************************************************************************** + * ISR. + */ +NTSTATUS +DMusMPUInterruptServiceRoutine +( + IN PINTERRUPTSYNC InterruptSync, + IN PVOID DynamicContext +) +{ + DPRINT("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) + ) + { + ULONG buffHead = that->m_MPUInputBufferHead; + if ( (that->m_MPUInputBufferTail + 1 == buffHead) + || (that->m_MPUInputBufferTail + 1 - kMPUInputBufferSize == buffHead)) + { + DPRINT("*****MPU Input Buffer Overflow*****"); + } + else + { + if (!that->m_InputTimeStamp) + { + clockStatus = that->m_MasterClock->GetTime(&that->m_InputTimeStamp); + if (STATUS_SUCCESS != clockStatus) + { + DPRINT("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; +} + +/***************************************************************************** + * CMiniportDMusUART::GetDescription() + ***************************************************************************** + * Gets the topology. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUART:: +GetDescription +( + OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + DPRINT("GetDescription"); + + *OutFilterDescriptor = &MiniportFilterDescriptor; + + return STATUS_SUCCESS; +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif
NTSTATUS NewMiniportDMusUART( OUT PMINIPORT* OutMiniport, IN REFCLSID ClassId) { - CMiniportDMus * This; - - This = new(NonPagedPool, TAG_PORTCLASS)CMiniportDMus(NULL); + CMiniportDMusUART * This; + NTSTATUS Status; + + This= new(NonPagedPool, TAG_PORTCLASS) CMiniportDMusUART(NULL); if (!This) return STATUS_INSUFFICIENT_RESOURCES;
- *OutMiniport = (PMINIPORT)This; - This->AddRef(); + Status = This->QueryInterface(IID_IMiniport, (PVOID*)OutMiniport); + + if (!NT_SUCCESS(Status)) + { + delete This; + } + + DPRINT("NewMiniportDMusUART %p Status %x\n", *OutMiniport, Status); + return Status; +} + + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +/***************************************************************************** + * CMiniportDMusUART::ProcessResources() + ***************************************************************************** + * Processes the resource list, setting up helper objects accordingly. + */ +NTSTATUS +CMiniportDMusUART:: +ProcessResources +( + IN PRESOURCELIST ResourceList +) +{ + PAGED_CODE(); + + DPRINT("ProcessResources"); + + ASSERT(ResourceList); + if (!ResourceList) + { + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + // + // Get counts for the types of resources. + // + ULONG countIO = ResourceList->NumberOfPorts(); + ULONG countIRQ = ResourceList->NumberOfInterrupts(); + ULONG countDMA = ResourceList->NumberOfDmas(); + ULONG lengthIO = ResourceList->FindTranslatedPort(0)->u.Port.Length; + +#ifdef DBG + DPRINT("Starting MPU401 Port 0x%lx", ResourceList->FindTranslatedPort(0)->u.Port.Start.LowPart); +#endif + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // Make sure we have the expected number of resources. + // + if ( (countIO != 1) + || (countIRQ > 1) + || (countDMA != 0) + || (lengthIO == 0) + ) + { + DPRINT("Unknown ResourceList configuraton"); + ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; + } + + if (NT_SUCCESS(ntStatus)) + { + // + // Get the port address. + // + m_pPortBase = + PUCHAR(ResourceList->FindTranslatedPort(0)->u.Port.Start.QuadPart); + + ntStatus = InitializeHardware(m_pInterruptSync,m_pPortBase); + } + + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUART::NonDelegatingQueryInterface() + ***************************************************************************** + * Obtains an interface. This function works just like a COM QueryInterface + * call and is used if the object is not being aggregated. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUART::QueryInterface +( + REFIID Interface, + PVOID * Object +) +{ + PAGED_CODE(); + + DPRINT("Miniport::NonDelegatingQueryInterface"); + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface,IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PMINIPORTDMUS(this))); + } + else + if (IsEqualGUIDAligned(Interface,IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else + if (IsEqualGUIDAligned(Interface,IID_IMiniportDMus)) + { + *Object = PVOID(PMINIPORTDMUS(this)); + } + else + if (IsEqualGUIDAligned(Interface,IID_IMusicTechnology)) + { + *Object = PVOID(PMUSICTECHNOLOGY(this)); + } + else + if (IsEqualGUIDAligned(Interface,IID_IPowerNotify)) + { + *Object = PVOID(PPOWERNOTIFY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // + // We reference the interface for the caller. + // + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUART::~CMiniportDMusUART() + ***************************************************************************** + * Destructor. + */ +CMiniportDMusUART::~CMiniportDMusUART(void) +{ + PAGED_CODE(); + + DPRINT("~CMiniportDMusUART"); + + ASSERT(0 == m_NumCaptureStreams); + ASSERT(0 == m_NumRenderStreams); + + // reset the HW so we don't get anymore interrupts + if (m_UseIRQ && m_pInterruptSync) + { + (void) m_pInterruptSync->CallSynchronizedRoutine((PINTERRUPTSYNCROUTINE)InitMPU,PVOID(m_pPortBase)); + } + else + { + (void) InitMPU(NULL,PVOID(m_pPortBase)); + } + + if (m_pInterruptSync) + { + m_pInterruptSync->Release(); + m_pInterruptSync = NULL; + } + if (m_pServiceGroup) + { + m_pServiceGroup->Release(); + m_pServiceGroup = NULL; + } + if (m_pPort) + { + m_pPort->Release(); + m_pPort = NULL; + } +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUART::Init() + ***************************************************************************** + * Initializes a the miniport. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUART:: +Init +( + IN PUNKNOWN UnknownInterruptSync OPTIONAL, + IN PRESOURCELIST ResourceList, + IN PPORTDMUS Port_, + OUT PSERVICEGROUP * ServiceGroup +) +{ + PAGED_CODE(); + + ASSERT(ResourceList); + if (!ResourceList) + { + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + ASSERT(Port_); + ASSERT(ServiceGroup); + + DPRINT("Init"); + + *ServiceGroup = NULL; + m_pPortBase = 0; + m_fMPUInitialized = FALSE; + + // This will remain unspecified if the miniport does not get any power + // messages. + // + m_PowerState.DeviceState = PowerDeviceUnspecified; + + // + // AddRef() is required because we are keeping this pointer. + // + m_pPort = Port_; + m_pPort->AddRef(); + + // Set dataformat. + // + if (IsEqualGUIDAligned(m_MusicFormatTechnology, GUID_NULL)) + { + RtlCopyMemory( &m_MusicFormatTechnology, + &KSMUSIC_TECHNOLOGY_PORT, + sizeof(GUID)); + } + RtlCopyMemory( &PinDataRangesStreamLegacy.Technology, + &m_MusicFormatTechnology, + sizeof(GUID)); + RtlCopyMemory( &PinDataRangesStreamDMusic.Technology, + &m_MusicFormatTechnology, + sizeof(GUID)); + + for (ULONG bufferCount = 0;bufferCount < kMPUInputBufferSize;bufferCount++) + { + m_MPUInputBuffer[bufferCount] = 0; + } + m_MPUInputBufferHead = 0; + m_MPUInputBufferTail = 0; + m_InputTimeStamp = 0; + m_KSStateInput = KSSTATE_STOP; + + NTSTATUS ntStatus = STATUS_SUCCESS; + + m_NumRenderStreams = 0; + m_NumCaptureStreams = 0; + + m_UseIRQ = TRUE; + if (ResourceList->NumberOfInterrupts() == 0) + { + m_UseIRQ = FALSE; + } + + ntStatus = PcNewServiceGroup(&m_pServiceGroup,NULL); + if (NT_SUCCESS(ntStatus) && !m_pServiceGroup) // keep any error + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS(ntStatus)) + { + *ServiceGroup = m_pServiceGroup; + m_pServiceGroup->AddRef(); + + // + // Register the service group with the port early so the port is + // prepared to handle interrupts. + // + m_pPort->RegisterServiceGroup(m_pServiceGroup); + } + + if (NT_SUCCESS(ntStatus) && m_UseIRQ) + { + // + // Due to a bug in the InterruptSync design, we shouldn't share + // the interrupt sync object. Whoever goes away first + // will disconnect it, and the other points off into nowhere. + // + // Instead we generate our own interrupt sync object. + // + UnknownInterruptSync = NULL; + + if (UnknownInterruptSync) + { + ntStatus = + UnknownInterruptSync->QueryInterface + ( + IID_IInterruptSync, + (PVOID *) &m_pInterruptSync + ); + + if (!m_pInterruptSync && NT_SUCCESS(ntStatus)) // keep any error + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + if (NT_SUCCESS(ntStatus)) + { // run this ISR first + ntStatus = m_pInterruptSync-> + RegisterServiceRoutine(DMusMPUInterruptServiceRoutine,PVOID(this),TRUE); + } + + } + else + { // create our own interruptsync mechanism. + ntStatus = + PcNewInterruptSync + ( + &m_pInterruptSync, + NULL, + ResourceList, + 0, // Resource Index + InterruptSyncModeNormal // Run ISRs once until we get SUCCESS + ); + + if (!m_pInterruptSync && NT_SUCCESS(ntStatus)) // keep any error + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = m_pInterruptSync->RegisterServiceRoutine( + DMusMPUInterruptServiceRoutine, + PVOID(this), + TRUE); // run this ISR first + } + if (NT_SUCCESS(ntStatus)) + { + ntStatus = m_pInterruptSync->Connect(); + } + } + } + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = ProcessResources(ResourceList); + } + + if (!NT_SUCCESS(ntStatus)) + { + // + // clean up our mess + // + + // clean up the interrupt sync + if( m_pInterruptSync ) + { + m_pInterruptSync->Release(); + m_pInterruptSync = NULL; + } + + // clean up the service group + if( m_pServiceGroup ) + { + m_pServiceGroup->Release(); + m_pServiceGroup = NULL; + } + + // clean up the out param service group. + if (*ServiceGroup) + { + (*ServiceGroup)->Release(); + (*ServiceGroup) = NULL; + } + + // release the port + m_pPort->Release(); + m_pPort = NULL; + } + + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUART::NewStream() + ***************************************************************************** + * Gets the topology. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUART:: +NewStream +( + OUT PMXF * MXF, + IN PUNKNOWN OuterUnknown OPTIONAL, + IN POOL_TYPE PoolType, + IN ULONG PinID, + IN DMUS_STREAM_TYPE StreamType, + IN PKSDATAFORMAT DataFormat, + OUT PSERVICEGROUP * ServiceGroup, + IN PAllocatorMXF AllocatorMXF, + IN PMASTERCLOCK MasterClock, + OUT PULONGLONG SchedulePreFetch +) +{ + PAGED_CODE(); + + DPRINT("NewStream"); + NTSTATUS ntStatus = STATUS_SUCCESS; + + // In 100 ns, we want stuff as soon as it comes in + // + *SchedulePreFetch = 0; + + // if we don't have any streams already open, get the hardware ready. + if ((!m_NumCaptureStreams) && (!m_NumRenderStreams)) + { + ntStatus = ResetHardware(m_pPortBase); + if (!NT_SUCCESS(ntStatus)) + { + DPRINT("CMiniportDMusUART::NewStream ResetHardware failed"); + return ntStatus; + } + } + + if ( ((m_NumCaptureStreams < kMaxNumCaptureStreams) + && (StreamType == DMUS_STREAM_MIDI_CAPTURE)) + || ((m_NumRenderStreams < kMaxNumLegacyRenderStreams + kMaxNumDMusicRenderStreams) + && (StreamType == DMUS_STREAM_MIDI_RENDER)) + ) + { + CMiniportDMusUARTStream *pStream = + new(PoolType) CMiniportDMusUARTStream(); + + if (pStream) + { + pStream->AddRef(); + + ntStatus = + pStream->Init(this,m_pPortBase,(StreamType == DMUS_STREAM_MIDI_CAPTURE),AllocatorMXF,MasterClock); + + if (NT_SUCCESS(ntStatus)) + { + *MXF = PMXF(pStream); + (*MXF)->AddRef(); + + if (StreamType == DMUS_STREAM_MIDI_CAPTURE) + { + m_NumCaptureStreams++; + *ServiceGroup = m_pServiceGroup; + (*ServiceGroup)->AddRef(); + } + else + { + m_NumRenderStreams++; + *ServiceGroup = NULL; + } + } + + pStream->Release(); + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + if (StreamType == DMUS_STREAM_MIDI_CAPTURE) + { + DPRINT("NewStream failed, too many capture streams"); + } + else if (StreamType == DMUS_STREAM_MIDI_RENDER) + { + DPRINT("NewStream failed, too many render streams"); + } + else + { + DPRINT("NewStream invalid stream type"); + } + } + + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUART::SetTechnology() + ***************************************************************************** + * Sets pindatarange technology. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUART:: +SetTechnology +( + IN const GUID * Technology +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + + // Fail if miniport has already been initialized. + // + if (NULL == m_pPort) + { + RtlCopyMemory(&m_MusicFormatTechnology, Technology, sizeof(GUID)); + ntStatus = STATUS_SUCCESS; + } + + return ntStatus; +} // SetTechnology + +/***************************************************************************** + * CMiniportDMusUART::PowerChangeNotify() + ***************************************************************************** + * Handle power state change for the miniport. + */ +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +STDMETHODIMP_(void) +CMiniportDMusUART:: +PowerChangeNotify +( + IN POWER_STATE PowerState +) +{ + PAGED_CODE(); + + DPRINT("CMiniportDMusUART::PoweChangeNotify D%d", PowerState.DeviceState); + + switch (PowerState.DeviceState) + { + case PowerDeviceD0: + if (m_PowerState.DeviceState != PowerDeviceD0) + { + if (!NT_SUCCESS(InitializeHardware(m_pInterruptSync,m_pPortBase))) + { + DPRINT("InitializeHardware failed when resuming"); + } + } + break; + + case PowerDeviceD1: + case PowerDeviceD2: + case PowerDeviceD3: + default: + break; + } + m_PowerState.DeviceState = PowerState.DeviceState; +} // PowerChangeNotify + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUARTStream::NonDelegatingQueryInterface() + ***************************************************************************** + * Obtains an interface. This function works just like a COM QueryInterface + * call and is used if the object is not being aggregated. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUARTStream::QueryInterface +( + REFIID Interface, + PVOID * Object +) +{ + PAGED_CODE(); + + DPRINT("Stream::NonDelegatingQueryInterface"); + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface,IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else + if (IsEqualGUIDAligned(Interface,IID_IMXF)) + { + *Object = PVOID(PMXF(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // + // We reference the interface for the caller. + // + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUARTStream::~CMiniportDMusUARTStream() + ***************************************************************************** + * Destructs a stream. + */ +CMiniportDMusUARTStream::~CMiniportDMusUARTStream(void) +{ + PAGED_CODE(); + + DPRINT("~CMiniportDMusUARTStream"); + + KeCancelTimer(&m_TimerEvent); + + if (m_DMKEvtQueue) + { + if (m_AllocatorMXF) + { + m_AllocatorMXF->PutMessage(m_DMKEvtQueue); + } + else + { + DPRINT("~CMiniportDMusUARTStream, no allocator, can't flush DMKEvts"); + } + m_DMKEvtQueue = NULL; + } + if (m_AllocatorMXF) + { + m_AllocatorMXF->Release(); + m_AllocatorMXF = NULL; + } + + if (m_pMiniport) + { + if (m_fCapture) + { + m_pMiniport->m_NumCaptureStreams--; + } + else + { + m_pMiniport->m_NumRenderStreams--; + } + + m_pMiniport->Release(); + } +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUARTStream::Init() + ***************************************************************************** + * Initializes a stream. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUARTStream:: +Init +( + IN CMiniportDMusUART * pMiniport, + IN PUCHAR pPortBase, + IN BOOLEAN fCapture, + IN PAllocatorMXF allocatorMXF, + IN PMASTERCLOCK masterClock +) +{ + PAGED_CODE(); + + ASSERT(pMiniport); + ASSERT(pPortBase); + + DPRINT("Init"); + + m_NumFailedMPUTries = 0; + m_TimerQueued = FALSE; + KeInitializeSpinLock(&m_DpcSpinLock); + m_pMiniport = pMiniport; + m_pMiniport->AddRef(); + + pMiniport->m_MasterClock = masterClock; + + m_pPortBase = pPortBase; + m_fCapture = fCapture; + + m_SnapshotTimeStamp = 0; + m_DMKEvtQueue = NULL; + m_DMKEvtOffset = 0; + + m_NumberOfRetries = 0; + + if (allocatorMXF) + { + allocatorMXF->AddRef(); + m_AllocatorMXF = allocatorMXF; + m_sinkMXF = m_AllocatorMXF; + } + else + { + return STATUS_INVALID_PARAMETER; + } + + KeInitializeDpc + ( + &m_Dpc, + &::DMusUARTTimerDPC, + PVOID(this) + ); + KeInitializeTimer(&m_TimerEvent);
return STATUS_SUCCESS; }
- +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif +/***************************************************************************** + * CMiniportDMusUARTStream::SetState() + ***************************************************************************** + * Sets the state of the channel. + */ +STDMETHODIMP_(NTSTATUS) +CMiniportDMusUARTStream:: +SetState +( + IN KSSTATE NewState +) +{ + PAGED_CODE(); + + DPRINT("SetState %d",NewState); + + if (NewState == KSSTATE_RUN) + { + if (m_pMiniport->m_fMPUInitialized) + { + LARGE_INTEGER timeDue100ns; + timeDue100ns.QuadPart = 0; + KeSetTimer(&m_TimerEvent,timeDue100ns,&m_Dpc); + } + else + { + DPRINT("CMiniportDMusUARTStream::SetState KSSTATE_RUN failed due to uninitialized MPU"); + return STATUS_INVALID_DEVICE_STATE; + } + } + + if (m_fCapture) + { + m_pMiniport->m_KSStateInput = NewState; + if (NewState == KSSTATE_STOP) // STOPping + { + m_pMiniport->m_MPUInputBufferHead = 0; // Previously read bytes are discarded. + m_pMiniport->m_MPUInputBufferTail = 0; // The entire FIFO is available. + } + } + return STATUS_SUCCESS; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + + +/***************************************************************************** + * CMiniportDMusUART::Service() + ***************************************************************************** + * DPC-mode service call from the port. + */ +STDMETHODIMP_(void) +CMiniportDMusUART:: +Service +( void +) +{ + DPRINT("Service"); + if (!m_NumCaptureStreams) + { + // we should never get here.... + // if we do, we must have read some trash, + // so just reset the input FIFO + m_MPUInputBufferTail = m_MPUInputBufferHead = 0; + } +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +/***************************************************************************** + * CMiniportDMusUARTStream::ConnectOutput() + ***************************************************************************** + * Writes outgoing MIDI data. + */ +NTSTATUS +CMiniportDMusUARTStream:: +ConnectOutput(PMXF sinkMXF) +{ + PAGED_CODE(); + + if (m_fCapture) + { + if ((sinkMXF) && (m_sinkMXF == m_AllocatorMXF)) + { + DPRINT("ConnectOutput"); + m_sinkMXF = sinkMXF; + return STATUS_SUCCESS; + } + else + { + DPRINT("ConnectOutput failed"); + } + } + else + { + DPRINT("ConnectOutput called on renderer; failed"); + } + return STATUS_UNSUCCESSFUL; +} + +#ifdef _MSC_VER +#pragma code_seg("PAGE") +#endif + +/***************************************************************************** + * CMiniportDMusUARTStream::DisconnectOutput() + ***************************************************************************** + * Writes outgoing MIDI data. + */ +NTSTATUS +CMiniportDMusUARTStream:: +DisconnectOutput(PMXF sinkMXF) +{ + PAGED_CODE(); + + if (m_fCapture) + { + if ((m_sinkMXF == sinkMXF) || (!sinkMXF)) + { + DPRINT("DisconnectOutput"); + m_sinkMXF = m_AllocatorMXF; + return STATUS_SUCCESS; + } + else + { + DPRINT("DisconnectOutput failed"); + } + } + else + { + DPRINT("DisconnectOutput called on renderer; failed"); + } + return STATUS_UNSUCCESSFUL; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + + +/***************************************************************************** + * CMiniportDMusUARTStream::PutMessageLocked() + ***************************************************************************** + * Now that the spinlock is held, add this message to the queue. + * + * Writes an outgoing MIDI message. + * We don't sort a new message into the queue -- we append it. + * This is fine, since the sequencer feeds us sequenced data. + * Timestamps will ascend by design. + */ +NTSTATUS CMiniportDMusUARTStream::PutMessageLocked(PDMUS_KERNEL_EVENT pDMKEvt) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PDMUS_KERNEL_EVENT aDMKEvt; + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + if (!m_fCapture) + { + DPRINT("PutMessage to render stream"); + if (pDMKEvt) + { + // m_DpcSpinLock already held + + if (m_DMKEvtQueue) + { + aDMKEvt = m_DMKEvtQueue; // put pDMKEvt in event queue + + while (aDMKEvt->pNextEvt) + { + aDMKEvt = aDMKEvt->pNextEvt; + } + aDMKEvt->pNextEvt = pDMKEvt; // here is end of queue + } + else // currently nothing in queue + { + m_DMKEvtQueue = pDMKEvt; + if (m_DMKEvtOffset) + { + DPRINT("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d",m_DMKEvtOffset); + m_DMKEvtOffset = 0; + } + } + + // m_DpcSpinLock already held + } + if (!m_TimerQueued) + { + (void) ConsumeEvents(); + } + } + else // capture + { + DPRINT("PutMessage to capture stream"); + ASSERT(NULL == pDMKEvt); + + SourceEvtsToPort(); + } + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * CMiniportDMusUARTStream::PutMessage() + ***************************************************************************** + * Writes an outgoing MIDI message. + * We don't sort a new message into the queue -- we append it. + * This is fine, since the sequencer feeds us sequenced data. + * Timestamps will ascend by design. + */ +NTSTATUS CMiniportDMusUARTStream::PutMessage(PDMUS_KERNEL_EVENT pDMKEvt) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PDMUS_KERNEL_EVENT aDMKEvt; + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + if (!m_fCapture) + { + DPRINT("PutMessage to render stream"); + if (pDMKEvt) + { + KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock); + + if (m_DMKEvtQueue) + { + aDMKEvt = m_DMKEvtQueue; // put pDMKEvt in event queue + + while (aDMKEvt->pNextEvt) + { + aDMKEvt = aDMKEvt->pNextEvt; + } + aDMKEvt->pNextEvt = pDMKEvt; // here is end of queue + } + else // currently nothing in queue + { + m_DMKEvtQueue = pDMKEvt; + if (m_DMKEvtOffset) + { + DPRINT("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d", m_DMKEvtOffset); + m_DMKEvtOffset = 0; + } + } + + KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock); + } + if (!m_TimerQueued) + { + (void) ConsumeEvents(); + } + } + else // capture + { + DPRINT("PutMessage to capture stream"); + ASSERT(NULL == pDMKEvt); + + SourceEvtsToPort(); + } + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * CMiniportDMusUARTStream::ConsumeEvents() + ***************************************************************************** + * Attempts to empty the render message queue. + * Called either from DPC timer or upon IRP submittal. +// TODO: support packages right +// process the package (actually, should do this above. +// treat the package as a list fragment that shouldn't be sorted. +// better yet, go through each event in the package, and when +// an event is exhausted, delete it and decrement m_offset. + */ +NTSTATUS CMiniportDMusUARTStream::ConsumeEvents(void) +{ + PDMUS_KERNEL_EVENT aDMKEvt; + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG bytesRemaining = 0,bytesWritten = 0; + LARGE_INTEGER aMillisecIn100ns; + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock); + + m_TimerQueued = FALSE; + while (m_DMKEvtQueue) // do we have anything to play at all? + { + aDMKEvt = m_DMKEvtQueue; // event we try to play + if (aDMKEvt->cbEvent) + { + bytesRemaining = aDMKEvt->cbEvent - m_DMKEvtOffset; // number of bytes left in this evt + + ASSERT(bytesRemaining > 0); + if (bytesRemaining <= 0) + { + bytesRemaining = aDMKEvt->cbEvent; + } + + if (aDMKEvt->cbEvent <= sizeof(PBYTE)) // short message + { + DPRINT("ConsumeEvents(%02x%02x%02x%02x)", aDMKEvt->uData.abData[0], aDMKEvt->uData.abData[1], aDMKEvt->uData.abData[2], aDMKEvt->uData.abData[3]); + ntStatus = Write(aDMKEvt->uData.abData + m_DMKEvtOffset,bytesRemaining,&bytesWritten); + } + else if (PACKAGE_EVT(aDMKEvt)) + { + ASSERT(m_DMKEvtOffset == 0); + m_DMKEvtOffset = 0; + DPRINT("ConsumeEvents(Package)"); + + ntStatus = PutMessageLocked(aDMKEvt->uData.pPackageEvt); // we already own the spinlock + + // null this because we are about to throw it in the allocator + aDMKEvt->uData.pPackageEvt = NULL; + aDMKEvt->cbEvent = 0; + bytesWritten = bytesRemaining; + } + else // SysEx message + { + DPRINT("ConsumeEvents(%02x%02x%02x%02x)", aDMKEvt->uData.pbData[0], aDMKEvt->uData.pbData[1], aDMKEvt->uData.pbData[2], aDMKEvt->uData.pbData[3]); + + ntStatus = Write(aDMKEvt->uData.pbData + m_DMKEvtOffset,bytesRemaining,&bytesWritten); + } + } // if (aDMKEvt->cbEvent) + if (STATUS_SUCCESS != ntStatus) + { + DPRINT("ConsumeEvents: Write returned 0x%08x", ntStatus); + bytesWritten = bytesRemaining; // just bail on this event and try next time + } + + ASSERT(bytesWritten <= bytesRemaining); + if (bytesWritten == bytesRemaining) + { + m_DMKEvtQueue = m_DMKEvtQueue->pNextEvt; + aDMKEvt->pNextEvt = NULL; + + m_AllocatorMXF->PutMessage(aDMKEvt); // throw back in free pool + m_DMKEvtOffset = 0; // start fresh on next evt + m_NumberOfRetries = 0; + } // but wait ... there's more! + else // our FIFO is full for now. + { + // update our offset by that amount we did write + m_DMKEvtOffset += bytesWritten; + ASSERT(m_DMKEvtOffset < aDMKEvt->cbEvent); + + DPRINT("ConsumeEvents tried %d, wrote %d, at offset %d", bytesRemaining,bytesWritten,m_DMKEvtOffset); + aMillisecIn100ns.QuadPart = -(kOneMillisec); // set timer, come back later + m_TimerQueued = TRUE; + m_NumberOfRetries++; + ntStatus = KeSetTimer( &m_TimerEvent, aMillisecIn100ns, &m_Dpc ); + break; + } // we didn't write it all + } // go back, Jack, do it again (while m_DMKEvtQueue) + KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock); + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * CMiniportDMusUARTStream::HandlePortParams() + ***************************************************************************** + * Writes an outgoing MIDI message. + */ +NTSTATUS +CMiniportDMusUARTStream:: +HandlePortParams +( + IN PPCPROPERTY_REQUEST pRequest +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + + if (pRequest->Verb & KSPROPERTY_TYPE_SET) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTH_PORTPARAMS), TRUE); + if (NT_SUCCESS(ntStatus)) + { + RtlCopyMemory(pRequest->Value, pRequest->Instance, sizeof(SYNTH_PORTPARAMS)); + + PSYNTH_PORTPARAMS Params = (PSYNTH_PORTPARAMS)pRequest->Value; + + if (Params->ValidParams & ~SYNTH_PORTPARAMS_CHANNELGROUPS) + { + Params->ValidParams &= SYNTH_PORTPARAMS_CHANNELGROUPS; + } + + if (!(Params->ValidParams & SYNTH_PORTPARAMS_CHANNELGROUPS)) + { + Params->ChannelGroups = 1; + } + else if (Params->ChannelGroups != 1) + { + Params->ChannelGroups = 1; + } + + pRequest->ValueSize = sizeof(SYNTH_PORTPARAMS); + } + + return ntStatus; +} + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/***************************************************************************** + * DMusTimerDPC() + ***************************************************************************** + * The timer DPC callback. Thunks to a C++ member function. + * This is called by the OS in response to the DirectMusic pin + * wanting to wakeup later to process more DirectMusic stuff. + */ +VOID +NTAPI +DMusUARTTimerDPC +( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 +) +{ + ASSERT(DeferredContext); + + CMiniportDMusUARTStream *aStream; + aStream = (CMiniportDMusUARTStream *) DeferredContext; + if (aStream) + { + DPRINT("DMusUARTTimerDPC"); + if (false == aStream->m_fCapture) + { + (void) aStream->ConsumeEvents(); + } + // ignores return value! + } +} + +/***************************************************************************** + * DirectMusic properties + ****************************************************************************/ + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +/* + * Properties concerning synthesizer functions. + */ +const WCHAR wszDescOut[] = L"DMusic MPU-401 Out "; +const WCHAR wszDescIn[] = L"DMusic MPU-401 In "; + +NTSTATUS PropertyHandler_Synth +( + IN PPCPROPERTY_REQUEST pRequest +) +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE); + if (NT_SUCCESS(ntStatus)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(pRequest->Value); + + *AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT; + switch (pRequest->PropertyItem->Id) + { + case KSPROPERTY_SYNTH_CAPS: + case KSPROPERTY_SYNTH_CHANNELGROUPS: + *AccessFlags |= KSPROPERTY_TYPE_GET; + } + switch (pRequest->PropertyItem->Id) + { + case KSPROPERTY_SYNTH_CHANNELGROUPS: + *AccessFlags |= KSPROPERTY_TYPE_SET; + } + ntStatus = STATUS_SUCCESS; + pRequest->ValueSize = sizeof(ULONG); + + switch (pRequest->PropertyItem->Id) + { + case KSPROPERTY_SYNTH_PORTPARAMETERS: + if (pRequest->MinorTarget) + { + *AccessFlags |= KSPROPERTY_TYPE_GET; + } + else + { + pRequest->ValueSize = 0; + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + } + } + } + else + { + ntStatus = STATUS_SUCCESS; + switch(pRequest->PropertyItem->Id) + { + case KSPROPERTY_SYNTH_CAPS: + DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_CAPS"); + + if (pRequest->Verb & KSPROPERTY_TYPE_SET) + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTHCAPS), TRUE); + + if (NT_SUCCESS(ntStatus)) + { + SYNTHCAPS *caps = (SYNTHCAPS*)pRequest->Value; + int increment; + RtlZeroMemory(caps, sizeof(SYNTHCAPS)); + // XXX Different guids for different instances! + // + if (pRequest->Node == eSynthNode) + { + increment = sizeof(wszDescOut) - 2; + RtlCopyMemory( caps->Description,wszDescOut,increment); + caps->Guid = CLSID_MiniportDriverDMusUART; + } + else + { + increment = sizeof(wszDescIn) - 2; + RtlCopyMemory( caps->Description,wszDescIn,increment); + caps->Guid = CLSID_MiniportDriverDMusUARTCapture; + } + + caps->Flags = SYNTH_PC_EXTERNAL; + caps->MemorySize = 0; + caps->MaxChannelGroups = 1; + caps->MaxVoices = 0xFFFFFFFF; + caps->MaxAudioChannels = 0xFFFFFFFF; + + caps->EffectFlags = 0; + + CMiniportDMusUART *aMiniport; + ASSERT(pRequest->MajorTarget); + aMiniport = (CMiniportDMusUART *)(PMINIPORTDMUS)(pRequest->MajorTarget); + WCHAR wszDesc2[16]; + int cLen; + cLen = swprintf(wszDesc2,L"[%03x]\0",PtrToUlong(aMiniport->m_pPortBase)); + + cLen *= sizeof(WCHAR); + RtlCopyMemory((WCHAR *)((DWORD_PTR)(caps->Description) + increment), + wszDesc2, + cLen); + + + pRequest->ValueSize = sizeof(SYNTHCAPS); + } + } + + break; + + case KSPROPERTY_SYNTH_PORTPARAMETERS: + DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_PORTPARAMETERS"); + { + CMiniportDMusUARTStream *aStream; + + aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget); + if (aStream) + { + ntStatus = aStream->HandlePortParams(pRequest); + } + else + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + } + break; + + case KSPROPERTY_SYNTH_CHANNELGROUPS: + DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_CHANNELGROUPS"); + + ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE); + if (NT_SUCCESS(ntStatus)) + { + *(PULONG)(pRequest->Value) = 1; + pRequest->ValueSize = sizeof(ULONG); + } + break; + + case KSPROPERTY_SYNTH_LATENCYCLOCK: + DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_LATENCYCLOCK"); + + if(pRequest->Verb & KSPROPERTY_TYPE_SET) + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + else + { + ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONGLONG), TRUE); + if(NT_SUCCESS(ntStatus)) + { + REFERENCE_TIME rtLatency; + CMiniportDMusUARTStream *aStream; + + aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget); + if(aStream == NULL) + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + else + { + aStream->m_pMiniport->m_MasterClock->GetTime(&rtLatency); + *((PULONGLONG)pRequest->Value) = rtLatency; + pRequest->ValueSize = sizeof(ULONGLONG); + } + } + } + break; + + default: + DPRINT("Unhandled property in PropertyHandler_Synth"); + break; + } + } + return ntStatus; +} + +/***************************************************************************** + * ValidatePropertyRequest() + ***************************************************************************** + * Validates pRequest. + * Checks if the ValueSize is valid + * Checks if the Value is valid + * + * This does not update pRequest->ValueSize if it returns NT_SUCCESS. + * Caller must set pRequest->ValueSize in case of NT_SUCCESS. + */ +NTSTATUS ValidatePropertyRequest +( + IN PPCPROPERTY_REQUEST pRequest, + IN ULONG ulValueSize, + IN BOOLEAN fValueRequired +) +{ + NTSTATUS ntStatus; + + if (pRequest->ValueSize >= ulValueSize) + { + if (fValueRequired && NULL == pRequest->Value) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else + { + ntStatus = STATUS_SUCCESS; + } + } + else if (0 == pRequest->ValueSize) + { + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + if (STATUS_BUFFER_OVERFLOW == ntStatus) + { + pRequest->ValueSize = ulValueSize; + } + else + { + pRequest->ValueSize = 0; + } + + return ntStatus; +} // ValidatePropertyRequest + +#ifdef _MSC_VER +#pragma code_seg() +#endif + +
Modified: trunk/reactos/drivers/wdm/audio/backpln/portcls/private.hpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/wdm/audio/backpln/p... ============================================================================== --- trunk/reactos/drivers/wdm/audio/backpln/portcls/private.hpp [iso-8859-1] (original) +++ trunk/reactos/drivers/wdm/audio/backpln/portcls/private.hpp [iso-8859-1] Mon Nov 1 00:26:59 2010 @@ -22,6 +22,8 @@ #include "interfaces.hpp" #include <ks.h> #include <ksmedia.h> +#include <stdio.h> + //#include <intrin.h>
#define TAG_PORTCLASS 'SLCP'