[PORTCLS] Fix races, fix WavePci Port Drivers

- Split buffers on page to prevent non-contiguous memory being passed to driver.
- Protect CIrpQueue::GetMappingWithTag, ReleaseMappingWithTag with spinlock to prevent race conditions
  (GetMapping, ReleaseMapping do not need spinlock, they are only called from a service routine).
- Remove ASSERT in CIrpQueue::ReleaseMappingWithTag, when mappings are released out of order. Just ignore
  the tag argument and release the next one in the list. This is what windows does, confirmed by calling
  PortWavePciStream::ReleaseMapping() with tag argument set to 0, absolutly no difference observed.
  Allowing out of order release is essential given that a driver is not permitted to hold a spinlock when calling
  ReleaseMapping().
- Remove IIrpQueue::HasLastMappingFailed(), it never worked and there is no way it could work.
  CPortPinWavePci::HandleKsStream() call MappingAvailable() non-conditionally, this is what Windows does,
  verified by debug prints in ac97 driver.
- Implement CIrpQueue::NumData().
- Remove incorrect interlocked operations/volatile variables and several (now unused) class fields.
This commit is contained in:
Michael Stamper 2022-01-05 00:20:44 +00:00 committed by GitHub
parent 7ed1883c8e
commit 378294a7df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 261 additions and 188 deletions

View file

