windows-nt/Source/XPSP1/NT/drivers/wdm/dvd/class/lowerapi.c
2020-09-26 16:20:57 +08:00

2351 lines
64 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
decinit.c
Abstract:
This is the WDM decoder class driver. This module contains code related
to request processing.
Author:
billpa
Environment:
Kernel mode only
Revision History:
--*/
#include "codcls.h"
#if DBG
#if WIN95_BUILD
ULONG StreamDebug = DebugLevelInfo;
#else
ULONG StreamDebug = DebugLevelError;
#endif
#define STREAM_BUFFER_SIZE 256
UCHAR StreamBuffer[STREAM_BUFFER_SIZE];
#endif
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'PscS')
#endif
VOID
StreamClassStreamNotification(
IN STREAM_MINIDRIVER_STREAM_NOTIFICATION_TYPE NotificationType,
IN PHW_STREAM_OBJECT HwStreamObject,
...
)
/*++
Routine Description:
stream notification routine for minidriver
Arguments:
NotificationType - indicates what has happened
HwStreamObject - address of minidriver's stream struct
Return Value:
none
--*/
{
va_list Arguments;
PSTREAM_REQUEST_BLOCK SRB;
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(
HwStreamObject,
STREAM_OBJECT,
HwStreamObject
);
PDEVICE_EXTENSION DeviceExtension;
KIRQL Irql;
#if DBG
PMDL CurrentMdl;
#endif
va_start(Arguments, HwStreamObject);
ASSERT(HwStreamObject != NULL);
DeviceExtension = StreamObject->DeviceExtension;
ASSERT((DeviceExtension->BeginMinidriverCallin == SCBeginSynchronizedMinidriverCallin) ||
(DeviceExtension->BeginMinidriverCallin == SCBeginUnsynchronizedMinidriverCallin));
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
} // if nosync
#endif
//
// optimization for async drivers - just directly call back the request
// rather than queuing it on the DPC processed completed list.
//
if ((DeviceExtension->NoSync) && (NotificationType == StreamRequestComplete)) {
SRB = CONTAINING_RECORD(va_arg(Arguments,
PHW_STREAM_REQUEST_BLOCK),
STREAM_REQUEST_BLOCK,
HwSRB);
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
//
// Clear the active flag.
//
ASSERT(SRB->Flags & SRB_FLAGS_IS_ACTIVE);
SRB->Flags &= ~SRB_FLAGS_IS_ACTIVE;
#if DBG
//
// assert the MDL list.
//
if (SRB->HwSRB.Irp) {
CurrentMdl = SRB->HwSRB.Irp->MdlAddress;
while (CurrentMdl) {
CurrentMdl = CurrentMdl->Next;
} // while
} // if IRP
ASSERT(SRB->HwSRB.Flags & SRB_HW_FLAGS_STREAM_REQUEST);
if ((SRB->HwSRB.Command == SRB_READ_DATA) ||
(SRB->HwSRB.Command == SRB_WRITE_DATA)) {
ASSERT(SRB->HwSRB.Flags & SRB_HW_FLAGS_DATA_TRANSFER);
} else {
ASSERT(!(SRB->HwSRB.Flags & SRB_HW_FLAGS_DATA_TRANSFER));
} // if read/write
#endif
if (SRB->DoNotCallBack) {
DebugPrint((DebugLevelError, "'ScNotify: NOT calling back request - Irp = %x, S# = %x\n",
SRB->HwSRB.Irp, StreamObject->HwStreamObject.StreamNumber));
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
return;
} // if NoCallback
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
DebugPrint((DebugLevelTrace, "'SCNotification: Completing async stream Irp %x, S# = %x, SRB = %x, Func = %x, Callback = %x, SRB->IRP = %x\n",
SRB->HwSRB.Irp, StreamObject->HwStreamObject.StreamNumber,
SRB, SRB->HwSRB.Command, SRB->Callback, SRB->HwSRB.Irp));
(SRB->Callback) (SRB);
return;
} // if nosync & complete
BEGIN_MINIDRIVER_STREAM_CALLIN(DeviceExtension, &Irql);
switch (NotificationType) {
case ReadyForNextStreamDataRequest:
//
// Start next data packet on adapter's stream queue.
//
DebugPrint((DebugLevelTrace, "'StreamClassStreamNotify: ready for next stream data request, S# = %x\n",
StreamObject->HwStreamObject.StreamNumber));
ASSERT(!(StreamObject->ReadyForNextDataReq));
ASSERT(!(DeviceExtension->NoSync));
StreamObject->ReadyForNextDataReq = TRUE;
break;
case ReadyForNextStreamControlRequest:
//
// Start next data packet on adapter's stream queue.
//
DebugPrint((DebugLevelTrace, "'StreamClassStreamNotify: ready for next stream control request, S# = %x\n",
StreamObject->HwStreamObject.StreamNumber));
ASSERT(!(StreamObject->ReadyForNextControlReq));
ASSERT(!(DeviceExtension->NoSync));
StreamObject->ReadyForNextControlReq = TRUE;
break;
case StreamRequestComplete:
SRB = CONTAINING_RECORD(va_arg(Arguments,
PHW_STREAM_REQUEST_BLOCK),
STREAM_REQUEST_BLOCK,
HwSRB);
DebugPrint((DebugLevelTrace, "'SCStreamNot: completing Irp %x, S# = %x, SRB = %x, Command = %x\n",
SRB->HwSRB.Irp, StreamObject->HwStreamObject.StreamNumber, SRB, SRB->HwSRB.Command));
ASSERT(SRB->HwSRB.Status != STATUS_PENDING);
ASSERT(SRB->Flags & SRB_FLAGS_IS_ACTIVE);
//
// Clear the active flag.
//
SRB->Flags &= ~SRB_FLAGS_IS_ACTIVE;
//
// add the SRB to the list of completed SRB's.
//
SRB->HwSRB.NextSRB = StreamObject->ComObj.InterruptData.CompletedSRB;
StreamObject->ComObj.InterruptData.CompletedSRB = &SRB->HwSRB;
#if DBG
//
// assert the MDL list.
//
if (SRB->HwSRB.Irp) {
CurrentMdl = SRB->HwSRB.Irp->MdlAddress;
while (CurrentMdl) {
CurrentMdl = CurrentMdl->Next;
} // while
} // if IRP
ASSERT(SRB->HwSRB.Flags & SRB_HW_FLAGS_STREAM_REQUEST);
if ((SRB->HwSRB.Command == SRB_READ_DATA) ||
(SRB->HwSRB.Command == SRB_WRITE_DATA)) {
ASSERT(SRB->HwSRB.Flags & SRB_HW_FLAGS_DATA_TRANSFER);
} else {
ASSERT(!(SRB->HwSRB.Flags & SRB_HW_FLAGS_DATA_TRANSFER));
} // if read/write
#endif
break;
case SignalMultipleStreamEvents:
{
GUID *EventGuid = va_arg(Arguments, GUID *);
ULONG EventItem = va_arg(Arguments, ULONG);
//
// signal all events that match the criteria. note that we are
// already
// at the level required for synchronizing the list, so no lock
// type is specified.
//
KsGenerateEventList(EventGuid,
EventItem,
&StreamObject->NotifyList,
KSEVENTS_NONE,
NULL);
} // case event
break;
case SignalStreamEvent:
KsGenerateEvent(va_arg(Arguments, PKSEVENT_ENTRY));
break;
case DeleteStreamEvent:
{
PKSEVENT_ENTRY EventEntry;
//
// remove the entry from the list, and add it to the dead list.
// note
// that we are already at the correct sync level to do this.
//
EventEntry = va_arg(Arguments, PKSEVENT_ENTRY);
RemoveEntryList(&EventEntry->ListEntry);
InsertTailList(&DeviceExtension->DeadEventList,
&EventEntry->ListEntry);
}
break;
default:
ASSERT(0);
}
va_end(Arguments);
END_MINIDRIVER_STREAM_CALLIN(StreamObject, &Irql);
} // end StreamClassStreamNotification()
VOID
StreamClassDeviceNotification(
IN STREAM_MINIDRIVER_DEVICE_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
...
)
/*++
Routine Description:
device notification routine for minidriver
Arguments:
NotificationType - indicates what has happened
HwDeviceExtension - address of minidriver's device extension
Return Value:
none
--*/
{
va_list Arguments;
PSTREAM_REQUEST_BLOCK SRB;
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) HwDeviceExtension - 1;
KIRQL Irql;
va_start(Arguments, HwDeviceExtension);
ASSERT(HwDeviceExtension != NULL);
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
} // if nosync
#endif
BEGIN_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
switch (NotificationType) {
case ReadyForNextDeviceRequest:
//
// Start next control packet on adapter's device queue.
//
DebugPrint((DebugLevelTrace, "'StreamClassDeviceNotify: ready for next stream.\n"));
ASSERT(!(DeviceExtension->ReadyForNextReq));
ASSERT(!(DeviceExtension->NoSync));
DeviceExtension->ReadyForNextReq = TRUE;
break;
case DeviceRequestComplete:
SRB = CONTAINING_RECORD(va_arg(Arguments, PHW_STREAM_REQUEST_BLOCK),
STREAM_REQUEST_BLOCK,
HwSRB);
DebugPrint((DebugLevelTrace, "'StreamClassDeviceNotify: stream request complete.\n"));
ASSERT(SRB->HwSRB.Status != STATUS_PENDING);
ASSERT(SRB->Flags & SRB_FLAGS_IS_ACTIVE);
ASSERT(!(SRB->HwSRB.Flags & SRB_HW_FLAGS_STREAM_REQUEST));
ASSERT(!(SRB->HwSRB.Flags & SRB_HW_FLAGS_DATA_TRANSFER));
//
// Clear the active flag.
//
SRB->Flags &= ~SRB_FLAGS_IS_ACTIVE;
//
// add the SRB to the list of completed SRB's.
//
SRB->HwSRB.NextSRB = DeviceExtension->ComObj.InterruptData.CompletedSRB;
DeviceExtension->ComObj.InterruptData.CompletedSRB = &SRB->HwSRB;
break;
case SignalMultipleDeviceEvents:
{
GUID *EventGuid = va_arg(Arguments, GUID *);
ULONG EventItem = va_arg(Arguments, ULONG);
//
// signal all events that match the criteria. note that we are
// already
// at the level required for synchronizing the list, so no lock
// type is specified.
//
PFILTER_INSTANCE FilterInstance;
ASSERT( 0 == DeviceExtension->MinidriverData->
HwInitData.FilterInstanceExtensionSize);
//
// this is synced should not need to avoid race
//
FilterInstance = (PFILTER_INSTANCE)
DeviceExtension->FilterInstanceList.Flink;
if ( (PLIST_ENTRY)FilterInstance ==
&DeviceExtension->FilterInstanceList ) {
DebugPrint((DebugLevelWarning, "Filter Closed\n"));
break;
}
FilterInstance = CONTAINING_RECORD(FilterInstance,
FILTER_INSTANCE,
NextFilterInstance);
KsGenerateEventList(EventGuid,
EventItem,
&FilterInstance->NotifyList,
KSEVENTS_NONE,
NULL);
}
break;
#if ENABLE_MULTIPLE_FILTER_TYPES
case SignalMultipleDeviceInstanceEvents:
{
PFILTER_INSTANCE FilterInstance =
(PFILTER_INSTANCE)va_arg( Arguments, PVOID) -1;
GUID *EventGuid = va_arg(Arguments, GUID *);
ULONG EventItem = va_arg(Arguments, ULONG);
//
// signal all events that match the criteria. note that we are
// already
// at the level required for synchronizing the list, so no lock
// type is specified.
//
KsGenerateEventList(EventGuid,
EventItem,
&FilterInstance->NotifyList,
KSEVENTS_NONE,
NULL);
}
break;
#endif // ENABLE_MULTIPLE_FILTER_TYPES
case SignalDeviceEvent:
KsGenerateEvent(va_arg(Arguments, PKSEVENT_ENTRY));
break;
case DeleteDeviceEvent:
{
PKSEVENT_ENTRY EventEntry;
//
// remove the entry from the list, and add it to the dead list.
// note
// that we are already at the correct sync level to do this.
//
EventEntry = va_arg(Arguments, PKSEVENT_ENTRY);
RemoveEntryList(&EventEntry->ListEntry);
InsertTailList(&DeviceExtension->DeadEventList,
&EventEntry->ListEntry);
}
break;
default:
ASSERT(0);
}
va_end(Arguments);
//
// Request a DPC be queued after the interrupt completes.
//
END_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
} // end StreamClassDeviceNotification()
VOID
StreamClassScheduleTimer(
IN OPTIONAL PHW_STREAM_OBJECT HwStreamObject,
IN PVOID HwDeviceExtension,
IN ULONG NumberOfMicroseconds,
IN PHW_TIMER_ROUTINE TimerRoutine,
IN PVOID Context
)
/*++
Routine Description:
schedules a timer callback for the minidriver
Arguments:
HwStreamObject - address of minidriver's stream struct
HwDeviceExtension - address of minidriver's device extension
NumberOfMicroseconds - # of microseconds that should elapse before calling
TimerRoutine - routine to call when the time expires
Context - value to pass into the timer routine
Return Value:
none
--*/
{
PSTREAM_OBJECT StreamObject;
KIRQL Irql;
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)
(HwDeviceExtension) - 1;
PCOMMON_OBJECT ComObj;
ASSERT(HwDeviceExtension != NULL);
StreamObject = CONTAINING_RECORD(
HwStreamObject,
STREAM_OBJECT,
HwStreamObject
);
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
} // if nosync
#endif
//
// The driver wants to set the timer.
// Save the timer parameters.
//
BEGIN_MINIDRIVER_STREAM_CALLIN(DeviceExtension, &Irql);
if (HwStreamObject) {
ComObj = &StreamObject->ComObj;
//DebugPrint((DebugLevelVerbose, "'StreamClassScheduleTimer for stream.\n"));
} else {
StreamObject = NULL;
ComObj = &DeviceExtension->ComObj;
ComObj->InterruptData.Flags |= INTERRUPT_FLAGS_NOTIFICATION_REQUIRED;
DebugPrint((DebugLevelVerbose, "'StreamClassScheduleTimer for device.\n"));
}
//
// assert that a timer is not scheduled multiple times.
//
#if DBG
if ((ComObj->InterruptData.Flags & INTERRUPT_FLAGS_TIMER_CALL_REQUEST) &&
((NumberOfMicroseconds != 0) && (ComObj->InterruptData.HwTimerValue
!= 0))) {
DebugPrint((DebugLevelFatal, "Stream Minidriver scheduled same timer twice!\n"));
DEBUG_BREAKPOINT();
ASSERT(1 == 0);
} // if scheduled twice
#endif
ComObj->InterruptData.Flags |= INTERRUPT_FLAGS_TIMER_CALL_REQUEST;
ComObj->InterruptData.HwTimerRoutine = TimerRoutine;
ComObj->InterruptData.HwTimerValue = NumberOfMicroseconds;
ComObj->InterruptData.HwTimerContext = Context;
if (StreamObject) {
END_MINIDRIVER_STREAM_CALLIN(StreamObject, &Irql);
} else {
END_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
} // if streamobject
}
VOID
StreamClassCallAtNewPriority(
IN OPTIONAL PHW_STREAM_OBJECT HwStreamObject,
IN PVOID HwDeviceExtension,
IN STREAM_PRIORITY Priority,
IN PHW_PRIORITY_ROUTINE PriorityRoutine,
IN PVOID Context
)
/*++
Routine Description:
schedules a callback at the specified priority
Arguments:
HwStreamObject - address of minidriver's stream struct
HwDeviceExtension - address of minidriver's device extension
Priority - priority at which to call minidriver
PriorityRoutine - routine to call at specified priority
Context - value to pass into the priority routine
Return Value:
none
--*/
{
PSTREAM_OBJECT StreamObject;
KIRQL Irql;
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)
(HwDeviceExtension) - 1;
PCOMMON_OBJECT ComObj;
ASSERT(HwDeviceExtension != NULL);
StreamObject = CONTAINING_RECORD(
HwStreamObject,
STREAM_OBJECT,
HwStreamObject
);
//
// The driver wants to get called back at a different priority.
// Save the priority parameters.
//
if (Priority == LowToHigh) {
//
// the minidriver wishes to be called from low priority to high
// we must call it directly from this routine as we cannot use
// the interruptcontext structure due to the possibility of
// reentrancy.
//
DebugPrint((DebugLevelVerbose, "'StreamClassChangePriority LowToHigh.\n"));
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
DeviceExtension->SynchronizeExecution(
DeviceExtension->InterruptObject,
(PVOID) PriorityRoutine,
Context);
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
//
// Call the DPC directly to check for work.
//
StreamClassDpc(NULL,
DeviceExtension->DeviceObject,
NULL,
NULL);
KeLowerIrql(Irql);
} else {
if (HwStreamObject) {
DebugPrint((DebugLevelVerbose, "'StreamClassChangePriority to %x for stream %x\n",
StreamObject->ComObj.InterruptData.HwPriorityLevel, StreamObject->HwStreamObject.StreamNumber));
ComObj = &StreamObject->ComObj;
SCRequestDpcForStream(StreamObject);
} else {
DebugPrint((DebugLevelVerbose, "'StreamClassChangePriority for device.\n"));
ComObj = &DeviceExtension->ComObj;
ComObj->InterruptData.Flags |= INTERRUPT_FLAGS_NOTIFICATION_REQUIRED;
} // if streamobject
#if DBG
if ((ComObj->InterruptData.Flags &
INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST) ||
((ComObj->PriorityWorkItemScheduled) && (Priority == Low))) {
DebugPrint((DebugLevelFatal, "Stream Minidriver scheduled priority twice!\n"));
DEBUG_BREAKPOINT();
ASSERT(1 == 0);
} // if scheduled twice
ComObj->PriorityWorkItemScheduled = TRUE;
#endif
ComObj->InterruptData.Flags |= INTERRUPT_FLAGS_PRIORITY_CHANGE_REQUEST;
ComObj->InterruptData.HwPriorityLevel = Priority;
ComObj->InterruptData.HwPriorityRoutine = PriorityRoutine;
ComObj->InterruptData.HwPriorityContext = Context;
} // if lowtohigh
}
VOID
StreamClassLogError(
IN PVOID HwDeviceExtension,
IN PHW_STREAM_REQUEST_BLOCK hwSRB OPTIONAL,
IN ULONG ErrorCode,
IN ULONG UniqueId
)
/*++
Routine Description:
This routine saves the error log information, and queues a DPC if necessary.
Arguments:
HwDeviceExtension - Supplies the HBA miniport driver's adapter data storage.
SRB - Supplies an optional pointer to SRB if there is one.
ErrorCode - Supplies an error code indicating the type of error.
UniqueId - Supplies a unique identifier for the error.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION deviceExtension =
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
PDEVICE_OBJECT DeviceObject = deviceExtension->DeviceObject;
PERROR_LOG_ENTRY errorLogEntry;
PSTREAM_REQUEST_BLOCK SRB;
KIRQL Irql;
//
// If the error log entry is already full, then dump the error.
//
DEBUG_BREAKPOINT();
ASSERT(HwDeviceExtension != NULL);
BEGIN_MINIDRIVER_DEVICE_CALLIN(deviceExtension, &Irql);
DebugPrint((DebugLevelError, "StreamClassLogError.\n"));
if (deviceExtension->ComObj.InterruptData.Flags & INTERRUPT_FLAGS_LOG_ERROR) {
DEBUG_BREAKPOINT();
DebugPrint((1, "'StreamClassLogError: Ignoring error log packet.\n"));
return;
}
//
// Save the error log data in the log entry.
//
errorLogEntry = &deviceExtension->ComObj.InterruptData.LogEntry;
errorLogEntry->ErrorCode = ErrorCode;
errorLogEntry->UniqueId = UniqueId;
//
// Get the sequence number from the SRB.
//
if (hwSRB != NULL) {
DEBUG_BREAKPOINT();
SRB = CONTAINING_RECORD(hwSRB,
STREAM_REQUEST_BLOCK,
HwSRB);
errorLogEntry->SequenceNumber = SRB->SequenceNumber;
} else {
DEBUG_BREAKPOINT();
errorLogEntry->SequenceNumber = 0;
}
//
// Indicate that the error log entry is in use and that a
// notification
// is required.
//
deviceExtension->ComObj.InterruptData.Flags |= INTERRUPT_FLAGS_LOG_ERROR;
END_MINIDRIVER_DEVICE_CALLIN(deviceExtension, &Irql);
return;
} // end StreamClassLogError()
#if DBG
VOID
StreamClassDebugPrint(
STREAM_DEBUG_LEVEL DebugPrintLevel,
PSCHAR DebugMessage,
...
)
/*++
Routine Description:
Debug print routine
Arguments:
DebugPrintLevel - Debug print level
DebugMessage - message to print
Return Value:
None
--*/
{
va_list ap;
va_start(ap, DebugMessage);
if (DebugPrintLevel <= (INT) StreamDebug) {
_vsnprintf(StreamBuffer, STREAM_BUFFER_SIZE-1, DebugMessage, ap);
DbgPrint(StreamBuffer);
}
va_end(ap);
} // end StreamClassDebugPrint()
#else
//
// StreamClassDebugPrint stub
//
VOID
StreamClassDebugPrint(
STREAM_DEBUG_LEVEL DebugPrintLevel,
PSCHAR DebugMessage,
...
)
{
}
#endif
STREAM_PHYSICAL_ADDRESS
StreamClassGetPhysicalAddress(
IN PVOID HwDeviceExtension,
IN PHW_STREAM_REQUEST_BLOCK HwSRB OPTIONAL,
IN PVOID VirtualAddress,
IN STREAM_BUFFER_TYPE Type,
OUT ULONG * Length
)
/*++
Routine Description:
Convert virtual address to physical address for DMA.
Arguments:
HwDeviceExtension - Supplies the HBA miniport driver's adapter data storage.
HwSRB - Supplies an optional pointer to SRB if there is one.
VirtualAddress - pointer to address for which to retrieve physical address
Type - type of buffer in VirtualAddress
Return Value:
Returns phys address and length or NULL if invalid address
--*/
{
PDEVICE_EXTENSION deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
PKSSTREAM_HEADER CurrentHeader;
PKSSCATTER_GATHER ScatterList;
PSTREAM_REQUEST_BLOCK SRB;
ULONG VirtualOffset;
PHYSICAL_ADDRESS address;
ULONG NumberOfBuffers,
i,
SizeSoFar = 0,
ListSize = 0;
ULONG DataBytes;
PHW_STREAM_OBJECT HwStreamObject;
ASSERT(HwDeviceExtension != NULL);
switch (Type) {
case PerRequestExtension:
ASSERT(HwSRB);
SRB = CONTAINING_RECORD((PHW_STREAM_REQUEST_BLOCK) HwSRB,
STREAM_REQUEST_BLOCK,
HwSRB);
VirtualOffset = (ULONG) ((ULONG_PTR) VirtualAddress - (ULONG_PTR) (SRB + 1));
*Length = SRB->ExtensionLength - VirtualOffset;
address.QuadPart = SRB->PhysicalAddress.QuadPart +
sizeof(STREAM_REQUEST_BLOCK) +
VirtualOffset;
return (address);
case DmaBuffer:
VirtualOffset = (ULONG) ((ULONG_PTR) VirtualAddress - (ULONG_PTR) deviceExtension->DmaBuffer);
*Length = deviceExtension->DmaBufferLength - VirtualOffset;
address.QuadPart = deviceExtension->DmaBufferPhysical.QuadPart
+ VirtualOffset;
return (address);
case SRBDataBuffer:
ASSERT(HwSRB);
SRB = CONTAINING_RECORD((PHW_STREAM_REQUEST_BLOCK) HwSRB,
STREAM_REQUEST_BLOCK,
HwSRB);
HwStreamObject = SRB->HwSRB.StreamObject;
ASSERT(HwStreamObject);
CurrentHeader = SRB->HwSRB.CommandData.DataBufferArray;
NumberOfBuffers = SRB->HwSRB.NumberOfBuffers;
for (i = 0; i < NumberOfBuffers; i++) {
if (SRB->HwSRB.Command == SRB_WRITE_DATA) {
DataBytes = CurrentHeader->DataUsed;
} else { // if write
DataBytes = CurrentHeader->FrameExtent;
} // if write
//
// see if the buffer is within the range of this element
//
VirtualOffset = (ULONG) ((ULONG_PTR) VirtualAddress - (ULONG_PTR) CurrentHeader->Data + 1);
if (VirtualOffset > DataBytes) {
//
// buffer not within this element. add the size of this one
// to our total.
//
SizeSoFar += DataBytes;
} else {
//
// we've found the element. Now calculate the phys
// address from the phys list.
//
// GUBGUB - This function is seldom called. n is most ofen small
// <=3. The O(n^2) performance concern is insignificant.
// - this algorithm gets n^2 expensive for long lists
// an alternative is to build a separate array which holds
// the mapping between the stream headers and the s/g
// elements
// for each header. We currently don't get that many
// elements
// so the below is more efficient now.
//
ScatterList = SRB->HwSRB.ScatterGatherBuffer;
while (SizeSoFar > ListSize) {
ListSize += ScatterList++->Length;
}
//
// Now ScatterList points to the correct scatter/gather
// element.
//
while (VirtualOffset > ScatterList->Length) {
VirtualOffset -= ScatterList->Length;
ScatterList++;
}
*Length = ScatterList->Length - VirtualOffset + 1;
address.QuadPart = ScatterList->PhysicalAddress.QuadPart
+ VirtualOffset - 1;
return (address);
} // if buffer
CurrentHeader = ((PKSSTREAM_HEADER) ((PBYTE) CurrentHeader +
HwStreamObject->StreamHeaderMediaSpecific +
HwStreamObject->StreamHeaderWorkspace));
} // for # buffers
DebugPrint((DebugLevelFatal, "StreamClassGetPhysicalAddress: address not in SRB!\n"));
default:
DEBUG_BREAKPOINT();
*Length = 0;
address.QuadPart = (LONGLONG) 0;
return (address);
} // switch
} // end StreamClassGetPhysicalAddress()
VOID
StreamClassDebugAssert(
IN PCHAR File,
IN ULONG Line,
IN PCHAR AssertText,
IN ULONG AssertValue
)
/*++
Routine Description:
This is the minidriver debug assert call. When running a checked version
of the class driver, asserts are recognized resulting in a debug
message and breakpoint. When running a free version of the port driver,
asserts are ignored.
Arguments:
File - file name where assert occurred
Line - line number of assert
AssertText - Text to be printed
AssertValue - value to be printed
Return Value:
none
--*/
{
DebugPrint((DebugLevelError, "(%s:%d) Assert failed (%s)=0x%x\n", File, Line, AssertText, AssertValue));
DbgBreakPoint();
}
VOID
SCRequestDpcForStream(
IN PSTREAM_OBJECT StreamObject
)
/*++
Routine Description:
This routine places a stream object on the NeedyStream queue if it is
not already there
Arguments:
StreamObject - pointer to stream object
Return Value:
none
--*/
{
PDEVICE_EXTENSION DeviceExtension = StreamObject->DeviceExtension;
//
// add the stream to the queue of needy streams unless it is already
// there.
//
#if DBG
if (DeviceExtension->NeedyStream) {
ASSERT(DeviceExtension->NeedyStream->OnNeedyQueue);
}
#endif
ASSERT(StreamObject->NextNeedyStream != StreamObject);
if (!(StreamObject->OnNeedyQueue)) {
ASSERT(!StreamObject->NextNeedyStream);
DebugPrint((DebugLevelVerbose, "'SCRequestDpc: Stream %x added to needy queue, Next = %x\n",
StreamObject, StreamObject->NextNeedyStream));
StreamObject->OnNeedyQueue = TRUE;
StreamObject->NextNeedyStream = DeviceExtension->NeedyStream;
DeviceExtension->NeedyStream = StreamObject;
ASSERT(StreamObject->NextNeedyStream != StreamObject);
} else {
DebugPrint((DebugLevelVerbose, "'SCRequestDpc: Stream %x already on needy queue\n",
StreamObject));
} // if on needy queue
StreamObject->ComObj.InterruptData.Flags |= INTERRUPT_FLAGS_NOTIFICATION_REQUIRED;
}
VOID
StreamClassAbortOutstandingRequests(
IN PVOID HwDeviceExtension,
IN PHW_STREAM_OBJECT HwStreamObject,
IN NTSTATUS Status
)
/*++
Routine Description:
aborts outstanding requests on the specified device or stream
Arguments:
HwStreamObject - address of minidriver's stream struct
HwDeviceExtension - device extension
Status - NT Status to use for aborting
Return Value:
none
--*/
{
PSTREAM_OBJECT StreamObject = NULL;
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) HwDeviceExtension - 1;
KIRQL Irql;
PLIST_ENTRY SrbEntry,
ListEntry;
PSTREAM_REQUEST_BLOCK CurrentSrb;
PHW_STREAM_OBJECT CurrentHwStreamObject;
PSTREAM_OBJECT CurrentStreamObject;
ASSERT(HwDeviceExtension != NULL);
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
} // if nosync
#endif
if (HwStreamObject) {
DEBUG_BREAKPOINT();
StreamObject = CONTAINING_RECORD(HwStreamObject,
STREAM_OBJECT,
HwStreamObject);
}
BEGIN_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
DebugPrint((DebugLevelError, "StreamClassAbortOutstandingRequests.\n"));
//
// walk the outstanding queue and abort all requests on it.
//
SrbEntry = ListEntry = &DeviceExtension->OutstandingQueue;
while (SrbEntry->Flink != ListEntry) {
SrbEntry = SrbEntry->Flink;
//
// follow the link to the Srb
//
CurrentSrb = CONTAINING_RECORD(SrbEntry,
STREAM_REQUEST_BLOCK,
SRBListEntry);
CurrentHwStreamObject = CurrentSrb->HwSRB.StreamObject;
if ((!HwStreamObject) || (CurrentHwStreamObject ==
HwStreamObject)) {
//
// abort this one and show that it's ready for a next request,
// assuming it's active. it might not be active if the
// minidriver
// just called it back.
//
if (CurrentSrb->Flags & SRB_FLAGS_IS_ACTIVE) {
//
// Clear the active flag.
//
CurrentSrb->Flags &= ~SRB_FLAGS_IS_ACTIVE;
CurrentSrb->HwSRB.Status = Status;
if (CurrentSrb->HwSRB.Flags & SRB_HW_FLAGS_STREAM_REQUEST) {
CurrentStreamObject = CONTAINING_RECORD(
CurrentHwStreamObject,
STREAM_OBJECT,
HwStreamObject
);
//
// indicate that the appropriate queue is ready for a
// next
// request.
//
if (CurrentSrb->HwSRB.Flags & SRB_HW_FLAGS_DATA_TRANSFER) {
CurrentStreamObject->ReadyForNextDataReq = TRUE;
} else { // if data
CurrentStreamObject->ReadyForNextControlReq = TRUE;
} // if data
DebugPrint((DebugLevelTrace, "'SCAbort: aborting stream IRP %x\n",
CurrentSrb->HwSRB.Irp));
//
// add the SRB to the list of completed stream SRB's.
//
CurrentSrb->HwSRB.NextSRB = CurrentStreamObject->ComObj.InterruptData.CompletedSRB;
CurrentStreamObject->ComObj.InterruptData.CompletedSRB = &CurrentSrb->HwSRB;
//
// add this stream to the queue of needy streams
//
SCRequestDpcForStream(CurrentStreamObject);
} else { // if stream
DebugPrint((DebugLevelTrace, "'SCAbort: aborting device IRP %x\n",
CurrentSrb->HwSRB.Irp));
//
// add the SRB to the list of completed device SRB's.
//
DEBUG_BREAKPOINT();
CurrentSrb->HwSRB.NextSRB = DeviceExtension->ComObj.InterruptData.CompletedSRB;
DeviceExtension->ComObj.InterruptData.CompletedSRB = &CurrentSrb->HwSRB;
DeviceExtension->ReadyForNextReq = TRUE;
} // if stream
} // if active
} // if aborting this one
} // while list entry
//
// all necessary requests have been aborted. exit.
//
END_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
}
PKSEVENT_ENTRY
StreamClassGetNextEvent(
IN PVOID HwInstanceExtension_OR_HwDeviceExtension,
IN OPTIONAL PHW_STREAM_OBJECT HwStreamObject,
IN OPTIONAL GUID * EventGuid,
IN OPTIONAL ULONG EventItem,
IN OPTIONAL PKSEVENT_ENTRY CurrentEvent
)
/*++
Routine Description:
Arguments:
HwInstanceExtenion: was HwDeviceExtension. But we now support multiinstances.
Therefore, we need the HwInstanceExtension instead for MF.
CurrentEvent - event (if any) to get the next from
Return Value:
next event, if any
--*/
{
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(HwStreamObject,
STREAM_OBJECT,
HwStreamObject);
PFILTER_INSTANCE FilterInstance;
PDEVICE_EXTENSION DeviceExtension;
//(PDEVICE_EXTENSION) HwDeviceExtension - 1;
PLIST_ENTRY EventListEntry,
EventEntry;
PKSEVENT_ENTRY NextEvent,
ReturnEvent = NULL;
KIRQL Irql;
//
// see which is HwInstanceExtension_OR_HwDeviceExtension
// need to try HwInstanceExtension first because is has a smaller
// offset backward so we don't touch invalid memory.
//
// try
FilterInstance = (PFILTER_INSTANCE)
HwInstanceExtension_OR_HwDeviceExtension-1;
if ( SIGN_FILTER_INSTANCE != FilterInstance->Signature ) {
//
// single instance legacy driver
//
DeviceExtension = (PDEVICE_EXTENSION)
HwInstanceExtension_OR_HwDeviceExtension -1;
ASSERT( 0 == DeviceExtension->MinidriverData->
HwInitData.FilterInstanceExtensionSize);
if (DeviceExtension->NoSync) {
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
}
if ( IsListEmpty( &DeviceExtension->FilterInstanceList ) ) {
//
// filter has been closed. but we are called.
// Single instance drivers do not receive open/close
// they don't know when to sotp calling this.
// We need to check.
//
DebugPrint((DebugLevelWarning, "GetNextEvent no open filters\n"));
if (DeviceExtension->NoSync) {
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
}
return NULL;
}
FilterInstance = (PFILTER_INSTANCE)
DeviceExtension->FilterInstanceList.Flink;
if (DeviceExtension->NoSync) {
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
}
FilterInstance = CONTAINING_RECORD(FilterInstance,
FILTER_INSTANCE,
NextFilterInstance);
}
else {
DeviceExtension = FilterInstance ->DeviceExtension;
}
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
}
#endif
//
// take the spinlock if we are unsynchronized.
//
BEGIN_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
//
// loop thru the events, trying to find the requested one.
//
if (HwStreamObject) {
EventListEntry = EventEntry = &StreamObject->NotifyList;
} else {
EventListEntry = EventEntry = &FilterInstance->NotifyList;
}
while (EventEntry->Flink != EventListEntry) {
EventEntry = EventEntry->Flink;
NextEvent = CONTAINING_RECORD(EventEntry,
KSEVENT_ENTRY,
ListEntry);
if ((EventItem == NextEvent->EventItem->EventId) &&
(!EventGuid || IsEqualGUIDAligned(EventGuid, NextEvent->EventSet->Set))) {
//
// if we are to return the 1st event which matches, break.
//
if (!CurrentEvent) {
ReturnEvent = NextEvent;
break;
} // if !current
//
// if we are to return the next event after the specified one,
// check
// to see if these match. If they do, zero the specified event
// so
// that we will return the next event of the specified type.
//
if (CurrentEvent == NextEvent) {
CurrentEvent = NULL;
} // if cur=next
} // if guid & id match
} // while events
//
// if we are unsynchronized, release the spinlock acquired in the macro
// above.
//
ASSERT(--DeviceExtension->LowerApiThreads == 0); // typo barfs. but this is truely ok
if (DeviceExtension->NoSync) {
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
}
//
// return the next event, if any.
//
return (ReturnEvent);
}
VOID
StreamClassQueryMasterClock(
IN PHW_STREAM_OBJECT HwStreamObject,
IN HANDLE MasterClockHandle,
IN TIME_FUNCTION TimeFunction,
IN PHW_QUERY_CLOCK_ROUTINE ClockCallbackRoutine
)
/*++
Routine Description:
Arguments:
HwStreamObject - address of minidriver's stream struct
Context - value to pass into the time callback routine
Return Value:
none
--*/
{
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(HwStreamObject,
STREAM_OBJECT,
HwStreamObject);
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) StreamObject->DeviceExtension;
KIRQL Irql;
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
} // if nosync
#endif
BEGIN_MINIDRIVER_STREAM_CALLIN(DeviceExtension, &Irql);
//
// save away the parameters for the clock query. The DPC will do the
// actual processing.
//
StreamObject->ComObj.InterruptData.HwQueryClockRoutine = ClockCallbackRoutine;
StreamObject->ComObj.InterruptData.HwQueryClockFunction = TimeFunction;
StreamObject->ComObj.InterruptData.Flags |= INTERRUPT_FLAGS_CLOCK_QUERY_REQUEST;
END_MINIDRIVER_STREAM_CALLIN(StreamObject, &Irql);
}
#if ENABLE_MULTIPLE_FILTER_TYPES
VOID
StreamClassFilterReenumerateStreams(
IN PVOID HwInstanceExtension,
IN ULONG StreamDescriptorSize )
/*++
Description:
Reenumerates all streams on the filter instance.
This is used to increase the number of pins exposed to
the world so that application can make connections on
new streams exposed. It's caller's responsibility
not to change the order of the streams that have been
open ( connected ). If there is no reduction of the streams
This won't be an issue.
Arguments;
HwInstanceExtension:
The instanc extension pointer we gave to the mini driver
StreamDecriptorSize:
# of bytes to contain the new stream descriptor for the filter
Return Valuse:
None
--*/
{
PFILTER_INSTANCE FilterInstance;
PDEVICE_EXTENSION DeviceExtension;
KIRQL Irql;
FilterInstance = ( PFILTER_INSTANCE ) HwInstanceExtension -1;
DeviceExtension = FilterInstance->DeviceExtension;
//
// take the spinlock if we are unsynchronized.
//
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
}
# endif
BEGIN_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
//
// show that we need to rescan the stream info, and set the new size in
// the config info structure.
//
DeviceExtension->ComObj.InterruptData.Flags |=
INTERRUPT_FLAGS_NEED_STREAM_RESCAN;
InterlockedExchange( &FilterInstance->NeedReenumeration, 1 );
FilterInstance->StreamDescriptorSize = StreamDescriptorSize;
//
// queue a DPC to service the request.
//
END_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
return;
}
#endif // ENABLE_MULTIPLE_FILTER_TYPES
VOID
StreamClassReenumerateStreams(
IN PVOID HwDeviceExtension,
IN ULONG StreamDescriptorSize
)
/*++
Routine Description:
Reenumerates all streams on the device
Arguments:
HwDeviceExtension - pointer to minidriver's device extension
StreamDescriptorSize - size of the buffer needed by the minidriver to
hold the stream info.
Return Value:
none
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) HwDeviceExtension - 1;
KIRQL Irql;
//
// take the spinlock if we are unsynchronized.
//
TRAP;
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
} // if nosync
#endif
BEGIN_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
//
// show that we need to rescan the stream info, and set the new size in
// the config info structure.
//
ASSERT(!DeviceExtension->ComObj.InterruptData.Flags &
INTERRUPT_FLAGS_NEED_STREAM_RESCAN);
DeviceExtension->ComObj.InterruptData.Flags |=
INTERRUPT_FLAGS_NEED_STREAM_RESCAN;
DeviceExtension->ConfigurationInformation->StreamDescriptorSize =
StreamDescriptorSize;
//
// queue a DPC to service the request.
//
END_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
return;
}
#define FCC(ch4) ((((DWORD)(ch4) & 0xFF) << 24) | \
(((DWORD)(ch4) & 0xFF00) << 8) | \
(((DWORD)(ch4) & 0xFF0000) >> 8) | \
(((DWORD)(ch4) & 0xFF000000) >> 24))
// OK to have zero instances of pin In this case you will have to
// Create a pin to have even one instance
#define REG_PIN_B_ZERO 0x1
// The filter renders this input
#define REG_PIN_B_RENDERER 0x2
// OK to create many instance of pin
#define REG_PIN_B_MANY 0x4
// This is an Output pin
#define REG_PIN_B_OUTPUT 0x8
typedef struct {
ULONG Version;
ULONG Merit;
ULONG Pins;
ULONG Reserved;
} REGFILTER_REG;
typedef struct {
ULONG Signature;
ULONG Flags;
ULONG PossibleInstances;
ULONG MediaTypes;
ULONG MediumTypes;
ULONG CategoryOffset;
ULONG MediumOffset; // By definition, we always have a Medium
//#ifdef _WIN64
//This method create filterdata that upset ring3 code.
//ULONG ulPad; // align to quadword to make ia64 happy
//#endif
} REGFILTERPINS_REG2;
NTSTATUS
StreamClassRegisterFilterWithNoKSPins(
IN PDEVICE_OBJECT DeviceObject,
IN const GUID * InterfaceClassGUID,
IN ULONG PinCount,
IN BOOL * PinDirection,
IN KSPIN_MEDIUM * MediumList,
IN OPTIONAL GUID * CategoryList
)
/*++
Routine Description:
This routine is used to register filters with DShow which have no
KS pins and therefore do not stream in kernel mode. This is typically
used for TvTuners, Crossbars, and the like. On exit, a new binary
registry key, "FilterData" is created which contains the Mediums and
optionally the Categories for each pin on the filter.
Arguments:
DeviceObject -
Device object
InterfaceClassGUID
GUID representing the class to register
PinCount -
Count of the number of pins on this filter
PinDirection -
Array of BOOLS indicating pin direction for each pin (length PinCount)
If TRUE, this pin is an output pin
MediumList -
Array of PKSMEDIUM_DATA (length PinCount)
CategoryList -
Array of GUIDs indicating pin categories (length PinCount) OPTIONAL
Return Value:
NTSTATUS SUCCESS if the Blob was created
--*/
{
NTSTATUS Status;
ULONG CurrentPin;
ULONG TotalCategories;
REGFILTER_REG *RegFilter;
REGFILTERPINS_REG2 UNALIGNED * RegPin;
GUID UNALIGNED * CategoryCache;
KSPIN_MEDIUM UNALIGNED * MediumCache;
ULONG FilterDataLength;
PUCHAR FilterData;
PWSTR SymbolicLinkList;
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
if ((PinCount == 0) || (!InterfaceClassGUID) || (!PinDirection) || (!MediumList)) {
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Calculate the maximum amount of space which could be taken up by
// this cache data.
//
TotalCategories = (CategoryList ? PinCount : 0);
FilterDataLength = sizeof(REGFILTER_REG) +
PinCount * sizeof(REGFILTERPINS_REG2) +
PinCount * sizeof(KSPIN_MEDIUM) +
TotalCategories * sizeof(GUID);
//
// Allocate space to create the BLOB
//
FilterData = ExAllocatePool(PagedPool, FilterDataLength);
if (!FilterData) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Place the header in the data, defaulting the Merit to "unused".
//
DebugPrint((DebugLevelTrace,
"FilterData:%p\n",
FilterData ));
RegFilter = (REGFILTER_REG *) FilterData;
RegFilter->Version = 2;
RegFilter->Merit = 0x200000;
RegFilter->Pins = PinCount;
RegFilter->Reserved = 0;
//
// Calculate the offset to the list of pins, and to the
// MediumList and CategoryList
//
RegPin = (REGFILTERPINS_REG2 *) (RegFilter + 1);
MediumCache = (PKSPIN_MEDIUM) ((PUCHAR) (RegPin + PinCount));
CategoryCache = (GUID *) (MediumCache + PinCount);
//
// Create each pin header, followed by the list of Mediums
// followed by the list of optional categories.
//
for (CurrentPin = 0; CurrentPin < PinCount; CurrentPin++, RegPin++) {
//
// Initialize the pin header.
//
DebugPrint((DebugLevelTrace,
"CurrentPin:%d RegPin:%p MediumCache:%p CategoryCache:%p\n",
CurrentPin, RegPin, MediumCache, CategoryCache ));
RegPin->Signature = FCC('0pi3');
(*(PUCHAR) & RegPin->Signature) += (BYTE) CurrentPin;
RegPin->Flags = (PinDirection[CurrentPin] ? REG_PIN_B_OUTPUT : 0);
RegPin->PossibleInstances = 1;
RegPin->MediaTypes = 0;
RegPin->MediumTypes = 1;
RegPin->MediumOffset = (ULONG) ((PUCHAR) MediumCache - (PUCHAR) FilterData);
*MediumCache++ = MediumList[CurrentPin];
if (CategoryList) {
RegPin->CategoryOffset = (ULONG) ((PUCHAR) CategoryCache - (PUCHAR) FilterData);
*CategoryCache++ = CategoryList[CurrentPin];
} else {
RegPin->CategoryOffset = 0;
}
}
//
// Now create the BLOB in the registry
//
//
// Note for using the flag DEVICE_INTERFACE_INCLUDE_NONACTIVE following:
// PnP change circa 3/30/99 made the funtion IoSetDeviceInterfaceState() become
// asynchronous. It returns SUCCESS even when the enabling is deferred. Now when
// we arrive here, the DeviceInterface is still not enabled, we receive empty
// Symbolic link if the flag is not set. Here we only try to write relevent
// FilterData to the registry. I argue this should be fine for
// 1. Currently, if a device is removed, the registry key for the DeviceClass
// remains and with FilterData.Whatever components use the FilterData should
// be able to handle if the device is removed by either check Control\Linked
// or handling the failure in attempt to make connection to the non-exiting device.
// 2. I have found that if a device is moved between slots ( PCI, USB ports ) the
// DeviceInterface at DeviceClass is reused or at lease become the first entry in
// the registry. Therefore, we will be updating the right entry with the proposed flag.
//
if (NT_SUCCESS(Status = IoGetDeviceInterfaces(
InterfaceClassGUID, // ie.&KSCATEGORY_TVTUNER,etc.
DeviceObject, // IN PDEVICE_OBJECT PhysicalDeviceObject,OPTIONAL,
DEVICE_INTERFACE_INCLUDE_NONACTIVE, // IN ULONG Flags,
&SymbolicLinkList // OUT PWSTR *SymbolicLinkList
))) {
UNICODE_STRING SymbolicLinkListU;
HANDLE DeviceInterfaceKey;
RtlInitUnicodeString(&SymbolicLinkListU, SymbolicLinkList);
DebugPrint((DebugLevelVerbose,
"NoKSPin for SymbolicLink %S\n",
SymbolicLinkList ));
if (NT_SUCCESS(Status = IoOpenDeviceInterfaceRegistryKey(
&SymbolicLinkListU, // IN PUNICODE_STRING SymbolicLinkName,
STANDARD_RIGHTS_ALL, // IN ACCESS_MASK DesiredAccess,
&DeviceInterfaceKey // OUT PHANDLE DeviceInterfaceKey
))) {
UNICODE_STRING FilterDataString;
RtlInitUnicodeString(&FilterDataString, L"FilterData");
Status = ZwSetValueKey(DeviceInterfaceKey,
&FilterDataString,
0,
REG_BINARY,
FilterData,
FilterDataLength);
ZwClose(DeviceInterfaceKey);
}
// START NEW MEDIUM CACHING CODE
for (CurrentPin = 0; CurrentPin < PinCount; CurrentPin++) {
NTSTATUS LocalStatus;
LocalStatus = KsCacheMedium(&SymbolicLinkListU,
&MediumList[CurrentPin],
(DWORD) ((PinDirection[CurrentPin] ? 1 : 0)) // 1 == output
);
#if DBG
if (LocalStatus != STATUS_SUCCESS) {
DebugPrint((DebugLevelError,
"KsCacheMedium: SymbolicLink = %S, Status = %x\n",
SymbolicLinkListU.Buffer, LocalStatus));
}
#endif
}
// END NEW MEDIUM CACHING CODE
ExFreePool(SymbolicLinkList);
}
ExFreePool(RegFilter);
return Status;
}
BOOLEAN
StreamClassReadWriteConfig(
IN PVOID HwDeviceExtension,
IN BOOLEAN Read,
IN PVOID Buffer,
IN ULONG Offset,
IN ULONG Length
)
/*++
Routine Description:
Sends down a config space read/write. MUST BE CALLED AT PASSIVE LEVEL!
Arguments:
HwDeviceExtension - device extension
Read - TRUE if read, FALSE if write.
Buffer - The info to read or write.
Offset - The offset in config space to read or write.
Length - The length to transfer.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION nextStack;
PIRP irp;
NTSTATUS ntStatus;
KEVENT event;
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) HwDeviceExtension - 1;
PDEVICE_OBJECT DeviceObject = DeviceExtension->DeviceObject;
PAGED_CODE();
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
if (Read) {
memset(Buffer, '\0', Length);
}
irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if (!irp) {
DebugPrint((DebugLevelError, "StreamClassRWConfig: no IRP.\n"));
TRAP;
return (FALSE);
}
//
// new rule says all PnP Irp must be initialized to this
//
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoSetCompletionRoutine(irp,
SCSynchCompletionRoutine,
&event,
TRUE,
TRUE,
TRUE);
nextStack = IoGetNextIrpStackLocation(irp);
ASSERT(nextStack != NULL);
nextStack->MajorFunction = IRP_MJ_PNP;
nextStack->MinorFunction = Read ? IRP_MN_READ_CONFIG : IRP_MN_WRITE_CONFIG;
nextStack->Parameters.ReadWriteConfig.WhichSpace = 0;
nextStack->Parameters.ReadWriteConfig.Buffer = Buffer;
nextStack->Parameters.ReadWriteConfig.Offset = Offset;
nextStack->Parameters.ReadWriteConfig.Length = Length;
ASSERT( DeviceExtension->HwDeviceExtension == HwDeviceExtension );
ntStatus = IoCallDriver(DeviceExtension->PhysicalDeviceObject,
irp);
if (ntStatus == STATUS_PENDING) {
// wait for irp to complete
TRAP;
KeWaitForSingleObject(
&event,
Suspended,
KernelMode,
FALSE,
NULL);
}
if (!NT_SUCCESS(ntStatus)) {
DebugPrint((DebugLevelError, "StreamClassRWConfig: bad status!.\n"));
TRAP;
}
IoFreeIrp(irp);
return (TRUE);
}
VOID
StreamClassQueryMasterClockSync(
IN HANDLE MasterClockHandle,
IN OUT PHW_TIME_CONTEXT TimeContext
)
/*++
Routine Description:
synchronously returns the current time requested, based on the TimeContext
parameter.
Arguments:
Return Value:
none
--*/
{
PHW_STREAM_OBJECT HwStreamObject = TimeContext->HwStreamObject;
PSTREAM_OBJECT StreamObject = CONTAINING_RECORD(HwStreamObject,
STREAM_OBJECT,
HwStreamObject);
LARGE_INTEGER ticks;
ULONGLONG rate;
KIRQL SavedIrql;
ASSERT(MasterClockHandle);
ASSERT(TimeContext->HwDeviceExtension);
ASSERT(HwStreamObject);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Lock the use of MasterClock, so it won't dispear under us
//
KeAcquireSpinLock( &StreamObject->LockUseMasterClock, &SavedIrql );
if ( NULL == StreamObject->MasterClockInfo ) {
//
// If we are called when MasterClockInfo is NULL,
// the mini driver has screwed up. We don't want to fault.
//
ASSERT(0 && "Mini driver queries clock while there is no master clock" );
//
// give a hint that something is wrong via Time, since we return void.
//
TimeContext->Time = (ULONGLONG)-1;
goto Exit;
}
//
// process the requested time function
//
switch (TimeContext->Function) {
case TIME_GET_STREAM_TIME:
TimeContext->Time = StreamObject->MasterClockInfo->
FunctionTable.GetCorrelatedTime(
StreamObject->MasterClockInfo->ClockFileObject,
&TimeContext->SystemTime);
break;
case TIME_READ_ONBOARD_CLOCK:
TRAP;
TimeContext->Time = StreamObject->MasterClockInfo->
FunctionTable.GetTime(
StreamObject->MasterClockInfo->ClockFileObject);
//
// timestamp the value as close as possible
//
ticks = KeQueryPerformanceCounter((PLARGE_INTEGER) & rate);
TimeContext->SystemTime = KSCONVERT_PERFORMANCE_TIME( rate, ticks );
break;
default:
DebugPrint((DebugLevelFatal, "SCQueryClockSync: unknown type!"));
TRAP;
}
Exit:
KeReleaseSpinLock( &StreamObject->LockUseMasterClock, SavedIrql );
return;
}
VOID
StreamClassCompleteRequestAndMarkQueueReady(
IN PHW_STREAM_REQUEST_BLOCK Srb
)
/*++
Routine Description:
completes a stream request and marks the appropriate queue as ready for next
Arguments:
Return Value:
none
--*/
{
PDEVICE_EXTENSION DeviceExtension =
(PDEVICE_EXTENSION) Srb->HwDeviceExtension - 1;
ASSERT(!(DeviceExtension->NoSync));
ASSERT(Srb->Status != STATUS_PENDING);
DebugPrint((DebugLevelTrace, "'StreamClassComplete&Mark:SRB = %p\n",
Srb));
switch (Srb->Flags & (SRB_HW_FLAGS_DATA_TRANSFER |
SRB_HW_FLAGS_STREAM_REQUEST)) {
case SRB_HW_FLAGS_STREAM_REQUEST | SRB_HW_FLAGS_DATA_TRANSFER:
StreamClassStreamNotification(StreamRequestComplete,
Srb->StreamObject,
Srb);
StreamClassStreamNotification(ReadyForNextStreamDataRequest,
Srb->StreamObject);
break;
case SRB_HW_FLAGS_STREAM_REQUEST:
StreamClassStreamNotification(StreamRequestComplete,
Srb->StreamObject,
Srb);
StreamClassStreamNotification(ReadyForNextStreamControlRequest,
Srb->StreamObject);
break;
default:
StreamClassDeviceNotification(DeviceRequestComplete,
Srb->HwDeviceExtension,
Srb);
StreamClassDeviceNotification(ReadyForNextDeviceRequest,
Srb->HwDeviceExtension);
break;
} // switch
}
#if ENABLE_MULTIPLE_FILTER_TYPES
VOID STREAMAPI
StreamClassFilterNotification(
IN STREAM_MINIDRIVER_DEVICE_NOTIFICATION_TYPE NotificationType,
IN PVOID HwInstanceExtension,
...
);
VOID STREAMAPI
StreamClassFilterScheduleTimer(
IN PVOID HwInstanceExtension,
IN ULONG NumberOfMicroseconds,
IN PHW_TIMER_ROUTINE TimerRoutine,
IN PVOID Context
);
PKSEVENT_ENTRY
StreamClassDeviceInstanceGetNextEvent(
IN PVOID HwInstanceExtension,
IN OPTIONAL GUID * EventGuid,
IN OPTIONAL ULONG EventItem,
IN OPTIONAL PKSEVENT_ENTRY CurrentEvent
)
/*++
Routine Description:
Arguments:
CurrentEvent - event (if any) to get the next from
Return Value:
next event, if any
--*/
{
PFILTER_INSTANCE FilterInstance= (PFILTER_INSTANCE)
HwInstanceExtension - 1;
PDEVICE_EXTENSION DeviceExtension =
FilterInstance->DeviceObject->DeviceExtension;
PLIST_ENTRY EventListEntry, EventEntry;
PKSEVENT_ENTRY NextEvent, ReturnEvent = NULL;
KIRQL Irql;
#if DBG
if (DeviceExtension->NoSync) {
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
} // if nosync
#endif
//
// take the spinlock if we are unsynchronized.
//
BEGIN_MINIDRIVER_DEVICE_CALLIN(DeviceExtension, &Irql);
//
// loop thru the events, trying to find the requested one.
//
EventListEntry = EventEntry = &FilterInstance->NotifyList;
while (EventEntry->Flink != EventListEntry) {
EventEntry = EventEntry->Flink;
NextEvent = CONTAINING_RECORD(EventEntry,
KSEVENT_ENTRY,
ListEntry);
if ((EventItem == NextEvent->EventItem->EventId) &&
(!EventGuid || IsEqualGUIDAligned(EventGuid, NextEvent->EventSet->Set))) {
//
// if we are to return the 1st event which matches, break.
//
if (!CurrentEvent) {
ReturnEvent = NextEvent;
break;
} // if !current
//
// if we are to return the next event after the specified one,
// check
// to see if these match. If they do, zero the specified event
// so
// that we will return the next event of the specified type.
//
if (CurrentEvent == NextEvent) {
CurrentEvent = NULL;
} // if cur=next
} // if guid & id match
} // while events
//
// if we are unsynchronized, release the spinlock acquired in the macro
// above.
//
ASSERT(--DeviceExtension->LowerApiThreads == 0); // typo barfs. but this is truely ok.
if (DeviceExtension->NoSync) {
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
}
//
// return the next event, if any.
//
return (ReturnEvent);
}
#endif // ENABLE_MULTIPLE_FILTER_TYPES