windows-nt/Source/XPSP1/NT/drivers/wdm/capture/mini/1394dcam/datapkt.c
2020-09-26 16:20:57 +08:00

1313 lines
37 KiB
C

//===========================================================================
//
// 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:
DataPkt.c
Abstract:
Stream class based WDM driver for 1934 Desktop Camera.
This file contains code to handle the stream class packets.
Author:
Yee J. Wu 24-Jun-98
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"
extern CAMERA_ISOCH_INFO IsochInfoTable[];
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, DCamSurpriseRemoval)
#pragma alloc_text(PAGE, DCamReceiveDataPacket)
#endif
NTSTATUS
DCamCancelOnePacketCR(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
PISOCH_DESCRIPTOR IsochDescriptor
)
/*++
Routine Description:
Completion routine for detach an isoch descriptor associate with a pending read SRB.
Will cancel the pending SRB here if detaching descriptor has suceeded.
Arguments:
DriverObject - Pointer to driver object created by system.
pIrp - Allocated locally, need to be free here.
IsochDescriptor - Isoch descriptor containing the SRB to be cancelled.
Return Value:
None.
--*/
{
PHW_STREAM_REQUEST_BLOCK pSrbToCancel;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
PDCAM_EXTENSION pDevExt;
if(STATUS_SUCCESS != pIrp->IoStatus.Status) {
ERROR_LOG(("DCamCancelOnePacketCR: Detach buffer failed with pIrp->IoStatus.Status= %x (! STATUS_SUCCESS) \n", pIrp->IoStatus.Status));
ASSERT(STATUS_SUCCESS == pIrp->IoStatus.Status);
} else {
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) &IsochDescriptor->DeviceReserved[0];
pSrbToCancel = IsochDescriptorReserved->Srb;
pDevExt = (PDCAM_EXTENSION) pSrbToCancel->HwDeviceExtension;
IsochDescriptorReserved->Flags |= STATE_SRB_IS_COMPLETE;
pSrbToCancel->CommandData.DataBufferArray->DataUsed = 0;
pSrbToCancel->ActualBytesTransferred = 0;
pSrbToCancel->Status = pDevExt->bDevRemoved ? STATUS_DEVICE_REMOVED : STATUS_CANCELLED;
DbgMsg2(("DCamCancelOnePacketCR: SRB %x, Status %x, IsochDesc %x, Reserved %x cancelled\n",
pSrbToCancel, pSrbToCancel->Status, IsochDescriptor, IsochDescriptorReserved));
StreamClassStreamNotification(
StreamRequestComplete,
pSrbToCancel->StreamObject,
pSrbToCancel);
ExFreePool(IsochDescriptor);
}
// Allocated locally so free it.
IoFreeIrp(pIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
DCamDetachAndCancelOnePacket(
IN PHW_STREAM_REQUEST_BLOCK pSrbToCancel,
PISOCH_DESCRIPTOR IsochDescriptorToDetach,
HANDLE hResource,
PDEVICE_OBJECT pBusDeviceObject
)
/*++
Routine Description:
Detach an isoch descriptor and then cancel pending SRB in the completion routine.
Arguments:
pSrbToCancel - Pointer to SRB to cancel
IsochDescriptorToDetach - Iosch descriptor to detach
hResource - isoch resource allocated
hBusDeviceObject - bus device object
Return Value:
None.
--*/
{
PDCAM_EXTENSION pDevExt;
PIO_STACK_LOCATION NextIrpStack;
NTSTATUS Status;
PIRB pIrb;
PIRP pIrp;
DbgMsg2(("\'DCamDetachAndCancelOnePacket: pSrbTocancel %x, detaching IsochDescriptorToDetach %x\n", pSrbToCancel, IsochDescriptorToDetach));
pDevExt = (PDCAM_EXTENSION) pSrbToCancel->HwDeviceExtension;
pIrp = IoAllocateIrp(pDevExt->BusDeviceObject->StackSize, FALSE);
ASSERT(pIrp);
if(!pIrp)
return;
pIrb = pSrbToCancel->SRBExtension;
pIrb->Flags = 0;
pIrb->FunctionNumber = REQUEST_ISOCH_DETACH_BUFFERS;
pIrb->u.IsochDetachBuffers.hResource = hResource;
pIrb->u.IsochDetachBuffers.nNumberOfDescriptors = 1;
pIrb->u.IsochDetachBuffers.pIsochDescriptor = IsochDescriptorToDetach;
NextIrpStack = IoGetNextIrpStackLocation(pIrp);
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_1394_CLASS;
NextIrpStack->Parameters.Others.Argument1 = pIrb;
IoSetCompletionRoutine(
pIrp,
DCamCancelOnePacketCR,
IsochDescriptorToDetach,
TRUE,
TRUE,
TRUE
);
Status =
IoCallDriver(
pBusDeviceObject,
pIrp
);
ASSERT(Status == STATUS_SUCCESS || Status == STATUS_PENDING);
}
VOID
DCamCancelOnePacket(
IN PHW_STREAM_REQUEST_BLOCK pSrbToCancel
)
/*++
Routine Description:
This routine is called to cancel a pending streaming SRB. This is likely to
happen when transitioning from PAUSE to STOP state.
Note: This routine is called at DISPATCH_LEVEL !!
Arguments:
pSrbToCancel - Pointer to SRB to cancel
Return Value:
None.
--*/
{
PHW_STREAM_REQUEST_BLOCK pSrbInQ;
PISOCH_DESCRIPTOR IsochDescriptorToDetach;
PDCAM_EXTENSION pDevExt;
PSTREAMEX pStrmEx;
PLIST_ENTRY pEntry; // Pointer to an isoch decriptor reserved structure
KIRQL oldIrql;
BOOL Found;
pDevExt = (PDCAM_EXTENSION) pSrbToCancel->HwDeviceExtension;
ASSERT(pDevExt);
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
ASSERT(pStrmEx);
// Nothing to cancel
if(pStrmEx == NULL) {
return;
}
//
// We only expect stream SRB, but not device SRB.
//
if ( (pSrbToCancel->Flags & SRB_HW_FLAGS_STREAM_REQUEST) != SRB_HW_FLAGS_STREAM_REQUEST) {
ERROR_LOG(("DCamCancelOnePacket: Cannot cancel Device SRB %x\n", pSrbToCancel));
ASSERT( (pSrbToCancel->Flags & SRB_HW_FLAGS_STREAM_REQUEST) == SRB_HW_FLAGS_STREAM_REQUEST );
return;
}
//
// Loop through the linked list from the beginning to end,
// trying to find the SRB to cancel
//
KeAcquireSpinLock(&pDevExt->IsochDescriptorLock, &oldIrql);
Found = FALSE;
pEntry = pDevExt->IsochDescriptorList.Flink;
while (pEntry != &pDevExt->IsochDescriptorList) {
pSrbInQ = ((PISOCH_DESCRIPTOR_RESERVED)pEntry)->Srb;
IsochDescriptorToDetach = \
(PISOCH_DESCRIPTOR) ( ((PUCHAR) pEntry) -
FIELDOFFSET(ISOCH_DESCRIPTOR, DeviceReserved[0]));
if(pSrbToCancel == pSrbInQ) {
// If we are in RUN state, we could be competing with IsochCallback;
// Whichever grabs and change STATE_DETACHING_BUFFERS will detach.
if(((PISOCH_DESCRIPTOR_RESERVED) pEntry)->Flags & (STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS)) {
Found = FALSE; // IsochCallback are detaching it (we lost our chance).
ERROR_LOG(("DCamCancelOnePacket: pSrbToCancel %x, Descriptor %x, Reserved %x already detaching or completed\n",
pSrbToCancel, IsochDescriptorToDetach, pEntry));
} else {
((PISOCH_DESCRIPTOR_RESERVED) pEntry)->Flags |= STATE_DETACHING_BUFFERS;
#if DBG
// Should not have been detached.
ASSERT((IsochDescriptorToDetach->DeviceReserved[7] == 0x87654321));
IsochDescriptorToDetach->DeviceReserved[7]++;
#endif
RemoveEntryList(pEntry);
InterlockedDecrement(&pDevExt->PendingReadCount);
Found = TRUE;
}
break;
}
pEntry = pEntry->Flink; // Next
}
KeReleaseSpinLock (&pDevExt->IsochDescriptorLock, oldIrql);
//
// Since we are in DISPATCH level, we cannot do sync operation;
// so we will complete this asynchronously in the completion routine.
//
if (Found) {
DCamDetachAndCancelOnePacket(
pSrbToCancel,
IsochDescriptorToDetach,
pDevExt->hResource,
pDevExt->BusDeviceObject);
} else {
ERROR_LOG(("DCamCancelOnePacket: pSrbToCancel %x is not in our list!\n", pSrbToCancel));
ASSERT(Found);
}
}
VOID
DCamCancelAllPackets(
PHW_STREAM_REQUEST_BLOCK pSrb,
PDCAM_EXTENSION pDevExt,
LONG *plPendingReadCount
)
/*++
Routine Description:
This routine is use to cancel all pending IRP.
Can be called at DISPATCH_LEVEL.
Arguments:
pSrbToCancel - Pointer to SRB to cancel
pDevExt - Device's contect
plPendingReadCount - Number of pending read
Return Value:
None.
--*/
{
PHW_STREAM_REQUEST_BLOCK pSrbToCancel;
PISOCH_DESCRIPTOR IsochDescriptorToDetach;
PLIST_ENTRY pEntry;
KIRQL oldIrql;
PSTREAMEX pStrmEx;
pStrmEx = pDevExt->pStrmEx;
// Nothing to cancel
if(pStrmEx == NULL) {
return;
}
//
// Loop through the linked list from the beginning to end,
// trying to find the SRB to cancel
//
KeAcquireSpinLock(&pDevExt->IsochDescriptorLock, &oldIrql);
pEntry = pDevExt->IsochDescriptorList.Flink;
while (pEntry != &pDevExt->IsochDescriptorList) {
pSrbToCancel = ((PISOCH_DESCRIPTOR_RESERVED)pEntry)->Srb;
IsochDescriptorToDetach = \
(PISOCH_DESCRIPTOR) ( ((PUCHAR) pEntry) -
FIELDOFFSET(ISOCH_DESCRIPTOR, DeviceReserved[0]));
if(((PISOCH_DESCRIPTOR_RESERVED) pEntry)->Flags & (STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS)) {
// Skip this one since it is already in detaching phase or completed.
ERROR_LOG(("DCamCancelAllPacket: pSrbToCancel %x, Descriptor %x, Reserved %x already detaching or completed\n",
pSrbToCancel, IsochDescriptorToDetach, pEntry));
pEntry = pEntry->Flink; // next
} else {
((PISOCH_DESCRIPTOR_RESERVED) pEntry)->Flags |= STATE_DETACHING_BUFFERS;
#if DBG
// Should not have been detached.
ASSERT((IsochDescriptorToDetach->DeviceReserved[7] == 0x87654321));
IsochDescriptorToDetach->DeviceReserved[7]++;
#endif
RemoveEntryList(pEntry);
InterlockedDecrement(plPendingReadCount);
DbgMsg2(("DCamCancelAllPackets: pSrbToCancel %x, Descriptor %x, Reserved %x\n",
pSrbToCancel, IsochDescriptorToDetach, pEntry));
pEntry = pEntry->Flink; // pEntry is deleted in DCamDetachAndCancelOnePacket(); so get next here.
DCamDetachAndCancelOnePacket(
pSrbToCancel,
IsochDescriptorToDetach,
pDevExt->hResource,
pDevExt->BusDeviceObject);
}
}
KeReleaseSpinLock (&pDevExt->IsochDescriptorLock, oldIrql);
pSrb->Status = STATUS_SUCCESS;
DbgMsg1(("DCamCancelAllPackets: Complete pSrb %x, Status %x\n", pSrb, pSrb->Status));
COMPLETE_SRB(pSrb)
}
VOID
DCamSurpriseRemoval(
IN PHW_STREAM_REQUEST_BLOCK pSrb
)
/*++
Routine Description:
Response to SRB_SURPRISE_REMOVAL.
Arguments:
pSrb - Pointer to the stream request block
Return Value:
None.
--*/
{
PIRP pIrp;
PIRB pIrb;
PDCAM_EXTENSION pDevExt;
PSTREAMEX pStrmEx;
NTSTATUS Status, StatusWait;
PAGED_CODE();
pIrb = (PIRB) pSrb->SRBExtension;
ASSERT(pIrb);
pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
ASSERT(pDevExt);
//
// Set this to stop accepting incoming read.
//
pDevExt->bDevRemoved = TRUE;
//
// Wait for currect read to be attached so we cancel them all.
//
pStrmEx = pDevExt->pStrmEx;
if(pStrmEx) {
// Make sure that this structure is still valid.
if(pStrmEx->pVideoInfoHeader) {
StatusWait = KeWaitForSingleObject( &pStrmEx->hMutex, Executive, KernelMode, FALSE, 0 );
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
}
}
pIrp = IoAllocateIrp(pDevExt->BusDeviceObject->StackSize, FALSE);
if(!pIrp) {
ERROR_LOG(("DCamSurpriseRemovalPacket: faile to get resource; pIrb=%x, pDevExt=%x, pIrp\n", pIrb, pDevExt, pIrp));
pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
return;
}
//
// un-register a bus reset callback notification
//
pIrb->FunctionNumber = REQUEST_BUS_RESET_NOTIFICATION;
pIrb->Flags = 0;
pIrb->u.BusResetNotification.fulFlags = DEREGISTER_NOTIFICATION_ROUTINE;
pIrb->u.BusResetNotification.ResetRoutine = (PBUS_BUS_RESET_NOTIFICATION) DCamBusResetNotification;
pIrb->u.BusResetNotification.ResetContext = 0;
Status = DCamSubmitIrpSynch(pDevExt, pIrp, pIrb);
if(Status) {
ERROR_LOG(("DCamSurpriseRemoval: Status %x while trying to deregister bus reset notification.\n", Status));
}
//
// Get new generation number
//
pIrb->FunctionNumber = REQUEST_GET_GENERATION_COUNT;
pIrb->Flags = 0;
Status = DCamSubmitIrpSynch(pDevExt, pIrp, pIrb);
if(Status) {
ERROR_LOG(("DCamSurpriseRemoval: Status %x while trying to get generation number.\n", Status));
} else {
DbgMsg1(("DCamSurpriseRemoval: pDevExt %x, Generation number from %d to %d\n",
pDevExt, pDevExt->CurrentGeneration, pIrb->u.GetGenerationCount.GenerationCount));
InterlockedExchange(&pDevExt->CurrentGeneration, pIrb->u.GetGenerationCount.GenerationCount);
}
if(pStrmEx) {
//
// Stop isoch transmission so we can detach buffers and cancel pending SRBs
//
pIrb->FunctionNumber = REQUEST_ISOCH_STOP;
pIrb->Flags = 0;
pIrb->u.IsochStop.hResource = pDevExt->hResource;
pIrb->u.IsochStop.fulFlags = 0;
Status = DCamSubmitIrpSynch(pDevExt, pIrp, pIrb);
if(Status) {
ERROR_LOG(("DCamSurpriseRemoval: Status %x while trying to ISOCH_STOP.\n", Status));
}
IoFreeIrp(pIrp);
DCamCancelAllPackets(
pSrb,
pDevExt,
&pDevExt->PendingReadCount
);
} else {
IoFreeIrp(pIrp);
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
}
}
NTSTATUS
DCamAttachBufferCR(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PISOCH_DESCRIPTOR IsochDescriptor
)
/*++
Routine Description:
This routine is the completion routine from attaching a bufffer to lower driver.
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.
--*/
{
PHW_STREAM_REQUEST_BLOCK pSrb;
PDCAM_EXTENSION pDevExt;
PSTREAMEX pStrmEx;
NTSTATUS Status;
PIRB pIrb;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
KIRQL oldIrql;
pDevExt = (PDCAM_EXTENSION) IsochDescriptor->Context1;
ASSERT(pDevExt);
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
ASSERT(pStrmEx);
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) &IsochDescriptor->DeviceReserved[0];
ASSERT(IsochDescriptorReserved);
pSrb = IsochDescriptorReserved->Srb;
ASSERT(pSrb);
pIrb = (PIRB) pSrb->SRBExtension;
DbgMsg3(("\'DCamAttachBufferCR: completed KSSTATE=%d; pIrp->IoStatus.Status=%x; pSrb=%x\n",
pStrmEx->KSState, pIrp->IoStatus.Status, pSrb));
//
// Attaching a buffer return with error.
//
if(pIrp->IoStatus.Status) {
ERROR_LOG(("DCamAttachBufferCR: pIrp->IoStatus.Status=%x (STATUS_PENDING=%x); complete SRB with this status.\n",
pIrp->IoStatus.Status, STATUS_PENDING));
ASSERT(pIrp->IoStatus.Status == STATUS_SUCCESS);
if(!(IsochDescriptorReserved->Flags & STATE_SRB_IS_COMPLETE)) {
ASSERT(((IsochDescriptorReserved->Flags & STATE_SRB_IS_COMPLETE) != STATE_SRB_IS_COMPLETE));
IsochDescriptorReserved->Flags |= STATE_SRB_IS_COMPLETE;
pSrb->Status = pIrp->IoStatus.Status; // Read is completed with error status.
KeAcquireSpinLock(&pDevExt->IsochDescriptorLock, &oldIrql);
RemoveEntryList(&IsochDescriptorReserved->DescriptorList); InterlockedDecrement(&pDevExt->PendingReadCount);
KeReleaseSpinLock(&pDevExt->IsochDescriptorLock, oldIrql);
ExFreePool(IsochDescriptor);
StreamClassStreamNotification(StreamRequestComplete, pSrb->StreamObject, pSrb);
KeAcquireSpinLock(&pDevExt->IsochWaitingLock, &oldIrql);
//
// If we failed to attach (rtn with failed status),
// removed this entry, and
// pull one out of the waiting list if not yet exceeded out limit.
//
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(("\'DCamAttachBufferCR: Dequeueing request - Read Count=%d\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);
}
} else {
// Race condition ? or a valid error code?
ERROR_LOG(("DCamAttachBufferCR: IsochDescriptorReserved->Flags contain STATE_SRB_IS_COMPLETE\n"));
ASSERT(FALSE);
}
}
//
// Ealier when we set to RUN state, it might have failed with
// STATUS_INSUFFICIENT_RESOURCE due to no buffer attached;
// we have at least one now, ask controll to start listen and
// fill and return our buffer.
//
if(pDevExt->bNeedToListen) {
PIRB pIrb2;
PIRP pIrp2;
PDCAM_IO_CONTEXT pDCamIoContext;
PIO_STACK_LOCATION NextIrpStack;
if(!DCamAllocateIrbIrpAndContext(&pDCamIoContext, &pIrb2, &pIrp2, pDevExt->BusDeviceObject->StackSize)) {
ERROR_LOG(("DCamAttachBufferCR: Want to stat Listening but no resource !!\n"));
return STATUS_MORE_PROCESSING_REQUIRED;
}
pDevExt->bNeedToListen = FALSE;
DbgMsg2(("\'DCamAttachBufferCR: ##### pDevExt->bNeedToListen\n"));
pDCamIoContext->pDevExt = pDevExt;
pDCamIoContext->pIrb = pIrb2;
pIrb2->FunctionNumber = REQUEST_ISOCH_LISTEN;
pIrb2->Flags = 0;
pIrb2->u.IsochListen.hResource = pDevExt->hResource;
pIrb2->u.IsochListen.fulFlags = 0;
NextIrpStack = IoGetNextIrpStackLocation(pIrp2);
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_1394_CLASS;
NextIrpStack->Parameters.Others.Argument1 = pIrb2;
pDevExt->lRetries = RETRY_COUNT;
IoSetCompletionRoutine(
pIrp2,
DCamStartListenCR,
pDCamIoContext,
TRUE,
TRUE,
TRUE
);
Status =
IoCallDriver(
pDevExt->BusDeviceObject,
pIrp2);
}
// No resource to freed.
// Resource (pIrb is from original SRB)
return STATUS_MORE_PROCESSING_REQUIRED;
//
// The attached SRB read will be completed in IoschCallback().
//
}
NTSTATUS
DCamReSubmitPacketCR(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PISOCH_DESCRIPTOR IsochDescriptor
)
/*++
Routine Description:
This routine is called after a packet is detach and
will be attached here to complete the resubmission of
packet after a isoch. resource change.
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.
--*/
{
PIRB pIrb;
PIO_STACK_LOCATION NextIrpStack;
PDCAM_EXTENSION pDevExt;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
NTSTATUS Status;
pDevExt = IsochDescriptor->Context1;
ASSERT(pDevExt);
pIrb = (PIRB) IsochDescriptor->DeviceReserved[6];
ASSERT(pIrb);
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) &IsochDescriptor->DeviceReserved[0];
//
// Detached, so unmark it.
//
IsochDescriptorReserved->Flags &= ~STATE_DETACHING_BUFFERS;
DbgMsg2(("\'DCamReSubmitPacketCR: ReSubmit pDevExt %x, pIrb %x, hResource %x, IsochDescriptor %x, IsochDescriptorReserved %x\n",
pDevExt, pIrb, pDevExt->hResource, IsochDescriptor, IsochDescriptorReserved));
#if DBG
//
// Put signatures and use these count to track if the IsochDescriptor
// has been attached or detached unexpectely.
//
// When attach, [4]++ (DCamReadStreamWorker(), DCamReSumbitPacketCR())
// detach, [7]++ (DCamIsochcallback(), DCamCancelPacketCR(), DCamResubmitPacket())
//
IsochDescriptor->DeviceReserved[4] = 0x12345678;
IsochDescriptor->DeviceReserved[7] = 0x87654321;
#endif
//
// Attach descriptor onto our pending descriptor list
//
ExInterlockedInsertTailList(
&pDevExt->IsochDescriptorList,
&IsochDescriptorReserved->DescriptorList,
&pDevExt->IsochDescriptorLock
);
pIrb->FunctionNumber = REQUEST_ISOCH_ATTACH_BUFFERS;
pIrb->Flags = 0;
pIrb->u.IsochAttachBuffers.hResource = pDevExt->hResource;
pIrb->u.IsochAttachBuffers.nNumberOfDescriptors = 1;
pIrb->u.IsochAttachBuffers.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,
DCamAttachBufferCR,
IsochDescriptor,
TRUE,
TRUE,
TRUE
);
Status =
IoCallDriver(
pDevExt->BusDeviceObject,
pIrp
);
ASSERT(Status == STATUS_SUCCESS || Status == STATUS_PENDING);
return STATUS_MORE_PROCESSING_REQUIRED; // Complete Asynchronously in DCamAttachBufferCR
}
NTSTATUS
DCamReSubmitPacket(
HANDLE hStaleResource,
PDCAM_EXTENSION pDevExt,
PSTREAMEX pStrmEx,
LONG cntPendingRead
)
/*++
Routine Description:
Due to a bus reset, if a channel number has changed (subsequently, iso resource
change too), we must detach and re-attach pending packet(s).
While this function is executed, incoming SRB_READ is blocked and isoch callback
are returned and not processed (we are resubmiting them).
Arguments:
hStaleResource - staled isoch resource
pDevExt - Device's Extension
pStrmEx - Stremaing extension
cntPendingRead - Number of pending packets
Return Value:
NTSTATUS.
--*/
{
PIRB pIrb;
PIRP pIrp;
PIO_STACK_LOCATION NextIrpStack;
PISOCH_DESCRIPTOR IsochDescriptor;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
NTSTATUS Status = STATUS_SUCCESS;
KIRQL oldIrql;
DbgMsg1(("DCamReSubmitPacket: pDevExt %x, pStrmEx %x, PendingCount %d\n", pDevExt, pStrmEx, cntPendingRead));
for(; cntPendingRead > 0; cntPendingRead--) {
if(!IsListEmpty(&pDevExt->IsochDescriptorList)) {
//
// 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);
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) RemoveHeadList(&pDevExt->IsochDescriptorList);
if((IsochDescriptorReserved->Flags & (STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS))) {
ERROR_LOG(("DCamReSubmitPacket: Flags %x aleady mark STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS\n", IsochDescriptorReserved->Flags));
ASSERT(( !(IsochDescriptorReserved->Flags & (STATE_SRB_IS_COMPLETE | STATE_DETACHING_BUFFERS))));\
//Put it back since it has been detached.
InsertTailList(&pDevExt->IsochDescriptorList, &IsochDescriptorReserved->DescriptorList);
KeReleaseSpinLock(&pDevExt->IsochDescriptorLock, oldIrql);
continue;
}
IsochDescriptorReserved->Flags |= STATE_DETACHING_BUFFERS;
KeReleaseSpinLock(&pDevExt->IsochDescriptorLock, oldIrql);
IsochDescriptor = (PISOCH_DESCRIPTOR) (((PUCHAR) IsochDescriptorReserved) - FIELDOFFSET(ISOCH_DESCRIPTOR, DeviceReserved[0]));
pIrp = (PIRP) IsochDescriptor->DeviceReserved[5];
ASSERT(pIrp);
pIrb = (PIRB) IsochDescriptor->DeviceReserved[6];
ASSERT(pIrb);
DbgMsg1(("DCamReSubmitPacket: detaching IsochDescriptor %x IsochDescriptorReserved %x, pSrb %x\n",
IsochDescriptor, IsochDescriptorReserved, IsochDescriptorReserved->Srb));
#if DBG
// Should not have been detached
ASSERT((IsochDescriptor->DeviceReserved[7] == 0x87654321));
IsochDescriptor->DeviceReserved[7]++;
#endif
pIrb->FunctionNumber = REQUEST_ISOCH_DETACH_BUFFERS;
pIrb->Flags = 0;
pIrb->u.IsochDetachBuffers.hResource = hStaleResource;
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,
DCamReSubmitPacketCR,
IsochDescriptor,
TRUE,
TRUE,
TRUE
);
Status =
IoCallDriver(
pDevExt->BusDeviceObject,
pIrp
);
ASSERT(Status == STATUS_SUCCESS || Status == STATUS_PENDING);
} else {
ERROR_LOG(("PendingCount %d, but list is empty!!\n", cntPendingRead));
ASSERT(cntPendingRead == 0);
}
} // for()
return Status;
}
VOID
DCamReadStreamWorker(
IN PHW_STREAM_REQUEST_BLOCK pSrb,
IN PISOCH_DESCRIPTOR IsochDescriptor
)
/*++
Routine Description:
Does most of the work for handling reads via Attach buffers
Arguments:
Srb - Pointer to Stream request block
IsochDescriptor - Pointer to IsochDescriptor to be used
Return Value:
Nothing
--*/
{
PIRB pIrb;
PIRP pIrp;
PIO_STACK_LOCATION NextIrpStack;
PDCAM_EXTENSION pDevExt;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
NTSTATUS Status;
pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
pIrp = (PIRP) IsochDescriptor->DeviceReserved[5];
ASSERT(pIrp);
pIrb = (PIRB) IsochDescriptor->DeviceReserved[6];
ASSERT(pIrb);
#if DBG
// track number time the same IsochDescriptor are attaching; should only be one.
IsochDescriptor->DeviceReserved[4]++;
#endif
//
// It is pending and will be completed in isoch callback or cancelled.
//
pSrb->Status = STATUS_PENDING;
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) &IsochDescriptor->DeviceReserved[0];
DbgMsg3(("\'DCamReadStreamWorker: enter with pSrb = %x, pDevExt=0x%x\n", pSrb, pDevExt));
//
// Attach descriptor onto our pending descriptor list
//
ExInterlockedInsertTailList(
&pDevExt->IsochDescriptorList,
&IsochDescriptorReserved->DescriptorList,
&pDevExt->IsochDescriptorLock
);
pIrb->FunctionNumber = REQUEST_ISOCH_ATTACH_BUFFERS;
pIrb->Flags = 0;
pIrb->u.IsochAttachBuffers.hResource = pDevExt->hResource;
pIrb->u.IsochAttachBuffers.nNumberOfDescriptors = 1;
pIrb->u.IsochAttachBuffers.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,
DCamAttachBufferCR,
IsochDescriptor,
TRUE,
TRUE,
TRUE
);
Status =
IoCallDriver(
pDevExt->BusDeviceObject,
pIrp
);
ASSERT(Status == STATUS_SUCCESS || Status == STATUS_PENDING);
return; // Complete Asynchronously in IoCompletionRoutine*
}
VOID
DCamReadStream(
IN PHW_STREAM_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Called when an Read Data Srb request is received
Arguments:
Srb - Pointer to Stream request block
Return Value:
Nothing
--*/
{
PIRB pIrb;
PIRP pIrp;
KIRQL oldIrql;
PDCAM_EXTENSION pDevExt;
PSTREAMEX pStrmEx;
PISOCH_DESCRIPTOR IsochDescriptor;
PISOCH_DESCRIPTOR_RESERVED IsochDescriptorReserved;
NTSTATUS StatusWait;
pIrb = (PIRB) Srb->SRBExtension;
pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
ASSERT(pDevExt != NULL);
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
ASSERT(pStrmEx);
if(pDevExt->bDevRemoved ||
pStrmEx == NULL) {
Srb->Status = pDevExt->bDevRemoved ? STATUS_DEVICE_REMOVED : STATUS_UNSUCCESSFUL;
Srb->ActualBytesTransferred = 0;
Srb->CommandData.DataBufferArray->DataUsed = 0;
ERROR_LOG(("DCamReadStream: Failed with Status %x or pStrmEx %x\n", Srb->Status, pStrmEx));
StreamClassStreamNotification(StreamRequestComplete, Srb->StreamObject, Srb);
return;
}
//
// Mutext for either StreamIo (SRB_READ) ControlIo (SRB_SET_STREAM_STATE)
//
// Non-alertable; wait infinite
StatusWait = KeWaitForSingleObject( &pStrmEx->hMutex, Executive, KernelMode, FALSE, 0 );
ASSERT(StatusWait == STATUS_SUCCESS);
DbgMsg3(("\'%d:%s) DCamReadStream: enter with Srb %x, DevExt %x\n",
pDevExt->idxDev, pDevExt->pchVendorName, Srb, pDevExt));
// Rule:
// Only accept read requests when in either the Pause or Run
// States. If Stopped, immediately return the SRB.
if (pStrmEx->KSState == KSSTATE_STOP ||
pStrmEx->KSState == KSSTATE_ACQUIRE) {
DbgMsg2(("\'%d:%s)DCamReadStream: Current KSState(%d) < (%d)=KSSTATE_PAUSE; Srb=0x%x; DevExt=0x%x",
pDevExt->idxDev, pDevExt->pchVendorName, pStrmEx->KSState, KSSTATE_PAUSE, Srb, pDevExt));
DbgMsg2(("\'DCamReadStream: PendingRead=%d, IsochDescriptorList(%s)\n",
pDevExt->PendingReadCount, IsListEmpty(&pDevExt->IsochDescriptorList)?"Empty":"!Empty"));
Srb->Status = STATUS_UNSUCCESSFUL;
Srb->CommandData.DataBufferArray->DataUsed = 0;
StreamClassStreamNotification(StreamRequestComplete, Srb->StreamObject, Srb);
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
return;
}
// Buffer need to be big enough
if (IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize > Srb->CommandData.DataBufferArray->FrameExtent) {
ASSERT(IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize <= Srb->CommandData.DataBufferArray->FrameExtent);
Srb->Status = STATUS_INVALID_PARAMETER;
StreamClassStreamNotification(StreamRequestComplete, Srb->StreamObject, Srb);
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
return;
}
//
// Use our own IRP
//
pIrp = IoAllocateIrp(pDevExt->BusDeviceObject->StackSize, FALSE);
if(!pIrp) {
ASSERT(pIrp);
Srb->Status = STATUS_INSUFFICIENT_RESOURCES;
return;
}
//
// This structure (IsochDescriptor) has (ULONG) DeviceReserved[8];
// Its first 4 ULONGs are used by IsochDescriptorReserved,
// The 6th (index[5]), is used to keep pIrp
// 7th (index[6]), is used to keep pIrb
//
IsochDescriptor = ExAllocatePoolWithTag(NonPagedPool, sizeof(ISOCH_DESCRIPTOR), 'macd');
if (!IsochDescriptor) {
ASSERT(FALSE);
Srb->Status = STATUS_INSUFFICIENT_RESOURCES;
StreamClassStreamNotification(StreamRequestComplete, Srb->StreamObject, Srb);
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
return;
}
DbgMsg3(("\'DCamReadStream: IsochDescriptor = %x\n", IsochDescriptor));
IsochDescriptor->fulFlags = SYNCH_ON_SY;
DbgMsg3(("\'DCamReadStream: Incoming Mdl = %x\n", Srb->Irp->MdlAddress));
IsochDescriptor->Mdl = Srb->Irp->MdlAddress;
// Use size match what we originally requested in AllocateIsoch
IsochDescriptor->ulLength = IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize;
IsochDescriptor->nMaxBytesPerFrame = IsochInfoTable[pStrmEx->idxIsochTable].QuadletPayloadPerPacket << 2;
IsochDescriptor->ulSynch = START_OF_PICTURE;
IsochDescriptor->ulTag = 0;
IsochDescriptor->Callback = DCamIsochCallback;
IsochDescriptor->Context1 = pDevExt;
IsochDescriptor->Context2 = IsochDescriptor;
//
// IsochDescriptorReserved is pointed to the DeviceReserved[0];
// The entire, except the links, are kept in the DeviceReserved[]
//
IsochDescriptorReserved = (PISOCH_DESCRIPTOR_RESERVED) &IsochDescriptor->DeviceReserved[0];
IsochDescriptorReserved->Srb = Srb;
IsochDescriptorReserved->Flags = 0;
IsochDescriptor->DeviceReserved[5] = (ULONG_PTR) pIrp;
IsochDescriptor->DeviceReserved[6] = (ULONG_PTR) pIrb;
#if DBG
//
// Put signatures and use these count to track if the IsochDescriptor
// has been attached or detached unexpectely.
//
// When attach, [4]++ (DCamReadStreamWorker(), DCamReSumbitPacketCR())
// detach, [7]++ (DCamIsochcallback(), DCamCancelPacketCR(), DCamResubmitPacket())
//
IsochDescriptor->DeviceReserved[4] = 0x12345678;
IsochDescriptor->DeviceReserved[7] = 0x87654321;
#endif
//
// Checking here to see if we have enuff resources to put this read
// down right away. Since we only allocated N amount of resources
// from the 1394 stack beneath us, we'll have to stay within that
// limit and do some of the throttling ourself.
//
KeAcquireSpinLock(&pDevExt->IsochWaitingLock, &oldIrql);
if (InterlockedIncrement(&pDevExt->PendingReadCount) > MAX_BUFFERS_SUPPLIED) {
//
// don't have enuff resources to do an attach buffers right now.
// we'll queue this request and pull it off later when another
// read completes.
//
DbgMsg2(("\'DCamReadStream: Queueing request - Read Count = %x\n", pDevExt->PendingReadCount));
InsertTailList(
&pDevExt->IsochWaitingList,
&IsochDescriptorReserved->DescriptorList
);
KeReleaseSpinLock(&pDevExt->IsochWaitingLock, oldIrql);
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
return;
}
if(pStrmEx->KSState == KSSTATE_PAUSE) {
DbgMsg2(("\'DCamReadStream: Doing Pre-read in _PAUSE state; Srb %x, pDevExt %x, PendingCount %d\n",
Srb, pDevExt, pDevExt->PendingReadCount));
}
//
// Do actual read work here via our Read worker function
//
KeReleaseSpinLock(&pDevExt->IsochWaitingLock, oldIrql);
DCamReadStreamWorker(Srb, IsochDescriptor);
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
}
VOID
DCamReceiveDataPacket(
IN PHW_STREAM_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Called with video data packet commands
Arguments:
Srb - Pointer to Stream request block
Return Value:
Nothing
--*/
{
PAGED_CODE();
//
// determine the type of packet.
//
switch (Srb->Command) {
case SRB_READ_DATA:
DbgMsg3(("\'DCamReceiveDataPacket: SRB_READ_DATA\n"));
DCamReadStream(Srb);
// This request will be completed asynchronously...
break;
case SRB_WRITE_DATA:
DbgMsg3(("\'DCamReceiveDataPacket: SRB_WRITE_DATA, not used for digital camera.\n"));
ASSERT(FALSE);
default:
//
// invalid / unsupported command. Fail it as such
//
Srb->Status = STATUS_NOT_IMPLEMENTED;
StreamClassStreamNotification(StreamRequestComplete, Srb->StreamObject, Srb);
}
}