|
|
/*****************************************************************************
* miniport.cpp - UART miniport implementation ***************************************************************************** * Copyright (c) 1997-2000 Microsoft Corporation. All Rights Reserved. * * Feb 98 MartinP -- based on UART, began deltas for DirectMusic. */
#include "private.h"
#include "ksdebug.h"
#include "stdio.h"
#define STR_MODULENAME "DMusUART:Miniport: "
#pragma code_seg("PAGE")
/*****************************************************************************
* 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
};
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::GetDescription() ***************************************************************************** * Gets the topology. */ STDMETHODIMP_(NTSTATUS) CMiniportDMusUART:: GetDescription ( OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor ) { PAGED_CODE();
ASSERT(OutFilterDescriptor);
_DbgPrintF(DEBUGLVL_BLAB,("GetDescription"));
*OutFilterDescriptor = &MiniportFilterDescriptor;
return STATUS_SUCCESS; }
#pragma code_seg("PAGE")
/*****************************************************************************
* CreateMiniportDMusUART() ***************************************************************************** * Creates a MPU-401 miniport driver for the adapter. This uses a * macro from STDUNK.H to do all the work. */ NTSTATUS CreateMiniportDMusUART ( OUT PUNKNOWN * Unknown, IN REFCLSID, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("CreateMiniportDMusUART")); ASSERT(Unknown);
STD_CREATE_BODY_( CMiniportDMusUART, Unknown, UnknownOuter, PoolType, PMINIPORTDMUS); }
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::ProcessResources() ***************************************************************************** * Processes the resource list, setting up helper objects accordingly. */ NTSTATUS CMiniportDMusUART:: ProcessResources ( IN PRESOURCELIST ResourceList ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_BLAB,("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;
#if (DBG)
_DbgPrintF(DEBUGLVL_VERBOSE,("Starting MPU401 Port 0x%X", 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) ) { _DbgPrintF(DEBUGLVL_TERSE,("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; }
#pragma code_seg("PAGE")
/*****************************************************************************
* 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:: NonDelegatingQueryInterface ( REFIID Interface, PVOID * Object ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("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; }
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::~CMiniportDMusUART() ***************************************************************************** * Destructor. */ CMiniportDMusUART::~CMiniportDMusUART(void) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB,("~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(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; } }
#pragma code_seg("PAGE")
/*****************************************************************************
* 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);
_DbgPrintF(DEBUGLVL_BLAB,("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; }
#pragma code_seg("PAGE")
/*****************************************************************************
* 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();
_DbgPrintF(DEBUGLVL_BLAB, ("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)) { _DbgPrintF(DEBUGLVL_TERSE, ("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(OuterUnknown);
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) { _DbgPrintF(DEBUGLVL_TERSE,("NewStream failed, too many capture streams")); } else if (StreamType == DMUS_STREAM_MIDI_RENDER) { _DbgPrintF(DEBUGLVL_TERSE,("NewStream failed, too many render streams")); } else { _DbgPrintF(DEBUGLVL_TERSE,("NewStream invalid stream type")); } }
return ntStatus; }
#pragma code_seg("PAGE")
/*****************************************************************************
* 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. */ #pragma code_seg("PAGE")
STDMETHODIMP_(void) CMiniportDMusUART:: PowerChangeNotify ( IN POWER_STATE PowerState ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDMusUART::PoweChangeNotify D%d", PowerState.DeviceState));
switch (PowerState.DeviceState) { case PowerDeviceD0: if (m_PowerState.DeviceState != PowerDeviceD0) { if (!NT_SUCCESS(InitializeHardware(m_pInterruptSync,m_pPortBase))) { _DbgPrintF(DEBUGLVL_TERSE, ("InitializeHardware failed when resuming")); } } break;
case PowerDeviceD1: case PowerDeviceD2: case PowerDeviceD3: default: break; } m_PowerState.DeviceState = PowerState.DeviceState; } // PowerChangeNotify
#pragma code_seg("PAGE")
/*****************************************************************************
* 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:: NonDelegatingQueryInterface ( REFIID Interface, PVOID * Object ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB,("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; }
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::~CMiniportDMusUARTStream() ***************************************************************************** * Destructs a stream. */ CMiniportDMusUARTStream::~CMiniportDMusUARTStream(void) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB,("~CMiniportDMusUARTStream"));
KeCancelTimer(&m_TimerEvent);
if (m_DMKEvtQueue) { if (m_AllocatorMXF) { m_AllocatorMXF->PutMessage(m_DMKEvtQueue); } else { _DbgPrintF(DEBUGLVL_ERROR,("~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(); } }
#pragma code_seg("PAGE")
/*****************************************************************************
* 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);
_DbgPrintF(DEBUGLVL_BLAB,("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; }
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::SetState() ***************************************************************************** * Sets the state of the channel. */ STDMETHODIMP_(NTSTATUS) CMiniportDMusUARTStream:: SetState ( IN KSSTATE NewState ) { PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE,("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 { _DbgPrintF(DEBUGLVL_TERSE, ("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; }
#pragma code_seg()
/*****************************************************************************
* CMiniportDMusUART::Service() ***************************************************************************** * DPC-mode service call from the port. */ STDMETHODIMP_(void) CMiniportDMusUART:: Service ( void ) { _DbgPrintF(DEBUGLVL_BLAB, ("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; } }
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::ConnectOutput() ***************************************************************************** * Writes outgoing MIDI data. */ NTSTATUS CMiniportDMusUARTStream:: ConnectOutput(PMXF sinkMXF) { PAGED_CODE();
if (m_fCapture) { if ((sinkMXF) && (m_sinkMXF == m_AllocatorMXF)) { _DbgPrintF(DEBUGLVL_BLAB, ("ConnectOutput")); m_sinkMXF = sinkMXF; return STATUS_SUCCESS; } else { _DbgPrintF(DEBUGLVL_TERSE, ("ConnectOutput failed")); } } else { _DbgPrintF(DEBUGLVL_TERSE, ("ConnectOutput called on renderer; failed")); } return STATUS_UNSUCCESSFUL; }
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::DisconnectOutput() ***************************************************************************** * Writes outgoing MIDI data. */ NTSTATUS CMiniportDMusUARTStream:: DisconnectOutput(PMXF sinkMXF) { PAGED_CODE();
if (m_fCapture) { if ((m_sinkMXF == sinkMXF) || (!sinkMXF)) { _DbgPrintF(DEBUGLVL_BLAB, ("DisconnectOutput")); m_sinkMXF = m_AllocatorMXF; return STATUS_SUCCESS; } else { _DbgPrintF(DEBUGLVL_TERSE, ("DisconnectOutput failed")); } } else { _DbgPrintF(DEBUGLVL_TERSE, ("DisconnectOutput called on renderer; failed")); } return STATUS_UNSUCCESSFUL; }
#pragma code_seg()
/*****************************************************************************
* 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) { _DbgPrintF(DEBUGLVL_BLAB, ("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) { _DbgPrintF(DEBUGLVL_ERROR, ("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
{ _DbgPrintF(DEBUGLVL_BLAB, ("PutMessage to capture stream")); ASSERT(NULL == pDMKEvt);
SourceEvtsToPort(); } return ntStatus; }
#pragma code_seg()
/*****************************************************************************
* 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) { _DbgPrintF(DEBUGLVL_BLAB, ("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) { _DbgPrintF(DEBUGLVL_ERROR, ("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d",m_DMKEvtOffset)); m_DMKEvtOffset = 0; } }
KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock); } if (!m_TimerQueued) { (void) ConsumeEvents(); } } else // capture
{ _DbgPrintF(DEBUGLVL_BLAB, ("PutMessage to capture stream")); ASSERT(NULL == pDMKEvt);
SourceEvtsToPort(); } return ntStatus; }
#pragma code_seg()
/*****************************************************************************
* 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
{ _DbgPrintF(DEBUGLVL_BLAB, ("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; _DbgPrintF(DEBUGLVL_BLAB, ("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
{ _DbgPrintF(DEBUGLVL_BLAB, ("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) { _DbgPrintF(DEBUGLVL_TERSE, ("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);
_DbgPrintF(DEBUGLVL_BLAB,("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; }
#pragma code_seg()
/*****************************************************************************
* 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; }
#pragma code_seg()
/*****************************************************************************
* 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) { _DbgPrintF(DEBUGLVL_BLAB,("DMusUARTTimerDPC")); if (false == aStream->m_fCapture) { (void) aStream->ConsumeEvents(); } // ignores return value!
} }
/*****************************************************************************
* DirectMusic properties ****************************************************************************/
#pragma code_seg("PAGE")
/*
* 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: _DbgPrintF(DEBUGLVL_VERBOSE,("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: _DbgPrintF(DEBUGLVL_VERBOSE,("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: _DbgPrintF(DEBUGLVL_VERBOSE,("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: _DbgPrintF(DEBUGLVL_VERBOSE,("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: _DbgPrintF(DEBUGLVL_TERSE,("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
#pragma code_seg()
|