windows-nt/Source/XPSP1/NT/drivers/wdm/capture/mini/1394dcam/callback.c

714 lines
22 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//===========================================================================
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved.
//
//===========================================================================
/*++
Module Name:
dcampkt.c
Abstract:
This file contains code to handle callback from the bus/class driver.
They might be running in DISPATCH level.
Author:
Yee J. Wu 15-Oct-97
Environment:
Kernel mode only
Revision History:
--*/
#include "strmini.h"
#include "ksmedia.h"
#include "1394.h"
#include "wdm.h" // for DbgBreakPoint() defined in dbg.h
#include "dbg.h"
#include "dcamdef.h"
#include "dcampkt.h"
#include "sonydcam.h"
#include "capprop.h"
NTSTATUS
DCamToInitializeStateCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PDCAM_IO_CONTEXT pDCamIoContext
)
/*++
Routine Description:
Completion routine called after the device is initialize to a known state.
Arguments:
DriverObject - Pointer to driver object created by system.
pIrp - Irp that just completed
pDCamIoContext - A structure that contain the context of this IO completion routine.
Return Value:
None.
--*/
{
PDCAM_EXTENSION pDevExt;
PSTREAMEX pStrmEx;
PIRB pIrb;
if(!pDCamIoContext) {
return STATUS_MORE_PROCESSING_REQUIRED;
}
pIrb = pDCamIoContext->pIrb;
pDevExt = pDCamIoContext->pDevExt;
DbgMsg2(("\'DCamToInitializeStateCompletionRoutine: completed DeviceState=%d; pIrp->IoStatus.Status=%x\n",
pDCamIoContext->DeviceState, pIrp->IoStatus.Status));
// Free MDL
if(pIrb->FunctionNumber == REQUEST_ASYNC_WRITE) {
DbgMsg3(("DCamToInitializeStateCompletionRoutine: IoFreeMdl\n"));
IoFreeMdl(pIrb->u.AsyncWrite.Mdl);
}
// CAUTION:
// Do we need to retry if the return is STATUS_TIMEOUT or invalid generation number ?
//
if(pIrp->IoStatus.Status != STATUS_SUCCESS) {
ERROR_LOG(("DCamToInitializeStateCompletionRoutine: Status=%x != STATUS_SUCCESS; cannot restart its state.\n", pIrp->IoStatus.Status));
if(pDCamIoContext->pSrb) {
pDCamIoContext->pSrb->Status = STATUS_UNSUCCESSFUL;
StreamClassStreamNotification(StreamRequestComplete, pDCamIoContext->pSrb->StreamObject, pDCamIoContext->pSrb);
}
DCamFreeIrbIrpAndContext(pDCamIoContext, pDCamIoContext->pIrb, pIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Done here if we are in STOP or PAUSE state;
// else setting to RUN state.
//
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
//
// No stream is open, job is done.
//
if(!pStrmEx) {
if(pDCamIoContext->pSrb) {
pDCamIoContext->pSrb->Status = STATUS_SUCCESS;
StreamClassStreamNotification(StreamRequestComplete, pDCamIoContext->pSrb->StreamObject, pDCamIoContext->pSrb);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
switch(pStrmEx->KSStateFinal) {
case KSSTATE_STOP:
case KSSTATE_PAUSE:
pStrmEx->KSState = pStrmEx->KSStateFinal;
if(pDCamIoContext->pSrb) {
pDCamIoContext->pSrb->Status = STATUS_SUCCESS;
StreamClassStreamNotification(StreamRequestComplete, pDCamIoContext->pSrb->StreamObject, pDCamIoContext->pSrb);
}
DCamFreeIrbIrpAndContext(pDCamIoContext, pDCamIoContext->pIrb, pIrp);
break;
case KSSTATE_RUN:
if(pDCamIoContext->pSrb) {
pDCamIoContext->pSrb->Status = STATUS_SUCCESS;
StreamClassStreamNotification(StreamRequestComplete, pDCamIoContext->pSrb->StreamObject, pDCamIoContext->pSrb);
}
// Restart the stream.
DCamSetKSStateRUN(pDevExt, pDCamIoContext->pSrb);
// Need pDCamIoContext->pSrb; so free it after DCamSetKSStateRUN().
DCamFreeIrbIrpAndContext(pDCamIoContext, pDCamIoContext->pIrb, pIrp);
break;
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
DCamSetKSStateInitialize(
PDCAM_EXTENSION pDevExt
)
/*++
Routine Description:
Set KSSTATE to KSSTATE_RUN.
Arguments:
pDevExt -
Return Value:
Nothing
--*/
{
PSTREAMEX pStrmEx;
PIRB pIrb;
PIRP pIrp;
PDCAM_IO_CONTEXT pDCamIoContext;
PIO_STACK_LOCATION NextIrpStack;
NTSTATUS Status;
ASSERT(pDevExt);
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
if(!DCamAllocateIrbIrpAndContext(&pDCamIoContext, &pIrb, &pIrp, pDevExt->BusDeviceObject->StackSize)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the device to a known state
// may need to do this due to power down??
//
pDCamIoContext->DeviceState = DCAM_SET_INITIALIZE; // Keep track of device state that we just set.
pDCamIoContext->pDevExt = pDevExt;
pDCamIoContext->RegisterWorkArea.AsULONG = 0;
pDCamIoContext->RegisterWorkArea.Initialize.Initialize = TRUE;
pDCamIoContext->RegisterWorkArea.AsULONG = bswap(pDevExt->RegisterWorkArea.AsULONG);
pIrb->FunctionNumber = REQUEST_ASYNC_WRITE;
pIrb->Flags = 0;
pIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_High = INITIAL_REGISTER_SPACE_HI;
pIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_Low =
pDevExt->BaseRegister + FIELDOFFSET(CAMERA_REGISTER_MAP, Initialize);
pIrb->u.AsyncWrite.nNumberOfBytesToWrite = sizeof(ULONG);
pIrb->u.AsyncWrite.nBlockSize = 0;
pIrb->u.AsyncWrite.fulFlags = 0;
InterlockedExchange(&pIrb->u.AsyncWrite.ulGeneration, pDevExt->CurrentGeneration);
pIrb->u.AsyncWrite.Mdl =
IoAllocateMdl(&pDCamIoContext->RegisterWorkArea, sizeof(ULONG), FALSE, FALSE, NULL);
MmBuildMdlForNonPagedPool(pIrb->u.AsyncWrite.Mdl);
NextIrpStack = IoGetNextIrpStackLocation(pIrp);
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_1394_CLASS;
NextIrpStack->Parameters.Others.Argument1 = pIrb;
IoSetCompletionRoutine(
pIrp,
DCamToInitializeStateCompletionRoutine,
pDCamIoContext,
TRUE,
TRUE,
TRUE
);
Status =
IoCallDriver(
pDevExt->BusDeviceObject,
pIrp
);
return STATUS_SUCCESS;
}
VOID
DCamBusResetNotification(
IN PVOID Context
)
/*++
Routine Description:
We receive this callback notification after a bus reset and if the device is still attached.
This can happen when a new device is plugged in or an existing one is removed, or due to
awaken from sleep state. We will restore the device to its original streaming state by
(1) Initialize the device to a known state and then
(2) launch a state machine to restart streaming.
We will stop the state machine if previous state has failed. This can happen if the generation
count is changed before the state mahcine is completed.
The freeing and realocation of isoch bandwidth and channel are done in the bus reset irp.
It is passed down by stream class in SRB_UNKNOWN_DEVICE_COMMAND. This IRP is guarantee to
call after this bus reset notification has returned and while the state machine is going on.
This is a callback at IRQL_DPC level; there are many 1394 APIs cannot be called at this level
if it does blocking using KeWaitFor*Object(). Consult 1394 docuement for the list.
Arguments:
Context - Pointer to the context of this registered notification.
Return Value:
Nothing
--*/
{
PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Context;
PSTREAMEX pStrmEx;
NTSTATUS Status;
PIRP pIrp;
PIRB pIrb;
if(!pDevExt) {
ERROR_LOG(("DCamBusResetNotification:pDevExt is 0.\n\n"));
ASSERT(pDevExt);
return;
}
//
// Check a field in the context that must be valid to make sure that it is Ok to continue.
//
if(!pDevExt->BusDeviceObject) {
ERROR_LOG(("DCamBusResetNotification:pDevExtBusDeviceObject is 0.\n\n"));
ASSERT(pDevExt->BusDeviceObject);
return;
}
DbgMsg2(("DCamBusResetNotification: pDevExt %x, pDevExt->pStrmEx %x, pDevExt->BusDeviceObject %x\n",
pDevExt, pDevExt->pStrmEx, pDevExt->BusDeviceObject));
//
//
// Get the current generation count first
//
// CAUTION:
// not all 1394 APIs can be called in DCamSubmitIrpSynch() if in DISPATCH_LEVEL;
// Getting generation count require no blocking so it is OK.
if(!DCamAllocateIrbAndIrp(&pIrb, &pIrp, pDevExt->BusDeviceObject->StackSize)) {
ERROR_LOG(("DCamBusResetNotification: DcamAllocateIrbAndIrp has failed!!\n\n\n"));
ASSERT(FALSE);
return;
}
pIrb->FunctionNumber = REQUEST_GET_GENERATION_COUNT;
pIrb->Flags = 0;
Status = DCamSubmitIrpSynch(pDevExt, pIrp, pIrb);
if(Status) {
ERROR_LOG(("\'DCamBusResetNotification: Status=%x while trying to get generation number\n", Status));
// Done with them; free resources.
DCamFreeIrbIrpAndContext(0, pIrb, pIrp);
return;
}
ERROR_LOG(("DCamBusResetNotification: Generation number from %d to %d\n",
pDevExt->CurrentGeneration, pIrb->u.GetGenerationCount.GenerationCount));
InterlockedExchange(&pDevExt->CurrentGeneration, pIrb->u.GetGenerationCount.GenerationCount);
// Done with them; free resources.
DCamFreeIrbIrpAndContext(0, pIrb, pIrp);
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
DbgMsg2(("\'%d:%s) DCamBusResetNotification: !!! pDevExt, %x; pStrmEx, %x !!!\n",
pDevExt->idxDev, pDevExt->pchVendorName, pDevExt, pStrmEx));
//
// If the stream was open (pStrmEx != NULL && pStrmEx->pVideoInfoHeader != NULL),
// then we need to restore its streaming state.
//
if (pStrmEx &&
pStrmEx->pVideoInfoHeader != NULL) {
DbgMsg2(("\'%d:%s) DCamBusResetNotification: Stream was open; Try allocate them again.\n", pDevExt->idxDev, pDevExt->pchVendorName));
} else {
DbgMsg2(("DCamBusResetNotification:Stream has not open on this device. Done!\n"));
return;
}
//
// Save the original state as the final state.
//
if(pStrmEx)
pStrmEx->KSStateFinal = pStrmEx->KSState;
//
// Initialize the device, and restore to its original streaming state.
//
//
// CAUTION:
// maybe need to do this only if we are recovered from power loss state.
// We can move this to power management function in the future.
// In the completion routine, it will invoke other function to restore its streaming state.
//
DCamSetKSStateInitialize(pDevExt);
DbgMsg2(("\'DCamBusResetNotification: Leaving...; Task complete in the CompletionRoutine.\n"));
return;
}
NTSTATUS
DCamDetachBufferCR(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PIRB pIrb
)
/*++
Routine Description:
Detaching a buffer has completed. Attach next buffer.
Returns more processing required so the IO Manager will leave us alone
Arguments:
DriverObject - Pointer to driver object created by system.
pIrp - Irp that just completed
pIrb - Context set in DCamIsochCallback()
Return Value:
None.
--*/
{
IN PISOCH_DESCRIPTOR IsochDescriptor;
PDCAM_EXTENSION pDevExt;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
KIRQL oldIrql;
if(!pIrb) {
ERROR_LOG(("\'DCamDetachBufferCR: pIrb is NULL\n"));
ASSERT(pIrb);
IoFreeIrp(pIrp);
return (STATUS_MORE_PROCESSING_REQUIRED);
}
// Get IsochDescriptor from the context (pIrb)
IsochDescriptor = pIrb->u.IsochDetachBuffers.pIsochDescriptor;
if(!IsochDescriptor) {
ERROR_LOG(("\'DCamDetachBufferCR: IsochDescriptor is NULL\n"));
ASSERT(IsochDescriptor);
IoFreeIrp(pIrp);
return (STATUS_MORE_PROCESSING_REQUIRED);
}
if(pIrp->IoStatus.Status != STATUS_SUCCESS) {
ERROR_LOG(("\'DCamDetachBufferCR: pIrp->IoStatus.Status(%x) != STATUS_SUCCESS\n", pIrp->IoStatus.Status));
ASSERT(pIrp->IoStatus.Status == STATUS_SUCCESS);
IoFreeIrp(pIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
// IsochDescriptorReserved->Srb->Irp->IoStatus = pIrp->IoStatus.Status;
IoFreeIrp(pIrp);
// Freed and should not be referenced again!
IsochDescriptor->DeviceReserved[5] = 0;
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) &IsochDescriptor->DeviceReserved[0];
pDevExt = (PDCAM_EXTENSION) IsochDescriptor->Context1;
DbgMsg3(("\'DCamDetachBufferCR: IsochDescriptorReserved=%x; DevExt=%x\n", IsochDescriptorReserved, pDevExt));
ASSERT(IsochDescriptorReserved);
ASSERT(pDevExt);
if(pDevExt &&
IsochDescriptorReserved) {
//
// Indicate that the Srb should be complete
//
IsochDescriptorReserved->Flags |= STATE_SRB_IS_COMPLETE;
IsochDescriptorReserved->Srb->Status = STATUS_SUCCESS;
IsochDescriptorReserved->Srb->CommandData.DataBufferArray->DataUsed = IsochDescriptor->ulLength;
IsochDescriptorReserved->Srb->ActualBytesTransferred = IsochDescriptor->ulLength;
DbgMsg3(("\'DCamDetachBufferCR: Completing Srb %x\n", IsochDescriptorReserved->Srb));
KeAcquireSpinLock(&pDevExt->IsochDescriptorLock, &oldIrql);
RemoveEntryList(&IsochDescriptorReserved->DescriptorList); InterlockedDecrement(&pDevExt->PendingReadCount);
KeReleaseSpinLock(&pDevExt->IsochDescriptorLock, oldIrql);
ASSERT(IsochDescriptorReserved->Srb->StreamObject);
ASSERT(IsochDescriptorReserved->Srb->Flags & SRB_HW_FLAGS_STREAM_REQUEST);
StreamClassStreamNotification(
StreamRequestComplete,
IsochDescriptorReserved->Srb->StreamObject,
IsochDescriptorReserved->Srb);
// Free it here instead of in DCamCompletionRoutine.
ExFreePool(IsochDescriptor);
KeAcquireSpinLock(&pDevExt->IsochWaitingLock, &oldIrql);
if (!IsListEmpty(&pDevExt->IsochWaitingList) && pDevExt->PendingReadCount >= MAX_BUFFERS_SUPPLIED) {
//
// We had someone blocked waiting for us to complete. Pull
// them off the waiting list and get them running
//
DbgMsg3(("DCamDetachBufferCR: Dequeueing request - Read Count = %x\n", pDevExt->PendingReadCount));
IsochDescriptorReserved = \
(PISOCH_DESCRIPTOR_RESERVED) RemoveHeadList(
&pDevExt->IsochWaitingList
);
KeReleaseSpinLock(&pDevExt->IsochWaitingLock, oldIrql);
IsochDescriptor = \
(PISOCH_DESCRIPTOR) (((PUCHAR) IsochDescriptorReserved) -
FIELDOFFSET(ISOCH_DESCRIPTOR, DeviceReserved[0]));
DCamReadStreamWorker(IsochDescriptorReserved->Srb, IsochDescriptor);
} else {
KeReleaseSpinLock(&pDevExt->IsochWaitingLock, oldIrql);
}
}
return (STATUS_MORE_PROCESSING_REQUIRED);
}
VOID
DCamIsochCallback(
IN PDCAM_EXTENSION pDevExt,
IN PISOCH_DESCRIPTOR IsochDescriptor
)
/*++
Routine Description:
Called when an Isoch Descriptor completes
Arguments:
pDevExt - Pointer to our DeviceExtension
IsochDescriptor - IsochDescriptor that completed
Return Value:
Nothing
--*/
{
PIRB pIrb;
PIRP pIrp;
PSTREAMEX pStrmEx;
PIO_STACK_LOCATION NextIrpStack;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
PKSSTREAM_HEADER pDataPacket;
PKS_FRAME_INFO pFrameInfo;
KIRQL oldIrql;
//
// Debug check to make sure we're dealing with a real IsochDescriptor
//
ASSERT ( IsochDescriptor );
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) &IsochDescriptor->DeviceReserved[0];
//
// All Pending read will be either resubmitted, or cancelled (if out of resource).
//
if(pDevExt->bStopIsochCallback) {
ERROR_LOG(("DCamIsochCallback: bStopCallback is set. IsochDescriptor %x (and Reserved %x) is returned and not processed.\n",
IsochDescriptor, IsochDescriptorReserved));
return;
}
//
// Synchronization note:
//
// We are competing with cancel packet routine in the
// event of device removal or setting to STOP state.
// which ever got the spin lock to set DEATCH_BUFFER
// flag take ownership completing the Irp/IsochDescriptor.
//
KeAcquireSpinLock(&pDevExt->IsochDescriptorLock, &oldIrql);
if(pDevExt->bDevRemoved ||
(IsochDescriptorReserved->Flags & (STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS)) ) {
ERROR_LOG(("DCamIsochCallback: bDevRemoved || STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS %x %x\n",
IsochDescriptorReserved,IsochDescriptorReserved->Flags));
ASSERT((!pDevExt->bDevRemoved && !(IsochDescriptorReserved->Flags & (STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS))));
KeReleaseSpinLock(&pDevExt->IsochDescriptorLock, oldIrql);
return;
}
IsochDescriptorReserved->Flags |= STATE_DETACHING_BUFFERS;
KeReleaseSpinLock(&pDevExt->IsochDescriptorLock, oldIrql);
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
ASSERT(pStrmEx == (PSTREAMEX)IsochDescriptorReserved->Srb->StreamObject->HwStreamExtension);
pStrmEx->FrameCaptured++;
pStrmEx->FrameInfo.PictureNumber = pStrmEx->FrameCaptured + pStrmEx->FrameInfo.DropCount;
//
// Return the timestamp for the frame
//
pDataPacket = IsochDescriptorReserved->Srb->CommandData.DataBufferArray;
pFrameInfo = (PKS_FRAME_INFO) (pDataPacket + 1);
ASSERT ( pDataPacket );
ASSERT ( pFrameInfo );
//
// Return the timestamp for the frame
//
pDataPacket->PresentationTime.Numerator = 1;
pDataPacket->PresentationTime.Denominator = 1;
pDataPacket->Duration = pStrmEx->pVideoInfoHeader->AvgTimePerFrame;
//
// if we have a master clock
//
if (pStrmEx->hMasterClock) {
ULONGLONG tmStream;
tmGetStreamTime(IsochDescriptorReserved->Srb, pStrmEx, &tmStream);
pDataPacket->PresentationTime.Time = tmStream;
pDataPacket->OptionsFlags = 0;
pDataPacket->OptionsFlags |=
KSSTREAM_HEADER_OPTIONSF_TIMEVALID |
KSSTREAM_HEADER_OPTIONSF_DURATIONVALID |
KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT; // Every frame we generate is a key frame (aka SplicePoint)
DbgMsg3(("\'IsochCallback: Time(%dms); P#(%d)=Cap(%d)+Drp(%d); Pend%d\n",
(ULONG) tmStream/10000,
(ULONG) pStrmEx->FrameInfo.PictureNumber,
(ULONG) pStrmEx->FrameCaptured,
(ULONG) pStrmEx->FrameInfo.DropCount,
pDevExt->PendingReadCount));
} else {
pDataPacket->PresentationTime.Time = 0;
pDataPacket->OptionsFlags &=
~(KSSTREAM_HEADER_OPTIONSF_TIMEVALID |
KSSTREAM_HEADER_OPTIONSF_DURATIONVALID);
}
// Set additional info fields about the data captured such as:
// Frames Captured
// Frames Dropped
// Field Polarity
pStrmEx->FrameInfo.ExtendedHeaderSize = pFrameInfo->ExtendedHeaderSize;
*pFrameInfo = pStrmEx->FrameInfo;
#ifdef SUPPORT_RGB24
// Swaps B and R or BRG24 to RGB24.
// There are 640x480 pixels so 307200 swaps are needed.
if(pDevExt->CurrentModeIndex == VMODE4_RGB24 && pStrmEx->pVideoInfoHeader) {
PBYTE pbFrameBuffer;
BYTE bTemp;
ULONG i, ulLen;
#ifdef USE_WDM110 // Win2000
// Driver verifier flag to use this but if this is used, this driver will not load for any Win9x OS.
pbFrameBuffer = (PBYTE) MmGetSystemAddressForMdlSafe(IsochDescriptorReserved->Srb->Irp->MdlAddress, NormalPagePriority);
#else // Win9x
pbFrameBuffer = (PBYTE) MmGetSystemAddressForMdl (IsochDescriptorReserved->Srb->Irp->MdlAddress);
#endif
if(pbFrameBuffer) {
// calculate number of pixels
ulLen = abs(pStrmEx->pVideoInfoHeader->bmiHeader.biWidth) * abs(pStrmEx->pVideoInfoHeader->bmiHeader.biHeight);
ASSERT(ulLen == pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage/3);
if(ulLen > pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage)
ulLen = pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage/3;
for (i=0; i < ulLen; i++) {
// swap R and B
bTemp = pbFrameBuffer[0];
pbFrameBuffer[0] = pbFrameBuffer[2];
pbFrameBuffer[2] = bTemp;
pbFrameBuffer += 3; // next RGB24 pixel
}
}
}
#endif
// Reuse the Irp and Irb
pIrp = (PIRP) IsochDescriptor->DeviceReserved[5];
ASSERT(pIrp);
pIrb = (PIRB) IsochDescriptor->DeviceReserved[6];
ASSERT(pIrb);
#if DBG
// Same isochdescriptor should only be callback once.
ASSERT((IsochDescriptor->DeviceReserved[7] == 0x87654321));
IsochDescriptor->DeviceReserved[7]++;
#endif
pIrb->FunctionNumber = REQUEST_ISOCH_DETACH_BUFFERS;
pIrb->u.IsochDetachBuffers.hResource = pDevExt->hResource;
pIrb->u.IsochDetachBuffers.nNumberOfDescriptors = 1;
pIrb->u.IsochDetachBuffers.pIsochDescriptor = IsochDescriptor;
NextIrpStack = IoGetNextIrpStackLocation(pIrp);
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_1394_CLASS;
NextIrpStack->Parameters.Others.Argument1 = pIrb;
IoSetCompletionRoutine(
pIrp,
DCamDetachBufferCR, // Detach complete and will attach queued buffer.
pIrb,
TRUE,
TRUE,
TRUE
);
IoCallDriver(pDevExt->BusDeviceObject, pIrp);
}