@ -350,7 +350,6 @@ DECLARE_INTERFACE_(IIrpQueue, IUnknown)
STDMETHOD_(NTSTATUS, ReleaseMappingWithTag)(THIS_ STDMETHOD_(NTSTATUS, ReleaseMappingWithTag)(THIS_
IN PVOID Tag) PURE; IN PVOID Tag) PURE;
STDMETHOD_(BOOLEAN, HasLastMappingFailed)(THIS) PURE;
STDMETHOD_(ULONG, GetCurrentIrpOffset)(THIS) PURE; STDMETHOD_(ULONG, GetCurrentIrpOffset)(THIS) PURE;
STDMETHOD_(BOOLEAN, GetAcquiredTagRange)(THIS_ STDMETHOD_(BOOLEAN, GetAcquiredTagRange)(THIS_

View file

@ -14,6 +14,31 @@
#include <debug.h> #include <debug.h>
static
PIRP
RemoveHeadList_IRP(
IN OUT PLIST_ENTRY QueueHead)
{
PIRP Irp;
PLIST_ENTRY CurEntry;
for (CurEntry = QueueHead->Flink; CurEntry != QueueHead; CurEntry = CurEntry->Flink)
{
/* Get the IRP offset */
Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
/* Remove the cancel routine */
if (IoSetCancelRoutine(Irp, NULL))
{
/* Remove the IRP from the list and return it */
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
return Irp;
}
}
/* no non canceled irp has been found */
return NULL;
}
class CIrpQueue : public IIrpQueue class CIrpQueue : public IIrpQueue
{ {
public: public:
@ -48,13 +73,16 @@ protected:
LIST_ENTRY m_IrpList; LIST_ENTRY m_IrpList;
LIST_ENTRY m_FreeIrpList; LIST_ENTRY m_FreeIrpList;
BOOLEAN m_OutOfMapping;
ULONG m_MaxFrameSize; ULONG m_MaxFrameSize;
ULONG m_Alignment; ULONG m_Alignment;
ULONG m_TagSupportEnabled; ULONG m_TagSupportEnabled;
volatile ULONG m_NumDataAvailable;
volatile ULONG m_CurrentOffset; ULONG m_StreamHeaderIndex;
volatile PIRP m_Irp; ULONG m_TagIndex;
PKSSTREAM_HEADER m_CurStreamHeader;
ULONG m_CurrentOffset;
PIRP m_Irp;
volatile LONG m_Ref; volatile LONG m_Ref;
}; };
@ -67,10 +95,8 @@ typedef struct
typedef struct typedef struct
{ {
ULONG StreamHeaderCount; ULONG StreamHeaderCount;
ULONG StreamHeaderIndex; ULONG nTags;
ULONG TotalStreamData;
PKSSTREAM_HEADER CurStreamHeader;
PVOID * Data; PVOID * Data;
PKSSTREAM_TAG Tags; PKSSTREAM_TAG Tags;
}KSSTREAM_DATA, *PKSSTREAM_DATA; }KSSTREAM_DATA, *PKSSTREAM_DATA;
@ -128,6 +154,9 @@ CIrpQueue::AddMapping(
ULONG Index, Length; ULONG Index, Length;
PMDL Mdl; PMDL Mdl;
PKSSTREAM_DATA StreamData; PKSSTREAM_DATA StreamData;
LONG TotalStreamData;
LONG StreamPageCount;
LONG HeaderLength;
PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
@ -165,14 +194,15 @@ CIrpQueue::AddMapping(
// get first stream header // get first stream header
Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer; Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
// store header
StreamData->CurStreamHeader = Header;
// sanity check // sanity check
PC_ASSERT(Header); PC_ASSERT(Header);
// first calculate the numbers of stream headers // first calculate the numbers of stream headers
Length = IoStack->Parameters.DeviceIoControl.OutputBufferLength; Length = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
Mdl = Irp->MdlAddress;
TotalStreamData = 0;
StreamPageCount = 0;
do do
{ {
@ -185,15 +215,23 @@ CIrpQueue::AddMapping(
if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN) if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN)
{ {
// irp sink // irp sink
StreamData->TotalStreamData += Header->DataUsed; HeaderLength = Header->DataUsed;
} }
else else
{ {
// irp source // irp source
StreamData->TotalStreamData += Header->FrameExtent; HeaderLength = Header->FrameExtent;
} }
/* move to next header */ // increment available data
TotalStreamData += HeaderLength;
// append page count
StreamPageCount += ADDRESS_AND_SIZE_TO_SPAN_PAGES(
MmGetMdlByteOffset(Mdl), HeaderLength);
// move to next header / mdl
Mdl = Mdl->Next;
Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size); Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
}while(Length); }while(Length);
@ -215,7 +253,7 @@ CIrpQueue::AddMapping(
if (m_TagSupportEnabled) if (m_TagSupportEnabled)
{ {
// allocate array for storing the pointers of the data */ // allocate array for storing the pointers of the data */
StreamData->Tags = (PKSSTREAM_TAG)AllocateItem(NonPagedPool, sizeof(KSSTREAM_TAG) * StreamData->StreamHeaderCount, TAG_PORTCLASS); StreamData->Tags = (PKSSTREAM_TAG)AllocateItem(NonPagedPool, sizeof(KSSTREAM_TAG) * StreamPageCount, TAG_PORTCLASS);
if (!StreamData->Data) if (!StreamData->Data)
{ {
// out of memory // out of memory
@ -254,17 +292,6 @@ CIrpQueue::AddMapping(
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
} }
if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN)
{
// increment available data
InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, Header->DataUsed);
}
else if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
{
// increment available data
InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, Header->FrameExtent);
}
// move to next header / mdl // move to next header / mdl
Mdl = Mdl->Next; Mdl = Mdl->Next;
Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size); Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
@ -274,7 +301,7 @@ CIrpQueue::AddMapping(
// store stream data // store stream data
Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET] = (PVOID)StreamData; Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET] = (PVOID)StreamData;
*Data = StreamData->TotalStreamData; *Data = TotalStreamData;
// mark irp as pending // mark irp as pending
IoMarkIrpPending(Irp); IoMarkIrpPending(Irp);
@ -282,9 +309,6 @@ CIrpQueue::AddMapping(
// add irp to cancelable queue // add irp to cancelable queue
KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL); KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
// disable mapping failed status
m_OutOfMapping = FALSE;
// done // done
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -322,6 +346,15 @@ CIrpQueue::GetMapping(
// get a fresh new irp from the queue // get a fresh new irp from the queue
m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem); m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
m_CurrentOffset = Offset = 0; m_CurrentOffset = Offset = 0;
if (m_Irp)
{
// reset stream header index
m_StreamHeaderIndex = 0;
// reset stream header
m_CurStreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
}
} }
if (!Irp) if (!Irp)
@ -340,22 +373,19 @@ CIrpQueue::GetMapping(
if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN) if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN)
{ {
// sink pin // sink pin
*BufferSize = StreamData->CurStreamHeader->DataUsed - Offset; *BufferSize = m_CurStreamHeader->DataUsed - Offset;
} }
else else
{ {
// source pin // source pin
*BufferSize = StreamData->CurStreamHeader->FrameExtent - Offset; *BufferSize = m_CurStreamHeader->FrameExtent - Offset;
} }
// sanity check // sanity check
PC_ASSERT(*BufferSize); PC_ASSERT(*BufferSize);
// store buffer // store buffer
*Buffer = &((PUCHAR)StreamData->Data[StreamData->StreamHeaderIndex])[Offset]; *Buffer = &((PUCHAR)StreamData->Data[m_StreamHeaderIndex])[Offset];
// unset flag that no irps are available
m_OutOfMapping = FALSE;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -381,22 +411,19 @@ CIrpQueue::UpdateMapping(
ASSERT(StreamData); ASSERT(StreamData);
// add to current offset // add to current offset
InterlockedExchangeAdd((PLONG)&m_CurrentOffset, (LONG)BytesWritten); m_CurrentOffset += BytesWritten;
if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT) if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
{ {
// store written bytes (source pin) // store written bytes (source pin)
StreamData->CurStreamHeader->DataUsed += BytesWritten; m_CurStreamHeader->DataUsed += BytesWritten;
} }
// decrement available data counter
m_NumDataAvailable -= BytesWritten;
// get audio buffer size // get audio buffer size
if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT) if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
Size = StreamData->CurStreamHeader->FrameExtent; Size = m_CurStreamHeader->FrameExtent;
else else
Size = StreamData->CurStreamHeader->DataUsed; Size = m_CurStreamHeader->DataUsed;
// sanity check // sanity check
PC_ASSERT(Size); PC_ASSERT(Size);
@ -406,13 +433,13 @@ CIrpQueue::UpdateMapping(
// sanity check // sanity check
PC_ASSERT(Size == m_CurrentOffset); PC_ASSERT(Size == m_CurrentOffset);
if (StreamData->StreamHeaderIndex + 1 < StreamData->StreamHeaderCount) if (m_StreamHeaderIndex + 1 < StreamData->StreamHeaderCount)
{ {
// move to next stream header // move to next stream header
StreamData->CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamData->CurStreamHeader + StreamData->CurStreamHeader->Size); m_CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)m_CurStreamHeader + m_CurStreamHeader->Size);
// increment stream header index // increment stream header index
StreamData->StreamHeaderIndex++; m_StreamHeaderIndex++;
// reset offset // reset offset
m_CurrentOffset = 0; m_CurrentOffset = 0;
@ -430,15 +457,6 @@ CIrpQueue::UpdateMapping(
// looped streaming repeat the buffers untill // looped streaming repeat the buffers untill
// the caller decides to stop the streams // the caller decides to stop the streams
// reset stream header index
StreamData->StreamHeaderIndex = 0;
// reset stream header
StreamData->CurStreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
// increment available data
InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, StreamData->TotalStreamData);
// re-insert irp // re-insert irp
KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL); KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
@ -495,8 +513,70 @@ ULONG
NTAPI NTAPI
CIrpQueue::NumData() CIrpQueue::NumData()
{ {
// returns the amount of audio stream data available KIRQL OldLevel;
return m_NumDataAvailable; ULONG NumDataAvailable;
PLIST_ENTRY CurEntry;
PIRP Irp;
ULONG CurrentOffset;
ULONG StreamHeaderIndex;
PKSSTREAM_HEADER CurStreamHeader;
PKSSTREAM_DATA StreamData;
ULONG Size;
KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
NumDataAvailable = 0;
CurEntry = &m_IrpList;
// current IRP state
Irp = m_Irp;
CurrentOffset = m_CurrentOffset;
StreamHeaderIndex = m_StreamHeaderIndex;
CurStreamHeader = m_CurStreamHeader;
while (TRUE)
{
if (Irp != NULL)
{
// get stream data
StreamData = (PKSSTREAM_DATA)Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
// loop over stream headers
for (; StreamHeaderIndex < StreamData->StreamHeaderCount; StreamHeaderIndex++)
{
// get audio buffer size
if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
Size = CurStreamHeader->FrameExtent;
else
Size = CurStreamHeader->DataUsed;
// increment available data
NumDataAvailable += Size - CurrentOffset;
CurrentOffset = 0;
// move to next stream header
CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)CurStreamHeader + CurStreamHeader->Size);
}
}
/* iterate to next entry */
CurEntry = CurEntry->Flink;
/* is the end of list reached */
if (CurEntry == &m_IrpList)
break;
/* get irp offset */
Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
// next IRP state
CurrentOffset = 0;
StreamHeaderIndex = 0;
CurStreamHeader = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
}
KeReleaseSpinLock(&m_IrpListLock, OldLevel);
return NumDataAvailable;
} }
BOOL BOOL
@ -517,9 +597,6 @@ CIrpQueue::CancelBuffers()
// cancel all irps // cancel all irps
KsCancelIo(&m_IrpList, &m_IrpListLock); KsCancelIo(&m_IrpList, &m_IrpListLock);
// reset number of data available
m_NumDataAvailable = 0;
// done // done
return TRUE; return TRUE;
} }
@ -534,6 +611,9 @@ CIrpQueue::GetMappingWithTag(
OUT PULONG Flags) OUT PULONG Flags)
{ {
PKSSTREAM_DATA StreamData; PKSSTREAM_DATA StreamData;
KIRQL OldLevel;
ULONG Size;
LPBYTE Data;
/* sanity checks */ /* sanity checks */
PC_ASSERT(PhysicalAddress); PC_ASSERT(PhysicalAddress);
@ -541,76 +621,103 @@ CIrpQueue::GetMappingWithTag(
PC_ASSERT(ByteCount); PC_ASSERT(ByteCount);
PC_ASSERT(Flags); PC_ASSERT(Flags);
KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
if (!m_Irp) if (!m_Irp)
{ {
// get an irp from the queue // get an irp from the queue
m_Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem); m_Irp = RemoveHeadList_IRP(&m_IrpList);
}
// check if there is an irp // check if there is an irp
if (!m_Irp) if (!m_Irp)
{ {
// no irp available // no irp available
m_OutOfMapping = TRUE; KeReleaseSpinLock(&m_IrpListLock, OldLevel);
DPRINT("GetMappingWithTag no mapping available\n");
return STATUS_NOT_FOUND; DPRINT("GetMappingWithTag no mapping available\n");
return STATUS_NOT_FOUND;
}
// reset offset
m_CurrentOffset = 0;
// reset tag index
m_TagIndex = 0;
// reset stream header index
m_StreamHeaderIndex = 0;
// reset stream header
m_CurStreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
} }
// get stream data // get stream data
StreamData = (PKSSTREAM_DATA)m_Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET]; StreamData = (PKSSTREAM_DATA)m_Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
// sanity check // sanity check
PC_ASSERT(StreamData->StreamHeaderIndex < StreamData->StreamHeaderCount); PC_ASSERT(m_StreamHeaderIndex < StreamData->StreamHeaderCount);
// setup mapping
*PhysicalAddress = MmGetPhysicalAddress(StreamData->Data[StreamData->StreamHeaderIndex]);
*VirtualAddress = StreamData->Data[StreamData->StreamHeaderIndex];
// store tag in irp // store tag in irp
StreamData->Tags[StreamData->StreamHeaderIndex].Tag = Tag; StreamData->Tags[m_TagIndex].Tag = Tag;
StreamData->Tags[StreamData->StreamHeaderIndex].Used = TRUE; StreamData->Tags[m_TagIndex].Used = TRUE;
m_TagIndex++;
// increment header index // get audio buffer size
StreamData->StreamHeaderIndex++; if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
Size = m_CurStreamHeader->FrameExtent;
// mapping size
if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN)
{
// sink pin
*ByteCount = StreamData->CurStreamHeader->DataUsed;
// decrement num data available
m_NumDataAvailable -= StreamData->CurStreamHeader->DataUsed;
}
else else
{ Size = m_CurStreamHeader->DataUsed;
// source pin
*ByteCount = StreamData->CurStreamHeader->FrameExtent;
// decrement num data available // sanity check
m_NumDataAvailable -= StreamData->CurStreamHeader->FrameExtent; PC_ASSERT(Size);
// setup mapping
Data = (LPBYTE)StreamData->Data[m_StreamHeaderIndex] + m_CurrentOffset;
*VirtualAddress = Data;
// get byte count
*ByteCount = (LPBYTE)ROUND_TO_PAGES(Data+1)-Data;
if (*ByteCount > (Size - m_CurrentOffset))
*ByteCount = (Size - m_CurrentOffset);
m_CurrentOffset += *ByteCount;
if (m_CurrentOffset >= Size)
{
// sanity check
PC_ASSERT(Size == m_CurrentOffset);
// increment header index
m_StreamHeaderIndex++;
if (m_StreamHeaderIndex == StreamData->StreamHeaderCount)
{
// last mapping
*Flags = 1;
//
StreamData->nTags = m_TagIndex;
// insert mapping into free list
InsertTailList(&m_FreeIrpList, &m_Irp->Tail.Overlay.ListEntry);
// clear irp
m_Irp = NULL;
}
else
{
// one more mapping in the irp
*Flags = 0;
// move to next header
m_CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)m_CurStreamHeader + m_CurStreamHeader->Size);
}
} }
if (StreamData->StreamHeaderIndex == StreamData->StreamHeaderCount) // get physical address
{ *PhysicalAddress = MmGetPhysicalAddress(*VirtualAddress);
// last mapping
*Flags = 1;
// insert mapping into free list KeReleaseSpinLock(&m_IrpListLock, OldLevel);
ExInterlockedInsertTailList(&m_FreeIrpList, &m_Irp->Tail.Overlay.ListEntry, &m_IrpListLock);
// clear irp
m_Irp = NULL;
}
else
{
// one more mapping in the irp
*Flags = 0;
// move to next header
StreamData->CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamData->CurStreamHeader + StreamData->CurStreamHeader->Size);
}
DPRINT("GetMappingWithTag Tag %p Buffer %p Flags %lu ByteCount %lx\n", Tag, VirtualAddress, *Flags, *ByteCount); DPRINT("GetMappingWithTag Tag %p Buffer %p Flags %lu ByteCount %lx\n", Tag, VirtualAddress, *Flags, *ByteCount);
// done // done
@ -627,82 +734,63 @@ CIrpQueue::ReleaseMappingWithTag(
PKSSTREAM_DATA StreamData; PKSSTREAM_DATA StreamData;
PIO_STACK_LOCATION IoStack; PIO_STACK_LOCATION IoStack;
ULONG Index; ULONG Index;
KIRQL OldLevel;
// first check if there is an active irp KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
if (m_Irp)
// check if used list empty
if (IsListEmpty(&m_FreeIrpList))
{ {
// now check if there are already used mappings // get current irp
StreamData = (PKSSTREAM_DATA)m_Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET]; if (!m_Irp)
if (StreamData->StreamHeaderIndex)
{ {
// check if the released mapping is one current processed irps KeReleaseSpinLock(&m_IrpListLock, OldLevel);
for(Index = 0; Index < StreamData->StreamHeaderIndex; Index++)
{
// check if it is the same tag
if ((StreamData->Tags[Index].Tag == Tag) &&
(StreamData->Tags[Index].Used != FALSE))
{
// mark mapping as released
StreamData->Tags[Index].Tag = NULL;
StreamData->Tags[Index].Used = FALSE;
// done // this should not happen
return STATUS_SUCCESS; DPRINT("ReleaseMappingWithTag Tag %p not found\n", Tag);
} return STATUS_NOT_FOUND;
}
} }
}
// remove irp from used list Irp = m_Irp;
CurEntry = ExInterlockedRemoveHeadList(&m_FreeIrpList, &m_IrpListLock); }
if (CurEntry == NULL) else
{ {
// this should not happen // remove irp from used list
DPRINT("ReleaseMappingWithTag Tag %p not found\n", Tag); CurEntry = RemoveHeadList(&m_FreeIrpList);
return STATUS_NOT_FOUND;
// get irp from list entry
Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
} }
// sanity check
PC_ASSERT(CurEntry);
// get irp from list entry
Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
// get stream data // get stream data
StreamData = (PKSSTREAM_DATA)Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET]; StreamData = (PKSSTREAM_DATA)Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
// sanity check // release oldest in use mapping
PC_ASSERT(StreamData->StreamHeaderIndex == StreamData->StreamHeaderCount); for (Index = 0; Index < StreamData->nTags; Index++)
// check if the released mapping is one of these
for(Index = 0; Index < StreamData->StreamHeaderCount; Index++)
{ {
if ((StreamData->Tags[Index].Tag == Tag) && if (StreamData->Tags[Index].Used != FALSE)
(StreamData->Tags[Index].Used != FALSE))
{ {
// mark mapping as released
StreamData->Tags[Index].Tag = NULL;
StreamData->Tags[Index].Used = FALSE; StreamData->Tags[Index].Used = FALSE;
// done // Warn if wrong mapping released
if (StreamData->Tags[Index].Tag != Tag)
{
DPRINT1("Mapping released out of order\n");
}
break; break;
} }
else }
{
// // If this is the current IRP, do not complete
// we assume that mappings are released in the same order as they have been acquired if (Irp == m_Irp)
// therefore if the current mapping is not the searched one, it must have been already {
// released KeReleaseSpinLock(&m_IrpListLock, OldLevel);
// return STATUS_SUCCESS;
ASSERT(StreamData->Tags[Index].Tag == NULL);
ASSERT(StreamData->Tags[Index].Used == FALSE);
}
} }
// check if this is the last one released mapping // check if this is the last one released mapping
if (Index + 1 == StreamData->StreamHeaderCount) if (Index + 1 == StreamData->nTags)
{ {
// last mapping released // last mapping released
// now check if this is a looped buffer // now check if this is a looped buffer
@ -711,14 +799,7 @@ CIrpQueue::ReleaseMappingWithTag(
// looped buffers are not completed when they have been played // looped buffers are not completed when they have been played
// they are completed when the stream is set to stop // they are completed when the stream is set to stop
// reset stream header index KeReleaseSpinLock(&m_IrpListLock, OldLevel);
StreamData->StreamHeaderIndex = 0;
// reset stream header
StreamData->CurStreamHeader = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
// increment available data
InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, StreamData->TotalStreamData);
// re-insert irp // re-insert irp
KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL); KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
@ -731,6 +812,8 @@ CIrpQueue::ReleaseMappingWithTag(
// time to complete non looped buffer // time to complete non looped buffer
// //
KeReleaseSpinLock(&m_IrpListLock, OldLevel);
// free stream data array // free stream data array
FreeItem(StreamData->Data, TAG_PORTCLASS); FreeItem(StreamData->Data, TAG_PORTCLASS);
@ -755,19 +838,14 @@ CIrpQueue::ReleaseMappingWithTag(
else else
{ {
// there are still some headers not consumed // there are still some headers not consumed
ExInterlockedInsertHeadList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry, &m_IrpListLock); InsertHeadList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLock(&m_IrpListLock, OldLevel);
} }
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
BOOLEAN
NTAPI
CIrpQueue::HasLastMappingFailed()
{
return m_OutOfMapping;
}
ULONG ULONG
NTAPI NTAPI
CIrpQueue::GetCurrentIrpOffset() CIrpQueue::GetCurrentIrpOffset()
@ -817,4 +895,3 @@ NewIrpQueue(
*Queue = (IIrpQueue*)This; *Queue = (IIrpQueue*)This;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }

View file

@ -549,13 +549,10 @@ CPortPinWavePci::HandleKsStream(
{ {
NTSTATUS Status; NTSTATUS Status;
ULONG Data = 0; ULONG Data = 0;
BOOLEAN bFailed;
InterlockedIncrement((PLONG)&m_TotalPackets); InterlockedIncrement((PLONG)&m_TotalPackets);
DPRINT("IPortPinWaveCyclic_HandleKsStream entered Total %u State %x MinData %u\n", m_TotalPackets, m_State, m_IrpQueue->NumData()); DPRINT("IPortPinWaveCyclic_HandleKsStream entered Total %u State %x MinData %u\n", m_TotalPackets, m_State, m_IrpQueue->NumData());
bFailed = m_IrpQueue->HasLastMappingFailed();
Status = m_IrpQueue->AddMapping(Irp, &Data); Status = m_IrpQueue->AddMapping(Irp, &Data);
if (NT_SUCCESS(Status)) if (NT_SUCCESS(Status))
@ -565,7 +562,7 @@ CPortPinWavePci::HandleKsStream(
else else
m_Position.PlayOffset += Data; m_Position.PlayOffset += Data;
if (bFailed) if (m_State == KSSTATE_RUN)
{ {
// notify stream of new mapping // notify stream of new mapping
m_Stream->MappingAvailable(); m_Stream->MappingAvailable();